mirror of
https://github.com/chenasraf/nextcloud-forum.git
synced 2026-05-18 01:28:58 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f6910cde2d | |||
| ed04879575 | |||
| 362fdc8b03 | |||
| 0d98473cbf | |||
|
|
3242a1cad5 | ||
| 9fac12b0c7 | |||
|
|
37a82842b1 | ||
|
|
46b2c820e8 | ||
|
|
715b2ab6ff | ||
|
|
3ab3c1cc76 | ||
| 3d1ddb9f26 | |||
| a286bbdfe9 | |||
| a8e158d35b | |||
| c3d267f122 | |||
| c2e4ebe242 | |||
|
|
679abe3fb6 | ||
|
|
043af15809 | ||
| 407df1d423 | |||
| e2dcebc6ee | |||
| a905ce3b4c | |||
| c017bb3d09 | |||
|
|
67c92c05a3 | ||
| e94ca2dec1 | |||
| 975744ec6f | |||
| cb7a03c1d5 | |||
| 00e5d6d3b2 | |||
| 8b489b9cc3 |
339
.github/workflows/phpunit-incremental.yml
vendored
Normal file
339
.github/workflows/phpunit-incremental.yml
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
# Incremental migration test workflow
|
||||
#
|
||||
# This workflow tests that migrations work correctly when upgrading from an older version.
|
||||
# It first installs the app at v0.14.0 (last version before forum_user_stats -> forum_users rename),
|
||||
# then upgrades to the current version and verifies all migrations run successfully.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: PHPUnit Incremental Migration
|
||||
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: phpunit-incremental-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
outputs:
|
||||
src: ${{ steps.changes.outputs.src }}
|
||||
|
||||
steps:
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: changes
|
||||
continue-on-error: true
|
||||
with:
|
||||
filters: |
|
||||
src:
|
||||
- '.github/workflows/**'
|
||||
- 'appinfo/**'
|
||||
- 'lib/**'
|
||||
- 'tests/**'
|
||||
- 'composer.json'
|
||||
- 'composer.lock'
|
||||
|
||||
incremental-pgsql:
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.src != 'false'
|
||||
|
||||
name: Incremental Migration (PostgreSQL)
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/nextcloud/continuous-integration-postgres-16:latest # zizmor: ignore[unpinned-images]
|
||||
ports:
|
||||
- 4444:5432/tcp
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: rootpassword
|
||||
POSTGRES_DB: nextcloud
|
||||
options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
|
||||
steps:
|
||||
- name: Checkout app (current)
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
path: app-current
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Detect app ID from appinfo/info.xml
|
||||
run: |
|
||||
APP_ID=$(grep -oP '(?<=<id>)[^<]+' app-current/appinfo/info.xml | head -1)
|
||||
echo "APP_NAME=$APP_ID" >> $GITHUB_ENV
|
||||
echo "Detected app ID: $APP_ID"
|
||||
|
||||
- name: Get supported server versions
|
||||
id: versions
|
||||
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
|
||||
with:
|
||||
filename: app-current/appinfo/info.xml
|
||||
|
||||
- name: Save current app for later
|
||||
run: |
|
||||
mkdir -p /tmp/app-backup
|
||||
cp -r app-current /tmp/app-backup/
|
||||
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: true
|
||||
repository: nextcloud/server
|
||||
ref: ${{ fromJson(steps.versions.outputs.branches)[0] }}
|
||||
|
||||
- name: Checkout app at v0.14.0 (pre-rename baseline)
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: v0.14.0
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php 8.3
|
||||
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql
|
||||
coverage: none
|
||||
ini-file: development
|
||||
ini-values: disable_functions=
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up dependencies (v0.14.0)
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: |
|
||||
composer remove nextcloud/ocp --dev --no-scripts || true
|
||||
composer i --no-scripts || composer i
|
||||
|
||||
- name: Set up Nextcloud and install app at v0.14.0
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
|
||||
echo "::group::Installing app at v0.14.0"
|
||||
./occ app:enable --force ${{ env.APP_NAME }}
|
||||
echo "::endgroup::"
|
||||
echo "::group::Database tables after v0.14.0 install"
|
||||
./occ db:convert-filecache-bigint --no-interaction || true
|
||||
PGPASSWORD=rootpassword psql -h 127.0.0.1 -p $DB_PORT -U root -d nextcloud -c "\dt oc_forum_*"
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Upgrade app to current version
|
||||
run: |
|
||||
echo "::group::Replacing app with current version"
|
||||
rm -rf apps/${{ env.APP_NAME }}
|
||||
cp -r /tmp/app-backup/app-current apps/${{ env.APP_NAME }}
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Set up dependencies (current)
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: |
|
||||
composer remove nextcloud/ocp --dev --no-scripts || true
|
||||
composer i
|
||||
|
||||
- name: Run migrations to current version
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
echo "::group::Running upgrade migrations"
|
||||
# Disable maintenance mode if it was left on
|
||||
./occ maintenance:mode --off || true
|
||||
# Disable and re-enable the app to trigger migrations
|
||||
# This simulates what happens when a user upgrades the app
|
||||
./occ app:disable ${{ env.APP_NAME }}
|
||||
./occ app:enable ${{ env.APP_NAME }}
|
||||
echo "::endgroup::"
|
||||
echo "::group::Database tables after upgrade"
|
||||
PGPASSWORD=rootpassword psql -h 127.0.0.1 -p $DB_PORT -U root -d nextcloud -c "\dt oc_forum_*"
|
||||
echo "::endgroup::"
|
||||
echo "::group::Checking forum_users table exists"
|
||||
PGPASSWORD=rootpassword psql -h 127.0.0.1 -p $DB_PORT -U root -d nextcloud -c "SELECT COUNT(*) FROM oc_forum_users" || exit 1
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Check PHPUnit integration script is defined
|
||||
id: check_integration
|
||||
continue-on-error: true
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: |
|
||||
composer run --list | grep '^ test:integration ' | wc -l | grep 1
|
||||
|
||||
- name: Run Nextcloud
|
||||
if: steps.check_integration.outcome == 'success'
|
||||
run: php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit integration
|
||||
if: steps.check_integration.outcome == 'success'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer run test:integration
|
||||
|
||||
- name: Print logs
|
||||
if: always()
|
||||
run: |
|
||||
cat data/nextcloud.log
|
||||
|
||||
incremental-mysql:
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.src != 'false'
|
||||
|
||||
name: Incremental Migration (MySQL)
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: ghcr.io/nextcloud/continuous-integration-mariadb-10.11:latest # zizmor: ignore[unpinned-images]
|
||||
ports:
|
||||
- 4444:3306/tcp
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: rootpassword
|
||||
MYSQL_DATABASE: nextcloud
|
||||
options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 5
|
||||
|
||||
steps:
|
||||
- name: Checkout app (current)
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
path: app-current
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Detect app ID from appinfo/info.xml
|
||||
run: |
|
||||
APP_ID=$(grep -oP '(?<=<id>)[^<]+' app-current/appinfo/info.xml | head -1)
|
||||
echo "APP_NAME=$APP_ID" >> $GITHUB_ENV
|
||||
echo "Detected app ID: $APP_ID"
|
||||
|
||||
- name: Get supported server versions
|
||||
id: versions
|
||||
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
|
||||
with:
|
||||
filename: app-current/appinfo/info.xml
|
||||
|
||||
- name: Save current app for later
|
||||
run: |
|
||||
mkdir -p /tmp/app-backup
|
||||
cp -r app-current /tmp/app-backup/
|
||||
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: true
|
||||
repository: nextcloud/server
|
||||
ref: ${{ fromJson(steps.versions.outputs.branches)[0] }}
|
||||
|
||||
- name: Checkout app at v0.14.0 (pre-rename baseline)
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: v0.14.0
|
||||
path: apps/${{ env.APP_NAME }}
|
||||
|
||||
- name: Set up php 8.3
|
||||
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql
|
||||
coverage: none
|
||||
ini-file: development
|
||||
ini-values: disable_functions=
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up dependencies (v0.14.0)
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: |
|
||||
composer remove nextcloud/ocp --dev --no-scripts || true
|
||||
composer i --no-scripts || composer i
|
||||
|
||||
- name: Set up Nextcloud and install app at v0.14.0
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
mkdir data
|
||||
./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
|
||||
echo "::group::Installing app at v0.14.0"
|
||||
./occ app:enable --force ${{ env.APP_NAME }}
|
||||
echo "::endgroup::"
|
||||
echo "::group::Database tables after v0.14.0 install"
|
||||
mysql -h 127.0.0.1 -P $DB_PORT -u root -prootpassword nextcloud -e "SHOW TABLES LIKE 'oc_forum_%'"
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Upgrade app to current version
|
||||
run: |
|
||||
echo "::group::Replacing app with current version"
|
||||
rm -rf apps/${{ env.APP_NAME }}
|
||||
cp -r /tmp/app-backup/app-current apps/${{ env.APP_NAME }}
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Set up dependencies (current)
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: |
|
||||
composer remove nextcloud/ocp --dev --no-scripts || true
|
||||
composer i
|
||||
|
||||
- name: Run migrations to current version
|
||||
env:
|
||||
DB_PORT: 4444
|
||||
run: |
|
||||
echo "::group::Running upgrade migrations"
|
||||
# Disable maintenance mode if it was left on
|
||||
./occ maintenance:mode --off || true
|
||||
# Disable and re-enable the app to trigger migrations
|
||||
# This simulates what happens when a user upgrades the app
|
||||
./occ app:disable ${{ env.APP_NAME }}
|
||||
./occ app:enable ${{ env.APP_NAME }}
|
||||
echo "::endgroup::"
|
||||
echo "::group::Database tables after upgrade"
|
||||
mysql -h 127.0.0.1 -P $DB_PORT -u root -prootpassword nextcloud -e "SHOW TABLES LIKE 'oc_forum_%'"
|
||||
echo "::endgroup::"
|
||||
echo "::group::Checking forum_users table exists"
|
||||
mysql -h 127.0.0.1 -P $DB_PORT -u root -prootpassword nextcloud -e "SELECT COUNT(*) FROM oc_forum_users" || exit 1
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Check PHPUnit integration script is defined
|
||||
id: check_integration
|
||||
continue-on-error: true
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: |
|
||||
composer run --list | grep '^ test:integration ' | wc -l | grep 1
|
||||
|
||||
- name: Run Nextcloud
|
||||
if: steps.check_integration.outcome == 'success'
|
||||
run: php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit integration
|
||||
if: steps.check_integration.outcome == 'success'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer run test:integration
|
||||
|
||||
- name: Print logs
|
||||
if: always()
|
||||
run: |
|
||||
cat data/nextcloud.log
|
||||
|
||||
summary:
|
||||
permissions:
|
||||
contents: none
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, incremental-pgsql, incremental-mysql]
|
||||
|
||||
if: always()
|
||||
|
||||
name: incremental-migration-summary
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.changes.outputs.src != 'false' && (needs.incremental-pgsql.result != 'success' || needs.incremental-mysql.result != 'success') }}; then exit 1; fi
|
||||
24
.github/workflows/phpunit-mysql.yml
vendored
24
.github/workflows/phpunit-mysql.yml
vendored
@@ -174,16 +174,34 @@ jobs:
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer run test:unit
|
||||
|
||||
- name: Check PHPUnit integration script is defined
|
||||
id: check_integration
|
||||
continue-on-error: true
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: |
|
||||
composer run --list | grep '^ test:integration ' | wc -l | grep 1
|
||||
|
||||
- name: Run Nextcloud
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outcome == 'success'
|
||||
run: php -S localhost:8080 &
|
||||
|
||||
- name: PHPUnit integration
|
||||
# Only run if phpunit integration config file exists
|
||||
if: steps.check_integration.outcome == 'success'
|
||||
working-directory: apps/${{ env.APP_NAME }}
|
||||
run: composer run test:integration
|
||||
|
||||
- name: Print logs
|
||||
if: always()
|
||||
run: |
|
||||
cat data/nextcloud.log
|
||||
|
||||
- name: Skipped
|
||||
# Fail the action when unit tests are not specified
|
||||
if: steps.check_phpunit.outcome == 'failure'
|
||||
# Fail the action when neither unit nor integration tests ran
|
||||
if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure'
|
||||
run: |
|
||||
echo 'PHPUnit tests are not specified in composer.json scripts'
|
||||
echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts'
|
||||
exit 1
|
||||
|
||||
summary:
|
||||
|
||||
@@ -1 +1 @@
|
||||
{".":"0.19.7"}
|
||||
{".":"0.20.5"}
|
||||
|
||||
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,5 +1,62 @@
|
||||
# Changelog
|
||||
|
||||
## [0.20.5](https://github.com/chenasraf/nextcloud-forum/compare/v0.20.4...v0.20.5) (2026-01-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow creating multiple custom roles ([362fdc8](https://github.com/chenasraf/nextcloud-forum/commit/362fdc8b032299a4f32d8e4b70163a3077dcab3c))
|
||||
* category header update ([0d98473](https://github.com/chenasraf/nextcloud-forum/commit/0d98473cbfc8f89a2b4962781449ccc881ab8eee))
|
||||
* db seeds ([ed04879](https://github.com/chenasraf/nextcloud-forum/commit/ed048795756f474ce8126aa37fb34a7c57fa2d65))
|
||||
* **l10n:** Update translations from Transifex ([3242a1c](https://github.com/chenasraf/nextcloud-forum/commit/3242a1cad51b90841ff1bde5ca7e231ea92687d9))
|
||||
|
||||
## [0.20.4](https://github.com/chenasraf/nextcloud-forum/compare/v0.20.3...v0.20.4) (2026-01-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **l10n:** Update translations from Transifex ([37a8284](https://github.com/chenasraf/nextcloud-forum/commit/37a82842b13012bf9bf578baf018bfa8b678d635))
|
||||
* **l10n:** Update translations from Transifex ([46b2c82](https://github.com/chenasraf/nextcloud-forum/commit/46b2c820e8bec48e432bf350d56a2d85f41da115))
|
||||
* **l10n:** Update translations from Transifex ([715b2ab](https://github.com/chenasraf/nextcloud-forum/commit/715b2ab6ff7a94ed431e0ca2525530e9e41f42c7))
|
||||
* **l10n:** Update translations from Transifex ([3ab3c1c](https://github.com/chenasraf/nextcloud-forum/commit/3ab3c1cc76b1f55e289d96dfd681d45b6e67b171))
|
||||
|
||||
## [0.20.3](https://github.com/chenasraf/nextcloud-forum/compare/v0.20.2...v0.20.3) (2026-01-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* db seed transactions logic ([c3d267f](https://github.com/chenasraf/nextcloud-forum/commit/c3d267f12269568e0a091cc094e043e4f1ad8cb8))
|
||||
* **l10n:** Update translations from Transifex ([679abe3](https://github.com/chenasraf/nextcloud-forum/commit/679abe3fb6ce7c8545bf99875092f20cf5d468b8))
|
||||
* **l10n:** Update translations from Transifex ([043af15](https://github.com/chenasraf/nextcloud-forum/commit/043af1580912d201a724313d4788d7a776e11934))
|
||||
|
||||
## [0.20.2](https://github.com/chenasraf/nextcloud-forum/compare/v0.20.1...v0.20.2) (2026-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bbcode cursor positions after inserting ([e2dcebc](https://github.com/chenasraf/nextcloud-forum/commit/e2dcebc6ee6e4d017f7f26fc86e72e6734a1f757))
|
||||
|
||||
## [0.20.1](https://github.com/chenasraf/nextcloud-forum/compare/v0.20.0...v0.20.1) (2026-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* db seed migrations ([c017bb3](https://github.com/chenasraf/nextcloud-forum/commit/c017bb3d09a517c19e772420311c23a957f25cba))
|
||||
* **l10n:** Update translations from Transifex ([67c92c0](https://github.com/chenasraf/nextcloud-forum/commit/67c92c05a3e7f58bbc05265087b763368653f7d3))
|
||||
|
||||
## [0.20.0](https://github.com/chenasraf/nextcloud-forum/compare/v0.19.7...v0.20.0) (2026-01-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add max-width to post/signature images ([00e5d6d](https://github.com/chenasraf/nextcloud-forum/commit/00e5d6d3b2e14939b233a80050f645ebd7b8503a))
|
||||
* add preference to auto subscribe to replied threads ([cb7a03c](https://github.com/chenasraf/nextcloud-forum/commit/cb7a03c1d51f2dc5642a47ab222b07cec6e01731))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* forum users tables migrations ([8b489b9](https://github.com/chenasraf/nextcloud-forum/commit/8b489b9cc3919dedf1463c7c7dd54e7a8009fc6f))
|
||||
|
||||
## [0.19.7](https://github.com/chenasraf/nextcloud-forum/compare/v0.19.6...v0.19.7) (2026-01-05)
|
||||
|
||||
|
||||
|
||||
46
Makefile
46
Makefile
@@ -17,13 +17,14 @@
|
||||
# - JS build is delegated to your package.json scripts (tool-agnostic).
|
||||
#
|
||||
# Common recipes:
|
||||
# make build → install deps & build
|
||||
# make dist → build source + appstore tarballs
|
||||
# make test → run PHP unit tests
|
||||
# make lint → lint JS & PHP
|
||||
# make openapi → generate OpenAPI JSON
|
||||
# make sign → print signature for GitHub tarball
|
||||
# make release → upload release to Nextcloud App Store
|
||||
# make build → install deps & build
|
||||
# make dist → build source + appstore tarballs
|
||||
# make test → run PHP unit tests
|
||||
# make test-integration → run PHP integration tests
|
||||
# make lint → lint JS & PHP
|
||||
# make openapi → generate OpenAPI JSON
|
||||
# make sign → print signature for GitHub tarball
|
||||
# make release → upload release to Nextcloud App Store
|
||||
#
|
||||
|
||||
app_name=forum
|
||||
@@ -242,10 +243,33 @@ test: composer
|
||||
exit 1; \
|
||||
fi; \
|
||||
echo "\x1b[32mUsing Nextcloud root: $$NC_ROOT\x1b[0m"; \
|
||||
NEXTCLOUD_ROOT="$$NC_ROOT" $(CURDIR)/vendor/phpunit/phpunit/phpunit -c tests/phpunit.xml; \
|
||||
if [ -f tests/phpunit.integration.xml ]; then \
|
||||
NEXTCLOUD_ROOT="$$NC_ROOT" $(CURDIR)/vendor/phpunit/phpunit/phpunit -c tests/phpunit.integration.xml; \
|
||||
fi
|
||||
NEXTCLOUD_ROOT="$$NC_ROOT" $(CURDIR)/vendor/phpunit/phpunit/phpunit -c tests/phpunit.xml
|
||||
|
||||
# test-integration:
|
||||
# - Run only PHP integration tests (database-dependent tests)
|
||||
# - These tests run against a real database and test migration/seeding
|
||||
.PHONY: test-integration
|
||||
test-integration: composer
|
||||
@NC_ROOT="$(NEXTCLOUD_ROOT)"; \
|
||||
if [ -n "$$NC_ROOT" ]; then \
|
||||
NC_ROOT=$$(echo "$$NC_ROOT" | sed "s|^\\\~|$$HOME|" | sed "s|^~|$$HOME|"); \
|
||||
fi; \
|
||||
if [ -z "$$NC_ROOT" ]; then \
|
||||
if [ -d "$(CURDIR)/../../../tests/bootstrap.php" ]; then \
|
||||
NC_ROOT="$(CURDIR)/../../.."; \
|
||||
fi; \
|
||||
fi; \
|
||||
if [ -z "$$NC_ROOT" ]; then \
|
||||
echo "\x1b[33mCould not find Nextcloud installation.\x1b[0m"; \
|
||||
echo "Set NEXTCLOUD_ROOT environment variable."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
if [ ! -f tests/phpunit.integration.xml ]; then \
|
||||
echo "\x1b[31mNo integration tests found (tests/phpunit.integration.xml missing)\x1b[0m"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
echo "\x1b[32mUsing Nextcloud root: $$NC_ROOT\x1b[0m"; \
|
||||
NEXTCLOUD_ROOT="$$NC_ROOT" $(CURDIR)/vendor/phpunit/phpunit/phpunit -c tests/phpunit.integration.xml
|
||||
|
||||
# test-docker:
|
||||
# - Run PHP unit tests inside a Nextcloud Docker container
|
||||
|
||||
@@ -37,7 +37,7 @@ This app is in early stages of development. While functional, you may encounter
|
||||
|
||||
The forum integrates seamlessly with your Nextcloud instance, using your existing users and groups for authentication and access control.
|
||||
]]></description>
|
||||
<version>0.19.7</version>
|
||||
<version>0.20.5</version>
|
||||
<licence>agpl</licence>
|
||||
<author mail="contact@casraf.dev" homepage="https://casraf.dev">Chen Asraf</author>
|
||||
<namespace>Forum</namespace>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"cs:fix": "php-cs-fixer fix",
|
||||
"psalm": "psalm --threads=1 --no-cache",
|
||||
"test:unit": "phpunit tests -c tests/phpunit.xml --colors=always --fail-on-warning --fail-on-risky",
|
||||
"test:integration": "phpunit tests -c tests/phpunit.integration.xml --colors=always --fail-on-warning --fail-on-risky",
|
||||
"openapi": "generate-spec"
|
||||
},
|
||||
"require": {
|
||||
|
||||
27
composer.lock
generated
27
composer.lock
generated
@@ -180,12 +180,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nextcloud-deps/ocp.git",
|
||||
"reference": "45612049852ea4234dd799a4a3281a397f0749c3"
|
||||
"reference": "a79703d9f38e964b003ae1cc805b6531d142fa93"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/45612049852ea4234dd799a4a3281a397f0749c3",
|
||||
"reference": "45612049852ea4234dd799a4a3281a397f0749c3",
|
||||
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/a79703d9f38e964b003ae1cc805b6531d142fa93",
|
||||
"reference": "a79703d9f38e964b003ae1cc805b6531d142fa93",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -220,7 +220,7 @@
|
||||
"issues": "https://github.com/nextcloud-deps/ocp/issues",
|
||||
"source": "https://github.com/nextcloud-deps/ocp/tree/stable32"
|
||||
},
|
||||
"time": "2025-12-16T00:55:52+00:00"
|
||||
"time": "2026-01-09T00:57:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
@@ -1035,12 +1035,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "5ba14c800ff89c74333c22d56ca1c1f35c424805"
|
||||
"reference": "ccfd723dc03e9864008d011603c412910180d7a6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5ba14c800ff89c74333c22d56ca1c1f35c424805",
|
||||
"reference": "5ba14c800ff89c74333c22d56ca1c1f35c424805",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ccfd723dc03e9864008d011603c412910180d7a6",
|
||||
"reference": "ccfd723dc03e9864008d011603c412910180d7a6",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
@@ -1173,10 +1173,11 @@
|
||||
"contao/core-bundle": "<4.13.57|>=5,<5.3.42|>=5.4,<5.6.5",
|
||||
"contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8",
|
||||
"contao/managed-edition": "<=1.5",
|
||||
"coreshop/core-shop": "<=4.1.7",
|
||||
"corveda/phpsandbox": "<1.3.5",
|
||||
"cosenary/instagram": "<=2.3",
|
||||
"couleurcitron/tarteaucitron-wp": "<0.3",
|
||||
"craftcms/cms": "<=4.16.5|>=5,<=5.8.6",
|
||||
"craftcms/cms": "<=4.16.16|>=5,<=5.8.20",
|
||||
"croogo/croogo": "<=4.0.7",
|
||||
"cuyz/valinor": "<0.12",
|
||||
"czim/file-handling": "<1.5|>=2,<2.3",
|
||||
@@ -1224,7 +1225,7 @@
|
||||
"drupal/commerce_alphabank_redirect": "<1.0.3",
|
||||
"drupal/commerce_eurobank_redirect": "<2.1.1",
|
||||
"drupal/config_split": "<1.10|>=2,<2.0.2",
|
||||
"drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8",
|
||||
"drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8",
|
||||
"drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8",
|
||||
"drupal/currency": "<3.5",
|
||||
"drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8",
|
||||
@@ -1339,7 +1340,7 @@
|
||||
"geshi/geshi": "<=1.0.9.1",
|
||||
"getformwork/formwork": "<2.2",
|
||||
"getgrav/grav": "<1.11.0.0-beta1",
|
||||
"getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<5.1.4",
|
||||
"getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<=5.2.1",
|
||||
"getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1",
|
||||
"getkirby/panel": "<2.5.14",
|
||||
"getkirby/starterkit": "<=3.7.0.2",
|
||||
@@ -1568,7 +1569,7 @@
|
||||
"october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1",
|
||||
"october/october": "<3.7.5",
|
||||
"october/rain": "<1.0.472|>=1.1,<1.1.2",
|
||||
"october/system": "<3.7.5",
|
||||
"october/system": "<=3.7.12|>=4,<=4.0.11",
|
||||
"oliverklee/phpunit": "<3.5.15",
|
||||
"omeka/omeka-s": "<4.0.3",
|
||||
"onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1",
|
||||
@@ -1681,7 +1682,7 @@
|
||||
"rap2hpoutre/laravel-log-viewer": "<0.13",
|
||||
"react/http": ">=0.7,<1.9",
|
||||
"really-simple-plugins/complianz-gdpr": "<6.4.2",
|
||||
"redaxo/source": "<5.20.1",
|
||||
"redaxo/source": "<=5.20.1",
|
||||
"remdex/livehelperchat": "<4.29",
|
||||
"renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1",
|
||||
"reportico-web/reportico": "<=8.1",
|
||||
@@ -2034,7 +2035,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-02T22:05:49+00:00"
|
||||
"time": "2026-01-09T19:06:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
||||
@@ -8,11 +8,11 @@ OC.L10N.register(
|
||||
"General" : "Агульныя",
|
||||
"Support" : "Падтрымка",
|
||||
"Attachment" : "Далучэнне",
|
||||
"Welcome to Nextcloud Forums" : "Вітаем на Форумах Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Вітаем на Форумах Nextcloud!",
|
||||
"Bold text" : "Тоўсты тэкст",
|
||||
"Italic text" : "Тэкст курсівам",
|
||||
"Underlined text" : "Падкрэслены тэкст",
|
||||
"Welcome to Nextcloud Forums" : "Вітаем на Форумах Nextcloud",
|
||||
"Forum" : "Форум",
|
||||
"Welcome to the forum!" : "Вітаем на форуме!",
|
||||
"Deleted user" : "Выдалены карыстальнік",
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"General" : "Агульныя",
|
||||
"Support" : "Падтрымка",
|
||||
"Attachment" : "Далучэнне",
|
||||
"Welcome to Nextcloud Forums" : "Вітаем на Форумах Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Вітаем на Форумах Nextcloud!",
|
||||
"Bold text" : "Тоўсты тэкст",
|
||||
"Italic text" : "Тэкст курсівам",
|
||||
"Underlined text" : "Падкрэслены тэкст",
|
||||
"Welcome to Nextcloud Forums" : "Вітаем на Форумах Nextcloud",
|
||||
"Forum" : "Форум",
|
||||
"Welcome to the forum!" : "Вітаем на форуме!",
|
||||
"Deleted user" : "Выдалены карыстальнік",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Skrytý obsah",
|
||||
"Spoilers" : "Spoilery",
|
||||
"Attachment" : "Příloha",
|
||||
"Welcome to Nextcloud Forums" : "Vítejte v Nextcloud fórech",
|
||||
"Welcome to the Nextcloud Forums!" : "Vítejte v Nextcloud fórech!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Toto je komunitou řízené fórum, vestavěné přímo do vámi využívané instance Nextcloud. Je možné zde probírat témata, sdílet nápady a spolupracovat s ostatními uživateli.",
|
||||
"Features:" : "Funkce:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Skloněný text",
|
||||
"Underlined text" : "Podtržený text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Neváhejte zahájit novou diskuzi nebo odpovězte na existující vlákna. Vesele pište příspěvky!.",
|
||||
"Welcome to Nextcloud Forums" : "Vítejte v Nextcloud fórech",
|
||||
"Forum" : "Diskuzní fórum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nová odpověď v {thread} ","{count} nové odpovědi v {thread} ","{count} nových odpovědí v {thread} ","{count} nové odpovědi v {thread} "],
|
||||
"{user} mentioned you in {thread}" : "{user} vás zmínil(a) v {thread}",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Skrytý obsah",
|
||||
"Spoilers" : "Spoilery",
|
||||
"Attachment" : "Příloha",
|
||||
"Welcome to Nextcloud Forums" : "Vítejte v Nextcloud fórech",
|
||||
"Welcome to the Nextcloud Forums!" : "Vítejte v Nextcloud fórech!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Toto je komunitou řízené fórum, vestavěné přímo do vámi využívané instance Nextcloud. Je možné zde probírat témata, sdílet nápady a spolupracovat s ostatními uživateli.",
|
||||
"Features:" : "Funkce:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Skloněný text",
|
||||
"Underlined text" : "Podtržený text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Neváhejte zahájit novou diskuzi nebo odpovězte na existující vlákna. Vesele pište příspěvky!.",
|
||||
"Welcome to Nextcloud Forums" : "Vítejte v Nextcloud fórech",
|
||||
"Forum" : "Diskuzní fórum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nová odpověď v {thread} ","{count} nové odpovědi v {thread} ","{count} nových odpovědí v {thread} ","{count} nové odpovědi v {thread} "],
|
||||
"{user} mentioned you in {thread}" : "{user} vás zmínil(a) v {thread}",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Versteckter Inhalt",
|
||||
"Spoilers" : "Spoiler",
|
||||
"Attachment" : "Anhang",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Welcome to the Nextcloud Forums!" : "Willkommen in den Nextcloud Foren!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Dies ist ein Community-Forum, das direkt in deine Nextcloud-Instanz integriert ist. Hier kannst du Themen diskutieren, Ideen austauschen und mit anderen Nutzern zusammenarbeiten.",
|
||||
"Features:" : "Funktionen:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Kursiver Text",
|
||||
"Underlined text" : "Unterstrichener Text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Gerne eine neue Diskussion starten oder auf bestehende Beiträge antworten. Viel Spaß beim Posten!",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} neue Antwort in {thread}","{count} neue Antworten in {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} hat dich in {thread} erwähnt",
|
||||
@@ -282,6 +282,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "Einrichten, wie du Benachrichtigungen erhältst",
|
||||
"Auto-subscribe to threads I create" : "Themen die ich erstellt habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Wenn diese Option aktiviert ist, erhältst du automatisch Benachrichtigungen für Antworten auf von dir erstellte Themen",
|
||||
"Auto-subscribe to threads I reply to" : "Themen auf die ich geantwortet habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Wenn diese Option aktiviert ist, erhältst du automatisch Benachrichtigungen für Antworten in Themen, in denen du geantwortet hast",
|
||||
"Files" : "Dateien",
|
||||
"Configure file upload settings" : "Einstellungen für das Hochladen von Dateien",
|
||||
"Upload directory" : "Hochladeverzeichnis",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Versteckter Inhalt",
|
||||
"Spoilers" : "Spoiler",
|
||||
"Attachment" : "Anhang",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Welcome to the Nextcloud Forums!" : "Willkommen in den Nextcloud Foren!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Dies ist ein Community-Forum, das direkt in deine Nextcloud-Instanz integriert ist. Hier kannst du Themen diskutieren, Ideen austauschen und mit anderen Nutzern zusammenarbeiten.",
|
||||
"Features:" : "Funktionen:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Kursiver Text",
|
||||
"Underlined text" : "Unterstrichener Text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Gerne eine neue Diskussion starten oder auf bestehende Beiträge antworten. Viel Spaß beim Posten!",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} neue Antwort in {thread}","{count} neue Antworten in {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} hat dich in {thread} erwähnt",
|
||||
@@ -280,6 +280,8 @@
|
||||
"Configure how you receive notifications" : "Einrichten, wie du Benachrichtigungen erhältst",
|
||||
"Auto-subscribe to threads I create" : "Themen die ich erstellt habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Wenn diese Option aktiviert ist, erhältst du automatisch Benachrichtigungen für Antworten auf von dir erstellte Themen",
|
||||
"Auto-subscribe to threads I reply to" : "Themen auf die ich geantwortet habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Wenn diese Option aktiviert ist, erhältst du automatisch Benachrichtigungen für Antworten in Themen, in denen du geantwortet hast",
|
||||
"Files" : "Dateien",
|
||||
"Configure file upload settings" : "Einstellungen für das Hochladen von Dateien",
|
||||
"Upload directory" : "Hochladeverzeichnis",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Versteckter Inhalt",
|
||||
"Spoilers" : "Spoilerwarnung",
|
||||
"Attachment" : "Anhang",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Welcome to the Nextcloud Forums!" : "Willkommen in den Nextcloud Foren!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Dies ist ein Community-Forum, das direkt in Ihre Nextcloud-Instanz integriert ist. Hier können Sie Themen diskutieren, Ideen austauschen und mit anderen Nutzern zusammenarbeiten.",
|
||||
"Features:" : "Funktionen:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Kursiver Text",
|
||||
"Underlined text" : "Unterstrichener Text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Gerne eine neue Diskussion starten oder auf bestehende Beiträge antworten. Viel Spaß beim Posten!",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} neue Antwort in {thread}","{count} neue Antworten in {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} hat Sie in {thread} erwähnt",
|
||||
@@ -282,6 +282,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "Einrichten, wie Sie Benachrichtigungen erhalten",
|
||||
"Auto-subscribe to threads I create" : "Themen die ich erstellt habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Wenn diese Option aktiviert ist, erhalten Sie automatisch Benachrichtigungen für Antworten auf von Ihnen erstellte Themen",
|
||||
"Auto-subscribe to threads I reply to" : "Themen auf die ich geantwortet habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Wenn diese Option aktiviert ist, erhalten Sie automatisch Benachrichtigungen für Antworten in Themen, in denen Sie geantwortet haben",
|
||||
"Files" : "Dateien",
|
||||
"Configure file upload settings" : "Einstellungen für das Hochladen von Dateien",
|
||||
"Upload directory" : "Hochladeverzeichnis",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Versteckter Inhalt",
|
||||
"Spoilers" : "Spoilerwarnung",
|
||||
"Attachment" : "Anhang",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Welcome to the Nextcloud Forums!" : "Willkommen in den Nextcloud Foren!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Dies ist ein Community-Forum, das direkt in Ihre Nextcloud-Instanz integriert ist. Hier können Sie Themen diskutieren, Ideen austauschen und mit anderen Nutzern zusammenarbeiten.",
|
||||
"Features:" : "Funktionen:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Kursiver Text",
|
||||
"Underlined text" : "Unterstrichener Text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Gerne eine neue Diskussion starten oder auf bestehende Beiträge antworten. Viel Spaß beim Posten!",
|
||||
"Welcome to Nextcloud Forums" : "Willkommen in den Nextcloud Foren",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} neue Antwort in {thread}","{count} neue Antworten in {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} hat Sie in {thread} erwähnt",
|
||||
@@ -280,6 +280,8 @@
|
||||
"Configure how you receive notifications" : "Einrichten, wie Sie Benachrichtigungen erhalten",
|
||||
"Auto-subscribe to threads I create" : "Themen die ich erstellt habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Wenn diese Option aktiviert ist, erhalten Sie automatisch Benachrichtigungen für Antworten auf von Ihnen erstellte Themen",
|
||||
"Auto-subscribe to threads I reply to" : "Themen auf die ich geantwortet habe, automatisch abonnieren",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Wenn diese Option aktiviert ist, erhalten Sie automatisch Benachrichtigungen für Antworten in Themen, in denen Sie geantwortet haben",
|
||||
"Files" : "Dateien",
|
||||
"Configure file upload settings" : "Einstellungen für das Hochladen von Dateien",
|
||||
"Upload directory" : "Hochladeverzeichnis",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Hidden content",
|
||||
"Spoilers" : "Spoilers",
|
||||
"Attachment" : "Attachment",
|
||||
"Welcome to Nextcloud Forums" : "Welcome to Nextcloud Forums",
|
||||
"Welcome to the Nextcloud Forums!" : "Welcome to the Nextcloud Forums!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users.",
|
||||
"Features:" : "Features:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Italic text",
|
||||
"Underlined text" : "Underlined text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Feel free to start a new discussion or reply to existing threads. Happy posting!",
|
||||
"Welcome to Nextcloud Forums" : "Welcome to Nextcloud Forums",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} new reply in {thread}","{count} new replies in {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mentioned you in {thread}",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Hidden content",
|
||||
"Spoilers" : "Spoilers",
|
||||
"Attachment" : "Attachment",
|
||||
"Welcome to Nextcloud Forums" : "Welcome to Nextcloud Forums",
|
||||
"Welcome to the Nextcloud Forums!" : "Welcome to the Nextcloud Forums!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users.",
|
||||
"Features:" : "Features:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Italic text",
|
||||
"Underlined text" : "Underlined text",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Feel free to start a new discussion or reply to existing threads. Happy posting!",
|
||||
"Welcome to Nextcloud Forums" : "Welcome to Nextcloud Forums",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} new reply in {thread}","{count} new replies in {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mentioned you in {thread}",
|
||||
|
||||
@@ -2,11 +2,15 @@ OC.L10N.register(
|
||||
"forum",
|
||||
{
|
||||
"Admin" : "Administrador",
|
||||
"Administrator role with full permissions" : "Rol de administrador con permisos completos",
|
||||
"Moderator" : "Moderador",
|
||||
"Moderator role with elevated permissions" : "Rol de moderador con permisos elevados",
|
||||
"User" : "Usuario",
|
||||
"Default user role with basic permissions" : "Rol de usuario por defecto con permisos básicos",
|
||||
"Guest" : "Invitado",
|
||||
"Guest role for unauthenticated users with read-only access" : "Rol de invitado para usuarios sin autenticar con acceso de solo lectura",
|
||||
"General" : "General",
|
||||
"General discussion categories" : "Categorías de discusión general",
|
||||
"Support" : "Soporte",
|
||||
"Bold text" : "Texto en negrita",
|
||||
"Underlined text" : "Texto subrayado",
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
{ "translations": {
|
||||
"Admin" : "Administrador",
|
||||
"Administrator role with full permissions" : "Rol de administrador con permisos completos",
|
||||
"Moderator" : "Moderador",
|
||||
"Moderator role with elevated permissions" : "Rol de moderador con permisos elevados",
|
||||
"User" : "Usuario",
|
||||
"Default user role with basic permissions" : "Rol de usuario por defecto con permisos básicos",
|
||||
"Guest" : "Invitado",
|
||||
"Guest role for unauthenticated users with read-only access" : "Rol de invitado para usuarios sin autenticar con acceso de solo lectura",
|
||||
"General" : "General",
|
||||
"General discussion categories" : "Categorías de discusión general",
|
||||
"Support" : "Soporte",
|
||||
"Bold text" : "Texto en negrita",
|
||||
"Underlined text" : "Texto subrayado",
|
||||
|
||||
@@ -18,7 +18,6 @@ OC.L10N.register(
|
||||
"Inline code" : "Código en línea",
|
||||
"Spoilers" : "Spoilers",
|
||||
"Attachment" : "Adjunto",
|
||||
"Welcome to Nextcloud Forums" : "Bienvenido a los Foros de Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "¡Bienvenido a los Foros de Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Este es un foro impulsado por la comunidad integrado directamente en tu instancia de Nextcloud. Aquí puedes discutir temas, compartir ideas y colaborar con otros usuarios.",
|
||||
"Features:" : "Funciones:",
|
||||
@@ -34,6 +33,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Texto en cursiva",
|
||||
"Underlined text" : "Texto subrayado",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "No dudes en iniciar una nueva discusión o responder a hilos existentes. ¡Feliz publicación!",
|
||||
"Welcome to Nextcloud Forums" : "Bienvenido a los Foros de Nextcloud",
|
||||
"Forum" : "Foro",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nueva respuesta en {thread}","{count} nuevas respuestas en {thread}","{count} nuevas respuestas en {thread}"],
|
||||
"Welcome to the forum!" : "¡Bienvenido al foro!",
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"Inline code" : "Código en línea",
|
||||
"Spoilers" : "Spoilers",
|
||||
"Attachment" : "Adjunto",
|
||||
"Welcome to Nextcloud Forums" : "Bienvenido a los Foros de Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "¡Bienvenido a los Foros de Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Este es un foro impulsado por la comunidad integrado directamente en tu instancia de Nextcloud. Aquí puedes discutir temas, compartir ideas y colaborar con otros usuarios.",
|
||||
"Features:" : "Funciones:",
|
||||
@@ -32,6 +31,7 @@
|
||||
"Italic text" : "Texto en cursiva",
|
||||
"Underlined text" : "Texto subrayado",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "No dudes en iniciar una nueva discusión o responder a hilos existentes. ¡Feliz publicación!",
|
||||
"Welcome to Nextcloud Forums" : "Bienvenido a los Foros de Nextcloud",
|
||||
"Forum" : "Foro",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nueva respuesta en {thread}","{count} nuevas respuestas en {thread}","{count} nuevas respuestas en {thread}"],
|
||||
"Welcome to the forum!" : "¡Bienvenido al foro!",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Peidetud sisu",
|
||||
"Spoilers" : "Spoilerid/tujurikkujad",
|
||||
"Attachment" : "Manus",
|
||||
"Welcome to Nextcloud Forums" : "Tere tulemast kasutama Nextcloudi Foorumit",
|
||||
"Welcome to the Nextcloud Forums!" : "Tere tulemast kasutama rakendust Nextcloudi Foorumid!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "See on kogukonnapõhine foorum, mis toimib otse sinu Nextcloudi serveris. Siin saad arutleda erinevatel teemadel, jagada mõtteid ning osaleda ühistöös.",
|
||||
"Features:" : "Foorumi võimalused:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Tekst kaldkirjas",
|
||||
"Underlined text" : "Allajoonitud tekst",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Alusta uut keskustelu või vasta olemasolevale jutulõngale. Soovime sulle toimekat suhtlust!",
|
||||
"Welcome to Nextcloud Forums" : "Tere tulemast kasutama Nextcloudi Foorumit",
|
||||
"Forum" : "Foorum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} uus vastus jutulõngas {thread}","{count} uut vastust jutulõngas {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mainis sind jutulõngas „{thread}“",
|
||||
@@ -281,6 +281,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "Seadista endale teavituste saatmise viisi",
|
||||
"Auto-subscribe to threads I create" : "Telli minu loodud jutulõngad automaatselt",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Kui see eelistus on kasutusel, siis saad automaatselt teavituse sinu loodud jutulõngade vastuste puhul",
|
||||
"Auto-subscribe to threads I reply to" : "Telli minu poolt vastatud jutulõngad automaatselt",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Kui see eelistus on kasutusel, siis saad automaatselt teavituse nende jutulõngade kohta, kuhu oled vastuse kirjutanud",
|
||||
"Files" : "Failid",
|
||||
"Configure file upload settings" : "Seadista failide üleslaadimise seadistusi",
|
||||
"Upload directory" : "Üleslaadimiskaust",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Peidetud sisu",
|
||||
"Spoilers" : "Spoilerid/tujurikkujad",
|
||||
"Attachment" : "Manus",
|
||||
"Welcome to Nextcloud Forums" : "Tere tulemast kasutama Nextcloudi Foorumit",
|
||||
"Welcome to the Nextcloud Forums!" : "Tere tulemast kasutama rakendust Nextcloudi Foorumid!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "See on kogukonnapõhine foorum, mis toimib otse sinu Nextcloudi serveris. Siin saad arutleda erinevatel teemadel, jagada mõtteid ning osaleda ühistöös.",
|
||||
"Features:" : "Foorumi võimalused:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Tekst kaldkirjas",
|
||||
"Underlined text" : "Allajoonitud tekst",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Alusta uut keskustelu või vasta olemasolevale jutulõngale. Soovime sulle toimekat suhtlust!",
|
||||
"Welcome to Nextcloud Forums" : "Tere tulemast kasutama Nextcloudi Foorumit",
|
||||
"Forum" : "Foorum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} uus vastus jutulõngas {thread}","{count} uut vastust jutulõngas {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mainis sind jutulõngas „{thread}“",
|
||||
@@ -279,6 +279,8 @@
|
||||
"Configure how you receive notifications" : "Seadista endale teavituste saatmise viisi",
|
||||
"Auto-subscribe to threads I create" : "Telli minu loodud jutulõngad automaatselt",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Kui see eelistus on kasutusel, siis saad automaatselt teavituse sinu loodud jutulõngade vastuste puhul",
|
||||
"Auto-subscribe to threads I reply to" : "Telli minu poolt vastatud jutulõngad automaatselt",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Kui see eelistus on kasutusel, siis saad automaatselt teavituse nende jutulõngade kohta, kuhu oled vastuse kirjutanud",
|
||||
"Files" : "Failid",
|
||||
"Configure file upload settings" : "Seadista failide üleslaadimise seadistusi",
|
||||
"Upload directory" : "Üleslaadimiskaust",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Ábhar i bhfolach",
|
||||
"Spoilers" : "Spoiléirí",
|
||||
"Attachment" : "Ceangaltán",
|
||||
"Welcome to Nextcloud Forums" : "Fáilte go Fóraim Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Fáilte go dtí Fóraim Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Is fóram pobail-thiomáinte é seo atá tógtha isteach i do chás Nextcloud. Anseo is féidir leat topaicí a phlé, smaointe a roinnt agus comhoibriú le húsáideoirí eile.",
|
||||
"Features:" : "Gnéithe:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Téacs iodálach",
|
||||
"Underlined text" : "Téacs a bhfuil líne faoi",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Ná bíodh drogall ort plé nua a thosú nó freagra a thabhairt ar shnáitheanna atá ann cheana féin. Go n-éirí leat ag postáil!",
|
||||
"Welcome to Nextcloud Forums" : "Fáilte go Fóraim Nextcloud",
|
||||
"Forum" : "Fóram",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count}freagra nua i {thread}","{count} freagraí nua i {thread}","{count} freagraí nua i {thread}","{count} freagraí nua i {thread}","{count} freagraí nua i {thread}"],
|
||||
"{user} mentioned you in {thread}" : "Luaigh {user} thú i {thread}",
|
||||
@@ -282,6 +282,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "Cumraigh conas a fhaigheann tú fógraí",
|
||||
"Auto-subscribe to threads I create" : "Liostáil go huathoibríoch le snáitheanna a chruthaím",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Nuair a bheidh sé cumasaithe, gheobhaidh tú fógraí go huathoibríoch le haghaidh freagraí ar shnáitheanna a chruthaíonn tú",
|
||||
"Auto-subscribe to threads I reply to" : "Liostáil go huathoibríoch le snáitheanna a bhfreagraím orthu",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Nuair a bheidh sé cumasaithe, gheobhaidh tú fógraí go huathoibríoch faoi fhreagraí nua i snáitheanna ar fhreagair tú iad",
|
||||
"Files" : "Comhaid",
|
||||
"Configure file upload settings" : "Cumraigh socruithe uaslódála comhad",
|
||||
"Upload directory" : "Uaslódáil eolaire",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Ábhar i bhfolach",
|
||||
"Spoilers" : "Spoiléirí",
|
||||
"Attachment" : "Ceangaltán",
|
||||
"Welcome to Nextcloud Forums" : "Fáilte go Fóraim Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Fáilte go dtí Fóraim Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Is fóram pobail-thiomáinte é seo atá tógtha isteach i do chás Nextcloud. Anseo is féidir leat topaicí a phlé, smaointe a roinnt agus comhoibriú le húsáideoirí eile.",
|
||||
"Features:" : "Gnéithe:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Téacs iodálach",
|
||||
"Underlined text" : "Téacs a bhfuil líne faoi",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Ná bíodh drogall ort plé nua a thosú nó freagra a thabhairt ar shnáitheanna atá ann cheana féin. Go n-éirí leat ag postáil!",
|
||||
"Welcome to Nextcloud Forums" : "Fáilte go Fóraim Nextcloud",
|
||||
"Forum" : "Fóram",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count}freagra nua i {thread}","{count} freagraí nua i {thread}","{count} freagraí nua i {thread}","{count} freagraí nua i {thread}","{count} freagraí nua i {thread}"],
|
||||
"{user} mentioned you in {thread}" : "Luaigh {user} thú i {thread}",
|
||||
@@ -280,6 +280,8 @@
|
||||
"Configure how you receive notifications" : "Cumraigh conas a fhaigheann tú fógraí",
|
||||
"Auto-subscribe to threads I create" : "Liostáil go huathoibríoch le snáitheanna a chruthaím",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Nuair a bheidh sé cumasaithe, gheobhaidh tú fógraí go huathoibríoch le haghaidh freagraí ar shnáitheanna a chruthaíonn tú",
|
||||
"Auto-subscribe to threads I reply to" : "Liostáil go huathoibríoch le snáitheanna a bhfreagraím orthu",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Nuair a bheidh sé cumasaithe, gheobhaidh tú fógraí go huathoibríoch faoi fhreagraí nua i snáitheanna ar fhreagair tú iad",
|
||||
"Files" : "Comhaid",
|
||||
"Configure file upload settings" : "Cumraigh socruithe uaslódála comhad",
|
||||
"Upload directory" : "Uaslódáil eolaire",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Contido agochado",
|
||||
"Spoilers" : "Destripes",
|
||||
"Attachment" : "Anexo",
|
||||
"Welcome to Nextcloud Forums" : "Dámoslle a benvida a os Foros de Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Dámoslle a benvida a os Foros de Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Este é un foro impulsado pola comunidade integrado directamente na súa instancia de Nextcloud. Aquí pode debater temas, compartir ideas e colaborar con outros usuarios.",
|
||||
"Features:" : "Funcionalidades:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Texto en cursiva",
|
||||
"Underlined text" : "Texto subliñado",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Síntase libre de iniciar un novo debate ou de responder a fíos existentes. Feliz publicación!",
|
||||
"Welcome to Nextcloud Forums" : "Dámoslle a benvida a os Foros de Nextcloud",
|
||||
"Forum" : "Foro",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nova resposta en {thread}","{count} novas respostas en {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mencionouno a Vde. en {thread}",
|
||||
@@ -93,7 +93,7 @@ OC.L10N.register(
|
||||
"Pick file from Nextcloud" : "Seleccionar un ficheiro en Nextcloud",
|
||||
"Upload file to Nextcloud" : "Enviar un ficheiro a Nextcloud",
|
||||
"Uploading file …" : "Enviando o ficheiro…",
|
||||
"Upload failed" : "Produciuse algún fallo no envío",
|
||||
"Upload failed" : "Produciuse un fallo no envío",
|
||||
"Close" : "Pechar",
|
||||
"Pick a file to attach" : "Escolla un ficheiro para anexar",
|
||||
"Failed to upload file" : "Produciuse un fallo ao enviar o ficheiro",
|
||||
@@ -280,8 +280,10 @@ OC.L10N.register(
|
||||
"Error loading preferences" : "Produciuse un erro ao cargar as preferencias",
|
||||
"Notifications" : "Notificacións",
|
||||
"Configure how you receive notifications" : "Configure como quere recibir as notificacións",
|
||||
"Auto-subscribe to threads I create" : "Subscríbirse automaticamente aos fíos que creou",
|
||||
"Auto-subscribe to threads I create" : "Subscribirme automaticamente aos fíos que creo",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Cando estea activado, recibirá automaticamente notificacións das respostas aos fíos que cree",
|
||||
"Auto-subscribe to threads I reply to" : "Subscribirme automaticamente aos fíos que respondo",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Cando estea activado, recibirá automaticamente notificacións de novas respostas nos fíos aos que respondeu",
|
||||
"Files" : "Ficheiros",
|
||||
"Configure file upload settings" : "Configurar os axustes de envío de ficheiros",
|
||||
"Upload directory" : "Directorio ao que enviar",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Contido agochado",
|
||||
"Spoilers" : "Destripes",
|
||||
"Attachment" : "Anexo",
|
||||
"Welcome to Nextcloud Forums" : "Dámoslle a benvida a os Foros de Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Dámoslle a benvida a os Foros de Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Este é un foro impulsado pola comunidade integrado directamente na súa instancia de Nextcloud. Aquí pode debater temas, compartir ideas e colaborar con outros usuarios.",
|
||||
"Features:" : "Funcionalidades:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Texto en cursiva",
|
||||
"Underlined text" : "Texto subliñado",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Síntase libre de iniciar un novo debate ou de responder a fíos existentes. Feliz publicación!",
|
||||
"Welcome to Nextcloud Forums" : "Dámoslle a benvida a os Foros de Nextcloud",
|
||||
"Forum" : "Foro",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nova resposta en {thread}","{count} novas respostas en {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mencionouno a Vde. en {thread}",
|
||||
@@ -91,7 +91,7 @@
|
||||
"Pick file from Nextcloud" : "Seleccionar un ficheiro en Nextcloud",
|
||||
"Upload file to Nextcloud" : "Enviar un ficheiro a Nextcloud",
|
||||
"Uploading file …" : "Enviando o ficheiro…",
|
||||
"Upload failed" : "Produciuse algún fallo no envío",
|
||||
"Upload failed" : "Produciuse un fallo no envío",
|
||||
"Close" : "Pechar",
|
||||
"Pick a file to attach" : "Escolla un ficheiro para anexar",
|
||||
"Failed to upload file" : "Produciuse un fallo ao enviar o ficheiro",
|
||||
@@ -278,8 +278,10 @@
|
||||
"Error loading preferences" : "Produciuse un erro ao cargar as preferencias",
|
||||
"Notifications" : "Notificacións",
|
||||
"Configure how you receive notifications" : "Configure como quere recibir as notificacións",
|
||||
"Auto-subscribe to threads I create" : "Subscríbirse automaticamente aos fíos que creou",
|
||||
"Auto-subscribe to threads I create" : "Subscribirme automaticamente aos fíos que creo",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Cando estea activado, recibirá automaticamente notificacións das respostas aos fíos que cree",
|
||||
"Auto-subscribe to threads I reply to" : "Subscribirme automaticamente aos fíos que respondo",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Cando estea activado, recibirá automaticamente notificacións de novas respostas nos fíos aos que respondeu",
|
||||
"Files" : "Ficheiros",
|
||||
"Configure file upload settings" : "Configurar os axustes de envío de ficheiros",
|
||||
"Upload directory" : "Directorio ao que enviar",
|
||||
|
||||
128
l10n/he.js
128
l10n/he.js
@@ -1,14 +1,14 @@
|
||||
OC.L10N.register(
|
||||
"forum",
|
||||
{
|
||||
"Admin" : "מנהל",
|
||||
"Administrator role with full permissions" : "תפקיד מנהל עם הרשאות מלאות",
|
||||
"Admin" : "אדמין",
|
||||
"Administrator role with full permissions" : "תפקיד אדמין עם הרשאות מלאות",
|
||||
"Moderator" : "מפקח",
|
||||
"Moderator role with elevated permissions" : "תפקיד מפקח עם הרשאות מוגבהות",
|
||||
"User" : "משתמש",
|
||||
"Default user role with basic permissions" : "תפקיד משתמש רגיל עם הרשאות בסיסיות",
|
||||
"Default user role with basic permissions" : "תפקיד משתמש ברירת מחדל עם הרשאות בסיסיות",
|
||||
"Guest" : "אורח",
|
||||
"Guest role for unauthenticated users with read-only access" : "תפקיד אורח עם הרשאות גישה לקריאה בלבד",
|
||||
"Guest role for unauthenticated users with read-only access" : "תפקיד אורח למשתמשים לא-מחוברים עם הרשאות גישה לקריאה בלבד",
|
||||
"General" : "כללי",
|
||||
"General discussion categories" : "קטגוריות לדיונים כלליים",
|
||||
"General discussions" : "דיונים כלליים",
|
||||
@@ -20,24 +20,29 @@ OC.L10N.register(
|
||||
"Hidden content" : "תוכן מוסתר",
|
||||
"Spoilers" : "ספוילרים",
|
||||
"Attachment" : "קובץ מצורף",
|
||||
"Welcome to Nextcloud Forums" : "ברוכים הבאים לפורום Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "ברוכים הבאים לפורום של Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "זהו פורום מונחה-קהילה מובנה ישירות ל-Nextcloud שלך. כאן ניתן להתדיין בנושאים שונים, לחלוק רעיונות ולשתף פעולה עם משתמשים אחרים.",
|
||||
"Features:" : "יכולות:",
|
||||
"Create and reply to threads" : "יצירה והגבה לשרשורים",
|
||||
"Organize discussions by categories" : "אירגון דיונים על-פי קטגוריות",
|
||||
"Use BBCode for rich text formatting" : "השתמשו ב-BBCode לפורמט טקסט עשיר",
|
||||
"Attach files from your Nextcloud storage" : "צירוף קבצים מאחסון ה-Nextcloud שלך",
|
||||
"React to posts" : "תגובות לשרשורים",
|
||||
"Track read/unread threads" : "מעקב אחר שרשורים שנקראו\\לא נקראו",
|
||||
"BBCode examples:" : "דוגמאות BBCode:",
|
||||
"Bold text" : "טקסט בולט",
|
||||
"Use %1$stext%2$s" : "השתמשו ב%1$sטקסט%2$s",
|
||||
"Italic text" : "טקסט מוטה",
|
||||
"Underlined text" : "טקסט בקו תחתון",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "הרגישו חופשי להתחיל שרשור חדש או להגיב לשרשורים קיימים. כתיבה מהנה!",
|
||||
"Welcome to Nextcloud Forums" : "ברוכים הבאים לפורום Nextcloud",
|
||||
"Forum" : "פורום",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["תגובה {count} חדשה ב-{thread}","{count} תגובות חדשות ב-{thread}","{count} תגובות חדשות ב-{thread}"],
|
||||
"{user} mentioned you in {thread}" : "אוזכרת על ידי {user}בנושא {thread}",
|
||||
"Welcome to the forum!" : "ברוכים הבאים לפורום!",
|
||||
"Deleted user" : "משתמש מחוק",
|
||||
"A community-driven forum built right into your Nextcloud instance" : "פורום מונחה-קהילה מובנה ישירות להתקנת ה Nextcloud שלכם.",
|
||||
"Create discussions, share ideas and collaborate with your community directly in Nextcloud.\n\n**⚠️ Early Development Notice:**\nThis app is in early stages of development. While functional, you may encounter bugs or incomplete features. Please report any issues on GitHub and consider backing up your data regularly.\n\n**Key features:**\n- **Thread-based Discussions** - Create and reply to organized discussion threads\n- **Category Organization** - Structure your forum with customizable categories and headers\n- **Rich Text Formatting** - Use BBCode for formatting posts with bold, italic, links, images, code blocks and more\n- **File Attachments** - Attach files from your Nextcloud storage to posts\n- **Post Reactions** - React to posts with emoji reactions\n- **Read/Unread Tracking** - Keep track of which threads you've read\n- **Search** - Find discussions quickly with built-in search\n- **User Profiles** - View user post history and statistics\n- **Role-Based Permissions** - Control access and moderation with flexible roles\n- **Guest Access**: Optional public access for unauthenticated users with configurable permissions\n- **Admin Tools** - Manage categories, roles, BBCodes and forum settings\n- **Moderation Tools** - Pin, lock and manage threads and posts\n\n**Perfect for:**\n- Team discussions and collaboration\n- Community forums\n- Support channels\n- Knowledge bases\n- Project discussions\n- Internal communication\n\nThe forum integrates seamlessly with your Nextcloud instance, using your existing users and groups for authentication and access control." : "צרו דיונים, חלקו רעיונות ושתפו פעולה עם משתמשים ישירות ב-Nextcloud.\n\n**⚠️ הודעה לגבי שלב פיתוח מוקדם:**\nהיישום הזה נמצא בשלבי פיתוח מוקדמים. בעוד היישום שמיש, אתם עלולים להיתקל בבאגים או יכולות לא שלמות. נא לדווח בעיות ב-GitHub ולשקול לגבות את המידע שלכם באופן תכוף.\n\n**יכולות מפתח:**\n- **דיונים מונחי נושאים** - צרו והגיבו לנושאי דיון\n- **ארגון קטגוריות** - צרו מבנה לפורום שלכם עם קטגוריות וכותרות הניתנות להתאמה\n- **פורמט טקסט עשיר** - השתמשו ב-BBCode לפרמוט של פוסטים עם הדגשות, הטיות, קישורים, תמונות, קטעי קוד ועוד\n- **צירוף קבצים** - צרפו קבצים מתוך ה-Nextcloud שלכם לפוסטים\n- **תגובונים לפוסטים** - הגיבו לפוסטים עם תגובוני אמוג'י\n- **מעקב נקרא\\לא נקרא** - עקבו אחרי סטטוס הקריאה של פוסטים\n- **חיפוש** - מצאו דיונים מהר בעזרת חיפוש מובנה\n- **פרופילי משתמשים** - צפו בהיסטוריה וסטטיסטיקות של משתמשים\n- **הרשאות מבוססי תפקיד** - שלטו בגישה ומודרציה עם תפקידים גמישים\n- **גישה לאורחים**: גישת רשות ציבורית למשתמשים לא מחוברים עם הרשאות ניתנות להתאמה\n- **כלי ניהול** - נהלו קטגוריות, תפקידים, BBCode והגדרות פורום\n- **כלי מודרציה** - נעלו, הדביקו, ונהלו נושאים ותגובות\n\n**מושלם עבור:**\n- דיוני צוותים ושיתוף פעולה\n- פורום קהילתי\n- ערוצי תמיכה\n- ניהול ידע\n- דיוני פרוייקטים\n- תקשורת פנימית\n\nהפורום מתממשק באופן ישיר ל-Nextcloud שלכם, ומשתמש במשתמשים קיימים וקבוצות קיימות לניהול גישה והתחברות.",
|
||||
"Loading …" : "בטעינה…",
|
||||
"Search" : "חיפוש",
|
||||
"Home" : "בית",
|
||||
@@ -48,6 +53,7 @@ OC.L10N.register(
|
||||
"Users" : "משתמשים",
|
||||
"Roles" : "תפקידים",
|
||||
"Categories" : "קטגוריות",
|
||||
"BBCodes" : "BBCodes",
|
||||
"Expand" : "הרחבה",
|
||||
"Collapse" : "קיווץ",
|
||||
"{bStart}Please note:{bEnd} Attached files will be visible to anyone in the forum, regardless of the file's sharing settings." : "{bStart}נא לשים לב:{bEnd} קבצים מצורפים יהיו זמינים לכל מי שנמצא בפורום, ללא התחשבות בהגדרות השיתוף של הקובץ.",
|
||||
@@ -73,8 +79,16 @@ OC.L10N.register(
|
||||
"List" : "רשימה",
|
||||
"List item within a list" : "פריט רשימה בתוך רשימה",
|
||||
"List item within a list (alias)" : "פריט רשימה בתוך רשימה (חלופה)",
|
||||
"BBCode help" : "עזרה עם BBCode",
|
||||
"Built-in BBCodes" : "BBCode מובנים",
|
||||
"These BBCodes are available by default." : "ה-BBCode הללו זמינים כברירת מחדל.",
|
||||
"Custom BBCodes" : "BBCode מותאמים אישית",
|
||||
"These BBCodes are custom to this forum and configured by administrators." : "ה-BBCodes הללו מותאמים לפורום הזה וניתנים להגדרה על ידי צוות הניהול.",
|
||||
"Example" : "דוגמה",
|
||||
"Replacement" : "החלפה",
|
||||
"Loading custom BBCodes …" : "טוען BBCodes מותאמים …",
|
||||
"No custom BBCodes configured." : "אין BBCodes מותאמים מוגדרים.",
|
||||
"Failed to load custom BBCodes" : "כשלון בטעינת BBCodes מותאמים",
|
||||
"Insert emoji" : "הכנסת אימוג'י",
|
||||
"Pick file from Nextcloud" : "בחירת קובץ מ-Nextcloud",
|
||||
"Upload file to Nextcloud" : "העלאת קובץ ל-Nextcloud",
|
||||
@@ -84,6 +98,7 @@ OC.L10N.register(
|
||||
"Pick a file to attach" : "בחירת קובץ לצירוף",
|
||||
"Failed to upload file" : "כשלון בהעלאת קובץ",
|
||||
"Threads" : "שרשורים",
|
||||
"Replies" : "תגובות",
|
||||
"No description available" : "תיאור לא זמין",
|
||||
"Create category header" : "יצירת כותרת קטגוריות",
|
||||
"Edit category header" : "עריכת כותרת קטגוריות",
|
||||
@@ -108,26 +123,129 @@ OC.L10N.register(
|
||||
"The page you are looking for could not be found." : "העמוד שחיפשתם לא נמצא.",
|
||||
"Back" : "אחורה",
|
||||
"Go to home" : "חזרה אל דף הבית",
|
||||
"Pagination" : "דפדוף",
|
||||
"First page" : "דף ראשון",
|
||||
"Previous page" : "דף קודם",
|
||||
"Next page" : "דף הבא",
|
||||
"Last page" : "דף אחרון",
|
||||
"Go to page {page}" : "עבור לדף {page}",
|
||||
"Edited" : "נערך",
|
||||
"Quote reply" : "ציטוט בתגובה",
|
||||
"Edit" : "עריכה",
|
||||
"Delete" : "מחיקה",
|
||||
"View edit history" : "צפייה בהיסטוריית עריכה",
|
||||
"Are you sure you want to delete this post? This action cannot be undone." : "האם אתם בטוחים שתרצו למחוק את הפוסט הזה? הפעולה בלתי ניתנת להפיכה.",
|
||||
"Unread" : "לא נקרא",
|
||||
"Edit your reply …" : "ערכו את תגובתכם …",
|
||||
"Save" : "שמירה",
|
||||
"Are you sure you want to discard your changes?" : "האם אתם בטוחים שתרצו לבטל את השינויים שלכם?",
|
||||
"Edit history" : "היסטוריית עריכה",
|
||||
"Loading history …" : "טוען היסטוריה …",
|
||||
"This post has no edit history." : "לפוסט הזה אין היסטוריית עריכה.",
|
||||
"Current version" : "גרסה נוכחית",
|
||||
"Edited by" : "נערך על ידי",
|
||||
"Failed to load edit history" : "כשלון בטעינת היסטוריית עריכה",
|
||||
"Version {index}" : "גרסה {index}",
|
||||
"Add reaction" : "הוספת תגובון",
|
||||
"React with {emoji}" : "הגיבו עם {emoji}",
|
||||
"You reacted with {emoji}" : "הגבת עם {emoji}",
|
||||
"_You and %n other reacted with {emoji}_::_You and %n others reacted with {emoji}_" : ["את\\ה ועוד %n אחר הגבתם עם {emoji}","את\\ה ועוד %n אחרים הגבתם עם {emoji}","את\\ה ועוד %n אחרים הגבתם עם {emoji}"],
|
||||
"_%n person reacted with {emoji}_::_%n people reacted with {emoji}_" : ["אדם אחד הגיב עם {emoji}","%n אנשים הגיבו עם {emoji}","%n אנשים הגיבו עם {emoji}"],
|
||||
"Write your reply …" : "כתבו את תגובתכם …",
|
||||
"Submit reply" : "שליחת תגובה",
|
||||
"Are you sure you want to discard your reply?" : "אתם בטוחים שברצונכם לבטל את התגובה?",
|
||||
"In thread" : "בנושא",
|
||||
"Thread unavailable" : "נושא לא זמין",
|
||||
"Pinned thread" : "נושא מודבק",
|
||||
"Locked thread" : "נושא נעול",
|
||||
"Uncategorized" : "ללא קטגוריה",
|
||||
"_%n reply_::_%n replies_" : ["תגובה אחת","%n תגובות","%n תגובות"],
|
||||
"_%n view_::_%n views_" : ["צפייה אחת","%n צפיות","%n צפיות"],
|
||||
"Views" : "תצוגות",
|
||||
"Title" : "כותרת",
|
||||
"Enter thread title …" : "כתבו כותרת לנושא …",
|
||||
"Write your thread content …" : "כתבו תוכן לנושא שלכם …",
|
||||
"Create thread" : "צרו נושא",
|
||||
"Are you sure you want to discard this thread?" : "אתם בטוחים שברצונכם לבטל את הנושא?",
|
||||
"Saving draft …" : "הטיוטה נשמרת…",
|
||||
"Draft saved" : "הטיוטה נשמרה",
|
||||
"Unsaved changes" : "שינויים שלא נשמרו",
|
||||
"Back to home" : "חזרה לדף בית",
|
||||
"Refresh" : "רענון",
|
||||
"Your bookmarked threads" : "הנושאים בסימניה שלכם",
|
||||
"Error loading bookmarks" : "שגיאה בטעינת סימניות",
|
||||
"No bookmarks yet" : "ללא סימניות עדיין",
|
||||
"Bookmark threads to quickly find them later." : "סמנו נושאים כדי למצוא אותם מהר מאוחר יותר",
|
||||
"Retry" : "ניסיון חוזר",
|
||||
"An unexpected error occurred" : "קרתה שגיאה בלתי צפוייה",
|
||||
"Failed to load bookmarks" : "כשלון בטעינת סימניות",
|
||||
"No categories yet" : "ללא קטגוריות עדיין",
|
||||
"Categories will appear here once they are created." : "קטגוריות יופיעו כאן ברגע שייוצרו",
|
||||
"No categories in this section" : "אין קטגוריות באיזור הזה",
|
||||
"Category not found" : "קטגוריה לא נמצאה",
|
||||
"The category you are looking for does not exist or has been removed." : "הקטגוריה שאתם מחפשים לא קיימת או הוסרה.",
|
||||
"Back to categories" : "חזרה אל קטגוריות",
|
||||
"New thread" : "נושא חדש",
|
||||
"Error loading category" : "שגיאה בטעינת קטגוריות",
|
||||
"No threads yet" : "ללא נושאים עדיין",
|
||||
"Be the first to start a discussion in this category." : "היו הראשונים שיוצרים דיון בקטגוריה זאת.",
|
||||
"No category ID or slug provided" : "מזהה קטגוריה לא סופק ",
|
||||
"Failed to load threads" : "שגיאה בטעינת נושאים",
|
||||
"Create New Thread" : "צרו נושא חדש",
|
||||
"In {category}" : "בתוך {category}",
|
||||
"Creating thread …" : "יוצר נושא …",
|
||||
"Thread created" : "נושא נוצר",
|
||||
"Failed to create thread" : "כשלון ביצירת נושא",
|
||||
"No category specified" : "קטגוריה לא סופקה",
|
||||
"Error" : "שגיאה",
|
||||
"First activity" : "פעילות ראשונה",
|
||||
"Threads ({count})" : "נושאים ({count})",
|
||||
"Replies ({count})" : "תגובות ({count})",
|
||||
"No threads" : "ללא נושאים",
|
||||
"This user has not created any threads yet" : "משתמש זה לא יצר נושאים עדיין",
|
||||
"No replies" : "ללא תגובות",
|
||||
"This user has not written any replies yet" : "משתמש זה לא כתב תגובות עדיין",
|
||||
"Failed to load user profile" : "כשלון בטעינת פרופיל משתמש",
|
||||
"Enter search query …" : "הכניסו שאילתת חיפוש …",
|
||||
"Search in threads" : "חפשו בנושאים",
|
||||
"Search in replies" : "חפשו בתגובות",
|
||||
"Syntax help" : "עזרה בתחביר",
|
||||
"Search syntax" : "תחביר חיפוש",
|
||||
"Match exact phrase" : "התאם מלל מדוייק",
|
||||
"Both terms required" : "שני הערכים הם חובה",
|
||||
"Either term matches" : "ערך אחד או אחר מתאים",
|
||||
"Group conditions with parentheses" : "קבצו תנאים בעזרת סוגריים",
|
||||
"Exclude term from results" : "השמיטו מתוצאות החיפוש",
|
||||
"Searching …" : "מתבצע חיפוש…",
|
||||
"Search Error" : "שגיאה בחיפוש",
|
||||
"Enter a search query" : "הכניסו שאילתת חיפוש",
|
||||
"Use the search box above to find threads and replies" : "השתמשו בתיבת החיפוש למעלה כדי למצוא נושאים ותגובות",
|
||||
"No results found" : "לא נמצאו תוצאות",
|
||||
"Try different keywords or check your syntax" : "נסו מילות מפתח אחרות או בדקו שוב את תחביר החיפוש שלכם",
|
||||
"_%n thread found_::_%n threads found_" : ["נמצא נושא אחד","נמצאו %n נושאים","נמצאו %n נושאים"],
|
||||
"_%n reply found_::_%n replies found_" : ["נמצאה תגובה אחת","נמצאו %n תגובות","נמצאו %n תגובות"],
|
||||
"Please enter a search query" : "נא להכניס שאילתת חיפוש",
|
||||
"Please select at least one search scope" : "נא בחרו לפחות איזור חיפוש אחד",
|
||||
"Failed to search" : "כשלון בחיפוש",
|
||||
"Thread not found" : "נושא לא נמצא",
|
||||
"The thread you are looking for does not exist or has been removed." : "הנושא שאתם מנסים לפתוח לא קיים או הוסר",
|
||||
"Back to {category}" : "חזרה אל {category}",
|
||||
"Reply" : "תגובה",
|
||||
"Error loading thread" : "שגיאה בטעינת נושא",
|
||||
"No replies yet" : "ללא תגובות עדיין",
|
||||
"Be the first to reply in this thread." : "היו הראשונים שיגיבו לנושא זה",
|
||||
"by" : "מאת",
|
||||
"This thread is locked. Only moderators can add replies." : "הנושא הזה נעול. רק מנהלים יכולים להוסיף תגובות.",
|
||||
"You must be signed in to reply to this thread." : "אתם חייבים להיות מחוברים כדי להגיב לנושא זה.",
|
||||
"Sign in to reply" : "התחברו כדי להגיב",
|
||||
"Lock thread" : "נעילת נושא",
|
||||
"Unlock thread" : "ביטול נעילת נושא",
|
||||
"Pin thread" : "הדבקת נושא",
|
||||
"Unpin thread" : "ביטול הדבקת נושא",
|
||||
"Thread locked" : "הנושא ננעל",
|
||||
"Thread unlocked" : "בוטלה נעילת הנושא",
|
||||
"Thread pinned" : "הנושא הודבק",
|
||||
"Thread unpinned" : "בוטלה הדבקת הנושא",
|
||||
"Subscribe" : "הרשמה",
|
||||
"Bookmark" : "סימנייה",
|
||||
"Edit title" : "עריכת כותרת",
|
||||
|
||||
128
l10n/he.json
128
l10n/he.json
@@ -1,12 +1,12 @@
|
||||
{ "translations": {
|
||||
"Admin" : "מנהל",
|
||||
"Administrator role with full permissions" : "תפקיד מנהל עם הרשאות מלאות",
|
||||
"Admin" : "אדמין",
|
||||
"Administrator role with full permissions" : "תפקיד אדמין עם הרשאות מלאות",
|
||||
"Moderator" : "מפקח",
|
||||
"Moderator role with elevated permissions" : "תפקיד מפקח עם הרשאות מוגבהות",
|
||||
"User" : "משתמש",
|
||||
"Default user role with basic permissions" : "תפקיד משתמש רגיל עם הרשאות בסיסיות",
|
||||
"Default user role with basic permissions" : "תפקיד משתמש ברירת מחדל עם הרשאות בסיסיות",
|
||||
"Guest" : "אורח",
|
||||
"Guest role for unauthenticated users with read-only access" : "תפקיד אורח עם הרשאות גישה לקריאה בלבד",
|
||||
"Guest role for unauthenticated users with read-only access" : "תפקיד אורח למשתמשים לא-מחוברים עם הרשאות גישה לקריאה בלבד",
|
||||
"General" : "כללי",
|
||||
"General discussion categories" : "קטגוריות לדיונים כלליים",
|
||||
"General discussions" : "דיונים כלליים",
|
||||
@@ -18,24 +18,29 @@
|
||||
"Hidden content" : "תוכן מוסתר",
|
||||
"Spoilers" : "ספוילרים",
|
||||
"Attachment" : "קובץ מצורף",
|
||||
"Welcome to Nextcloud Forums" : "ברוכים הבאים לפורום Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "ברוכים הבאים לפורום של Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "זהו פורום מונחה-קהילה מובנה ישירות ל-Nextcloud שלך. כאן ניתן להתדיין בנושאים שונים, לחלוק רעיונות ולשתף פעולה עם משתמשים אחרים.",
|
||||
"Features:" : "יכולות:",
|
||||
"Create and reply to threads" : "יצירה והגבה לשרשורים",
|
||||
"Organize discussions by categories" : "אירגון דיונים על-פי קטגוריות",
|
||||
"Use BBCode for rich text formatting" : "השתמשו ב-BBCode לפורמט טקסט עשיר",
|
||||
"Attach files from your Nextcloud storage" : "צירוף קבצים מאחסון ה-Nextcloud שלך",
|
||||
"React to posts" : "תגובות לשרשורים",
|
||||
"Track read/unread threads" : "מעקב אחר שרשורים שנקראו\\לא נקראו",
|
||||
"BBCode examples:" : "דוגמאות BBCode:",
|
||||
"Bold text" : "טקסט בולט",
|
||||
"Use %1$stext%2$s" : "השתמשו ב%1$sטקסט%2$s",
|
||||
"Italic text" : "טקסט מוטה",
|
||||
"Underlined text" : "טקסט בקו תחתון",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "הרגישו חופשי להתחיל שרשור חדש או להגיב לשרשורים קיימים. כתיבה מהנה!",
|
||||
"Welcome to Nextcloud Forums" : "ברוכים הבאים לפורום Nextcloud",
|
||||
"Forum" : "פורום",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["תגובה {count} חדשה ב-{thread}","{count} תגובות חדשות ב-{thread}","{count} תגובות חדשות ב-{thread}"],
|
||||
"{user} mentioned you in {thread}" : "אוזכרת על ידי {user}בנושא {thread}",
|
||||
"Welcome to the forum!" : "ברוכים הבאים לפורום!",
|
||||
"Deleted user" : "משתמש מחוק",
|
||||
"A community-driven forum built right into your Nextcloud instance" : "פורום מונחה-קהילה מובנה ישירות להתקנת ה Nextcloud שלכם.",
|
||||
"Create discussions, share ideas and collaborate with your community directly in Nextcloud.\n\n**⚠️ Early Development Notice:**\nThis app is in early stages of development. While functional, you may encounter bugs or incomplete features. Please report any issues on GitHub and consider backing up your data regularly.\n\n**Key features:**\n- **Thread-based Discussions** - Create and reply to organized discussion threads\n- **Category Organization** - Structure your forum with customizable categories and headers\n- **Rich Text Formatting** - Use BBCode for formatting posts with bold, italic, links, images, code blocks and more\n- **File Attachments** - Attach files from your Nextcloud storage to posts\n- **Post Reactions** - React to posts with emoji reactions\n- **Read/Unread Tracking** - Keep track of which threads you've read\n- **Search** - Find discussions quickly with built-in search\n- **User Profiles** - View user post history and statistics\n- **Role-Based Permissions** - Control access and moderation with flexible roles\n- **Guest Access**: Optional public access for unauthenticated users with configurable permissions\n- **Admin Tools** - Manage categories, roles, BBCodes and forum settings\n- **Moderation Tools** - Pin, lock and manage threads and posts\n\n**Perfect for:**\n- Team discussions and collaboration\n- Community forums\n- Support channels\n- Knowledge bases\n- Project discussions\n- Internal communication\n\nThe forum integrates seamlessly with your Nextcloud instance, using your existing users and groups for authentication and access control." : "צרו דיונים, חלקו רעיונות ושתפו פעולה עם משתמשים ישירות ב-Nextcloud.\n\n**⚠️ הודעה לגבי שלב פיתוח מוקדם:**\nהיישום הזה נמצא בשלבי פיתוח מוקדמים. בעוד היישום שמיש, אתם עלולים להיתקל בבאגים או יכולות לא שלמות. נא לדווח בעיות ב-GitHub ולשקול לגבות את המידע שלכם באופן תכוף.\n\n**יכולות מפתח:**\n- **דיונים מונחי נושאים** - צרו והגיבו לנושאי דיון\n- **ארגון קטגוריות** - צרו מבנה לפורום שלכם עם קטגוריות וכותרות הניתנות להתאמה\n- **פורמט טקסט עשיר** - השתמשו ב-BBCode לפרמוט של פוסטים עם הדגשות, הטיות, קישורים, תמונות, קטעי קוד ועוד\n- **צירוף קבצים** - צרפו קבצים מתוך ה-Nextcloud שלכם לפוסטים\n- **תגובונים לפוסטים** - הגיבו לפוסטים עם תגובוני אמוג'י\n- **מעקב נקרא\\לא נקרא** - עקבו אחרי סטטוס הקריאה של פוסטים\n- **חיפוש** - מצאו דיונים מהר בעזרת חיפוש מובנה\n- **פרופילי משתמשים** - צפו בהיסטוריה וסטטיסטיקות של משתמשים\n- **הרשאות מבוססי תפקיד** - שלטו בגישה ומודרציה עם תפקידים גמישים\n- **גישה לאורחים**: גישת רשות ציבורית למשתמשים לא מחוברים עם הרשאות ניתנות להתאמה\n- **כלי ניהול** - נהלו קטגוריות, תפקידים, BBCode והגדרות פורום\n- **כלי מודרציה** - נעלו, הדביקו, ונהלו נושאים ותגובות\n\n**מושלם עבור:**\n- דיוני צוותים ושיתוף פעולה\n- פורום קהילתי\n- ערוצי תמיכה\n- ניהול ידע\n- דיוני פרוייקטים\n- תקשורת פנימית\n\nהפורום מתממשק באופן ישיר ל-Nextcloud שלכם, ומשתמש במשתמשים קיימים וקבוצות קיימות לניהול גישה והתחברות.",
|
||||
"Loading …" : "בטעינה…",
|
||||
"Search" : "חיפוש",
|
||||
"Home" : "בית",
|
||||
@@ -46,6 +51,7 @@
|
||||
"Users" : "משתמשים",
|
||||
"Roles" : "תפקידים",
|
||||
"Categories" : "קטגוריות",
|
||||
"BBCodes" : "BBCodes",
|
||||
"Expand" : "הרחבה",
|
||||
"Collapse" : "קיווץ",
|
||||
"{bStart}Please note:{bEnd} Attached files will be visible to anyone in the forum, regardless of the file's sharing settings." : "{bStart}נא לשים לב:{bEnd} קבצים מצורפים יהיו זמינים לכל מי שנמצא בפורום, ללא התחשבות בהגדרות השיתוף של הקובץ.",
|
||||
@@ -71,8 +77,16 @@
|
||||
"List" : "רשימה",
|
||||
"List item within a list" : "פריט רשימה בתוך רשימה",
|
||||
"List item within a list (alias)" : "פריט רשימה בתוך רשימה (חלופה)",
|
||||
"BBCode help" : "עזרה עם BBCode",
|
||||
"Built-in BBCodes" : "BBCode מובנים",
|
||||
"These BBCodes are available by default." : "ה-BBCode הללו זמינים כברירת מחדל.",
|
||||
"Custom BBCodes" : "BBCode מותאמים אישית",
|
||||
"These BBCodes are custom to this forum and configured by administrators." : "ה-BBCodes הללו מותאמים לפורום הזה וניתנים להגדרה על ידי צוות הניהול.",
|
||||
"Example" : "דוגמה",
|
||||
"Replacement" : "החלפה",
|
||||
"Loading custom BBCodes …" : "טוען BBCodes מותאמים …",
|
||||
"No custom BBCodes configured." : "אין BBCodes מותאמים מוגדרים.",
|
||||
"Failed to load custom BBCodes" : "כשלון בטעינת BBCodes מותאמים",
|
||||
"Insert emoji" : "הכנסת אימוג'י",
|
||||
"Pick file from Nextcloud" : "בחירת קובץ מ-Nextcloud",
|
||||
"Upload file to Nextcloud" : "העלאת קובץ ל-Nextcloud",
|
||||
@@ -82,6 +96,7 @@
|
||||
"Pick a file to attach" : "בחירת קובץ לצירוף",
|
||||
"Failed to upload file" : "כשלון בהעלאת קובץ",
|
||||
"Threads" : "שרשורים",
|
||||
"Replies" : "תגובות",
|
||||
"No description available" : "תיאור לא זמין",
|
||||
"Create category header" : "יצירת כותרת קטגוריות",
|
||||
"Edit category header" : "עריכת כותרת קטגוריות",
|
||||
@@ -106,26 +121,129 @@
|
||||
"The page you are looking for could not be found." : "העמוד שחיפשתם לא נמצא.",
|
||||
"Back" : "אחורה",
|
||||
"Go to home" : "חזרה אל דף הבית",
|
||||
"Pagination" : "דפדוף",
|
||||
"First page" : "דף ראשון",
|
||||
"Previous page" : "דף קודם",
|
||||
"Next page" : "דף הבא",
|
||||
"Last page" : "דף אחרון",
|
||||
"Go to page {page}" : "עבור לדף {page}",
|
||||
"Edited" : "נערך",
|
||||
"Quote reply" : "ציטוט בתגובה",
|
||||
"Edit" : "עריכה",
|
||||
"Delete" : "מחיקה",
|
||||
"View edit history" : "צפייה בהיסטוריית עריכה",
|
||||
"Are you sure you want to delete this post? This action cannot be undone." : "האם אתם בטוחים שתרצו למחוק את הפוסט הזה? הפעולה בלתי ניתנת להפיכה.",
|
||||
"Unread" : "לא נקרא",
|
||||
"Edit your reply …" : "ערכו את תגובתכם …",
|
||||
"Save" : "שמירה",
|
||||
"Are you sure you want to discard your changes?" : "האם אתם בטוחים שתרצו לבטל את השינויים שלכם?",
|
||||
"Edit history" : "היסטוריית עריכה",
|
||||
"Loading history …" : "טוען היסטוריה …",
|
||||
"This post has no edit history." : "לפוסט הזה אין היסטוריית עריכה.",
|
||||
"Current version" : "גרסה נוכחית",
|
||||
"Edited by" : "נערך על ידי",
|
||||
"Failed to load edit history" : "כשלון בטעינת היסטוריית עריכה",
|
||||
"Version {index}" : "גרסה {index}",
|
||||
"Add reaction" : "הוספת תגובון",
|
||||
"React with {emoji}" : "הגיבו עם {emoji}",
|
||||
"You reacted with {emoji}" : "הגבת עם {emoji}",
|
||||
"_You and %n other reacted with {emoji}_::_You and %n others reacted with {emoji}_" : ["את\\ה ועוד %n אחר הגבתם עם {emoji}","את\\ה ועוד %n אחרים הגבתם עם {emoji}","את\\ה ועוד %n אחרים הגבתם עם {emoji}"],
|
||||
"_%n person reacted with {emoji}_::_%n people reacted with {emoji}_" : ["אדם אחד הגיב עם {emoji}","%n אנשים הגיבו עם {emoji}","%n אנשים הגיבו עם {emoji}"],
|
||||
"Write your reply …" : "כתבו את תגובתכם …",
|
||||
"Submit reply" : "שליחת תגובה",
|
||||
"Are you sure you want to discard your reply?" : "אתם בטוחים שברצונכם לבטל את התגובה?",
|
||||
"In thread" : "בנושא",
|
||||
"Thread unavailable" : "נושא לא זמין",
|
||||
"Pinned thread" : "נושא מודבק",
|
||||
"Locked thread" : "נושא נעול",
|
||||
"Uncategorized" : "ללא קטגוריה",
|
||||
"_%n reply_::_%n replies_" : ["תגובה אחת","%n תגובות","%n תגובות"],
|
||||
"_%n view_::_%n views_" : ["צפייה אחת","%n צפיות","%n צפיות"],
|
||||
"Views" : "תצוגות",
|
||||
"Title" : "כותרת",
|
||||
"Enter thread title …" : "כתבו כותרת לנושא …",
|
||||
"Write your thread content …" : "כתבו תוכן לנושא שלכם …",
|
||||
"Create thread" : "צרו נושא",
|
||||
"Are you sure you want to discard this thread?" : "אתם בטוחים שברצונכם לבטל את הנושא?",
|
||||
"Saving draft …" : "הטיוטה נשמרת…",
|
||||
"Draft saved" : "הטיוטה נשמרה",
|
||||
"Unsaved changes" : "שינויים שלא נשמרו",
|
||||
"Back to home" : "חזרה לדף בית",
|
||||
"Refresh" : "רענון",
|
||||
"Your bookmarked threads" : "הנושאים בסימניה שלכם",
|
||||
"Error loading bookmarks" : "שגיאה בטעינת סימניות",
|
||||
"No bookmarks yet" : "ללא סימניות עדיין",
|
||||
"Bookmark threads to quickly find them later." : "סמנו נושאים כדי למצוא אותם מהר מאוחר יותר",
|
||||
"Retry" : "ניסיון חוזר",
|
||||
"An unexpected error occurred" : "קרתה שגיאה בלתי צפוייה",
|
||||
"Failed to load bookmarks" : "כשלון בטעינת סימניות",
|
||||
"No categories yet" : "ללא קטגוריות עדיין",
|
||||
"Categories will appear here once they are created." : "קטגוריות יופיעו כאן ברגע שייוצרו",
|
||||
"No categories in this section" : "אין קטגוריות באיזור הזה",
|
||||
"Category not found" : "קטגוריה לא נמצאה",
|
||||
"The category you are looking for does not exist or has been removed." : "הקטגוריה שאתם מחפשים לא קיימת או הוסרה.",
|
||||
"Back to categories" : "חזרה אל קטגוריות",
|
||||
"New thread" : "נושא חדש",
|
||||
"Error loading category" : "שגיאה בטעינת קטגוריות",
|
||||
"No threads yet" : "ללא נושאים עדיין",
|
||||
"Be the first to start a discussion in this category." : "היו הראשונים שיוצרים דיון בקטגוריה זאת.",
|
||||
"No category ID or slug provided" : "מזהה קטגוריה לא סופק ",
|
||||
"Failed to load threads" : "שגיאה בטעינת נושאים",
|
||||
"Create New Thread" : "צרו נושא חדש",
|
||||
"In {category}" : "בתוך {category}",
|
||||
"Creating thread …" : "יוצר נושא …",
|
||||
"Thread created" : "נושא נוצר",
|
||||
"Failed to create thread" : "כשלון ביצירת נושא",
|
||||
"No category specified" : "קטגוריה לא סופקה",
|
||||
"Error" : "שגיאה",
|
||||
"First activity" : "פעילות ראשונה",
|
||||
"Threads ({count})" : "נושאים ({count})",
|
||||
"Replies ({count})" : "תגובות ({count})",
|
||||
"No threads" : "ללא נושאים",
|
||||
"This user has not created any threads yet" : "משתמש זה לא יצר נושאים עדיין",
|
||||
"No replies" : "ללא תגובות",
|
||||
"This user has not written any replies yet" : "משתמש זה לא כתב תגובות עדיין",
|
||||
"Failed to load user profile" : "כשלון בטעינת פרופיל משתמש",
|
||||
"Enter search query …" : "הכניסו שאילתת חיפוש …",
|
||||
"Search in threads" : "חפשו בנושאים",
|
||||
"Search in replies" : "חפשו בתגובות",
|
||||
"Syntax help" : "עזרה בתחביר",
|
||||
"Search syntax" : "תחביר חיפוש",
|
||||
"Match exact phrase" : "התאם מלל מדוייק",
|
||||
"Both terms required" : "שני הערכים הם חובה",
|
||||
"Either term matches" : "ערך אחד או אחר מתאים",
|
||||
"Group conditions with parentheses" : "קבצו תנאים בעזרת סוגריים",
|
||||
"Exclude term from results" : "השמיטו מתוצאות החיפוש",
|
||||
"Searching …" : "מתבצע חיפוש…",
|
||||
"Search Error" : "שגיאה בחיפוש",
|
||||
"Enter a search query" : "הכניסו שאילתת חיפוש",
|
||||
"Use the search box above to find threads and replies" : "השתמשו בתיבת החיפוש למעלה כדי למצוא נושאים ותגובות",
|
||||
"No results found" : "לא נמצאו תוצאות",
|
||||
"Try different keywords or check your syntax" : "נסו מילות מפתח אחרות או בדקו שוב את תחביר החיפוש שלכם",
|
||||
"_%n thread found_::_%n threads found_" : ["נמצא נושא אחד","נמצאו %n נושאים","נמצאו %n נושאים"],
|
||||
"_%n reply found_::_%n replies found_" : ["נמצאה תגובה אחת","נמצאו %n תגובות","נמצאו %n תגובות"],
|
||||
"Please enter a search query" : "נא להכניס שאילתת חיפוש",
|
||||
"Please select at least one search scope" : "נא בחרו לפחות איזור חיפוש אחד",
|
||||
"Failed to search" : "כשלון בחיפוש",
|
||||
"Thread not found" : "נושא לא נמצא",
|
||||
"The thread you are looking for does not exist or has been removed." : "הנושא שאתם מנסים לפתוח לא קיים או הוסר",
|
||||
"Back to {category}" : "חזרה אל {category}",
|
||||
"Reply" : "תגובה",
|
||||
"Error loading thread" : "שגיאה בטעינת נושא",
|
||||
"No replies yet" : "ללא תגובות עדיין",
|
||||
"Be the first to reply in this thread." : "היו הראשונים שיגיבו לנושא זה",
|
||||
"by" : "מאת",
|
||||
"This thread is locked. Only moderators can add replies." : "הנושא הזה נעול. רק מנהלים יכולים להוסיף תגובות.",
|
||||
"You must be signed in to reply to this thread." : "אתם חייבים להיות מחוברים כדי להגיב לנושא זה.",
|
||||
"Sign in to reply" : "התחברו כדי להגיב",
|
||||
"Lock thread" : "נעילת נושא",
|
||||
"Unlock thread" : "ביטול נעילת נושא",
|
||||
"Pin thread" : "הדבקת נושא",
|
||||
"Unpin thread" : "ביטול הדבקת נושא",
|
||||
"Thread locked" : "הנושא ננעל",
|
||||
"Thread unlocked" : "בוטלה נעילת הנושא",
|
||||
"Thread pinned" : "הנושא הודבק",
|
||||
"Thread unpinned" : "בוטלה הדבקת הנושא",
|
||||
"Subscribe" : "הרשמה",
|
||||
"Bookmark" : "סימנייה",
|
||||
"Edit title" : "עריכת כותרת",
|
||||
|
||||
@@ -2,6 +2,7 @@ OC.L10N.register(
|
||||
"forum",
|
||||
{
|
||||
"Admin" : "Admin",
|
||||
"Moderator" : "Moderator",
|
||||
"User" : "Pengguna",
|
||||
"Guest" : "Tamu",
|
||||
"General" : "Umum",
|
||||
@@ -27,6 +28,7 @@ OC.L10N.register(
|
||||
"Update" : "Perbarui",
|
||||
"Move" : "Pindah",
|
||||
"Page not found" : "Halaman tidak ditemukan",
|
||||
"Back" : "Kembali",
|
||||
"Edit" : "Sunting",
|
||||
"Delete" : "Hapus",
|
||||
"Save" : "Simpan",
|
||||
@@ -36,12 +38,14 @@ OC.L10N.register(
|
||||
"Refresh" : "Muat ulang",
|
||||
"Retry" : "Ulangi",
|
||||
"Error" : "Galat",
|
||||
"Searching …" : "Mencari …",
|
||||
"Back to {category}" : "Kembali ke {category}",
|
||||
"by" : "oleh",
|
||||
"Subscribe" : "Berlangganan",
|
||||
"Edit title" : "Edit judul",
|
||||
"Preferences" : "Preferensi",
|
||||
"Notifications" : "Notifikasi",
|
||||
"Files" : "File",
|
||||
"Signature" : "Tanda tangan",
|
||||
"Enable" : "Aktifkan",
|
||||
"Disable" : "Nonaktifkan",
|
||||
@@ -49,6 +53,7 @@ OC.L10N.register(
|
||||
"Enabled" : "Diaktifkan",
|
||||
"Name" : "Nama",
|
||||
"New" : "Baru",
|
||||
"Last 7 days" : "7 hari terakhir",
|
||||
"Appearance" : "Tampilan",
|
||||
"Access control" : "Kontol akses",
|
||||
"Settings saved" : "Setelan tersimpan",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{ "translations": {
|
||||
"Admin" : "Admin",
|
||||
"Moderator" : "Moderator",
|
||||
"User" : "Pengguna",
|
||||
"Guest" : "Tamu",
|
||||
"General" : "Umum",
|
||||
@@ -25,6 +26,7 @@
|
||||
"Update" : "Perbarui",
|
||||
"Move" : "Pindah",
|
||||
"Page not found" : "Halaman tidak ditemukan",
|
||||
"Back" : "Kembali",
|
||||
"Edit" : "Sunting",
|
||||
"Delete" : "Hapus",
|
||||
"Save" : "Simpan",
|
||||
@@ -34,12 +36,14 @@
|
||||
"Refresh" : "Muat ulang",
|
||||
"Retry" : "Ulangi",
|
||||
"Error" : "Galat",
|
||||
"Searching …" : "Mencari …",
|
||||
"Back to {category}" : "Kembali ke {category}",
|
||||
"by" : "oleh",
|
||||
"Subscribe" : "Berlangganan",
|
||||
"Edit title" : "Edit judul",
|
||||
"Preferences" : "Preferensi",
|
||||
"Notifications" : "Notifikasi",
|
||||
"Files" : "File",
|
||||
"Signature" : "Tanda tangan",
|
||||
"Enable" : "Aktifkan",
|
||||
"Disable" : "Nonaktifkan",
|
||||
@@ -47,6 +51,7 @@
|
||||
"Enabled" : "Diaktifkan",
|
||||
"Name" : "Nama",
|
||||
"New" : "Baru",
|
||||
"Last 7 days" : "7 hari terakhir",
|
||||
"Appearance" : "Tampilan",
|
||||
"Access control" : "Kontol akses",
|
||||
"Settings saved" : "Setelan tersimpan",
|
||||
|
||||
@@ -19,6 +19,7 @@ OC.L10N.register(
|
||||
"Collapse" : "접기",
|
||||
"Hello world!" : "Hello world!",
|
||||
"Code" : "코드",
|
||||
"Quote" : "ㅇ",
|
||||
"Font size" : "글꼴 크기",
|
||||
"List" : "목록",
|
||||
"Insert emoji" : "이모지 삽입",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"Collapse" : "접기",
|
||||
"Hello world!" : "Hello world!",
|
||||
"Code" : "코드",
|
||||
"Quote" : "ㅇ",
|
||||
"Font size" : "글꼴 크기",
|
||||
"List" : "목록",
|
||||
"Insert emoji" : "이모지 삽입",
|
||||
|
||||
@@ -16,6 +16,7 @@ OC.L10N.register(
|
||||
"Dashboard" : "Skydelis",
|
||||
"Users" : "Naudotojai",
|
||||
"Categories" : "Kategorijos",
|
||||
"Expand" : "Išskleisti",
|
||||
"Collapse" : "Suskleisti",
|
||||
"Hello world!" : "Sveikas, pasauli!",
|
||||
"Code" : "Kodas",
|
||||
@@ -56,6 +57,7 @@ OC.L10N.register(
|
||||
"Edit title" : "Taisyti pavadinimą",
|
||||
"Move thread" : "Perkelti giją",
|
||||
"Preferences" : "Nuostatos",
|
||||
"Notifications" : "Pranešimai",
|
||||
"Files" : "Failai",
|
||||
"Browse" : "Naršyti",
|
||||
"Signature" : "Parašas",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Dashboard" : "Skydelis",
|
||||
"Users" : "Naudotojai",
|
||||
"Categories" : "Kategorijos",
|
||||
"Expand" : "Išskleisti",
|
||||
"Collapse" : "Suskleisti",
|
||||
"Hello world!" : "Sveikas, pasauli!",
|
||||
"Code" : "Kodas",
|
||||
@@ -54,6 +55,7 @@
|
||||
"Edit title" : "Taisyti pavadinimą",
|
||||
"Move thread" : "Perkelti giją",
|
||||
"Preferences" : "Nuostatos",
|
||||
"Notifications" : "Pranešimai",
|
||||
"Files" : "Failai",
|
||||
"Browse" : "Naršyti",
|
||||
"Signature" : "Parašas",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Conteúdo oculto",
|
||||
"Spoilers" : "Spoilers",
|
||||
"Attachment" : "Anexo",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo ao Nextcloud Fórums",
|
||||
"Welcome to the Nextcloud Forums!" : "Bem-vindo ao Nextcloud Fórums!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Este é um fórum comunitário integrado à sua instância Nextcloud. Aqui você pode discutir tópicos, compartilhar ideias e colaborar com outros usuários.",
|
||||
"Features:" : "Características:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Texto em itálico",
|
||||
"Underlined text" : "Texto sublinhado",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Sinta-se à vontade para iniciar uma nova discussão ou responder a fios existentes. Boas postagens!",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo ao Nextcloud Fórums",
|
||||
"Forum" : "Fórum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nova resposta em {thread}","{count} de novas respostas em {thread}","{count} novas respostas em {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mencionou você em {thread}",
|
||||
@@ -282,6 +282,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "Configure como você recebe notificações",
|
||||
"Auto-subscribe to threads I create" : "Inscreva-me automaticamente nos fios que eu criar",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Quando ativado, você receberá automaticamente notificações sobre respostas aos fios que criar",
|
||||
"Auto-subscribe to threads I reply to" : "Inscrever-se automaticamente nos fios aos quais respondo",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Quando ativado, você receberá automaticamente notificações sobre novas respostas nos fios em que você respondeu.",
|
||||
"Files" : "Arquivos",
|
||||
"Configure file upload settings" : "Configurar as definições de envio de arquivos",
|
||||
"Upload directory" : "Diretório de upload",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Conteúdo oculto",
|
||||
"Spoilers" : "Spoilers",
|
||||
"Attachment" : "Anexo",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo ao Nextcloud Fórums",
|
||||
"Welcome to the Nextcloud Forums!" : "Bem-vindo ao Nextcloud Fórums!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Este é um fórum comunitário integrado à sua instância Nextcloud. Aqui você pode discutir tópicos, compartilhar ideias e colaborar com outros usuários.",
|
||||
"Features:" : "Características:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Texto em itálico",
|
||||
"Underlined text" : "Texto sublinhado",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Sinta-se à vontade para iniciar uma nova discussão ou responder a fios existentes. Boas postagens!",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo ao Nextcloud Fórums",
|
||||
"Forum" : "Fórum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} nova resposta em {thread}","{count} de novas respostas em {thread}","{count} novas respostas em {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} mencionou você em {thread}",
|
||||
@@ -280,6 +280,8 @@
|
||||
"Configure how you receive notifications" : "Configure como você recebe notificações",
|
||||
"Auto-subscribe to threads I create" : "Inscreva-me automaticamente nos fios que eu criar",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Quando ativado, você receberá automaticamente notificações sobre respostas aos fios que criar",
|
||||
"Auto-subscribe to threads I reply to" : "Inscrever-se automaticamente nos fios aos quais respondo",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Quando ativado, você receberá automaticamente notificações sobre novas respostas nos fios em que você respondeu.",
|
||||
"Files" : "Arquivos",
|
||||
"Configure file upload settings" : "Configurar as definições de envio de arquivos",
|
||||
"Upload directory" : "Diretório de upload",
|
||||
|
||||
@@ -20,10 +20,10 @@ OC.L10N.register(
|
||||
"Hidden content" : "Conteúdo oculto",
|
||||
"Spoilers" : "Contém spoilers",
|
||||
"Attachment" : "Anexo",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo aos Fóruns Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Bem-vindo aos Fóruns do Nextcloud!",
|
||||
"Bold text" : "Texto a negrito",
|
||||
"Underlined text" : "Texto sublinhado",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo aos Fóruns Nextcloud",
|
||||
"Forum" : "Fórum",
|
||||
"Create discussions, share ideas and collaborate with your community directly in Nextcloud.\n\n**⚠️ Early Development Notice:**\nThis app is in early stages of development. While functional, you may encounter bugs or incomplete features. Please report any issues on GitHub and consider backing up your data regularly.\n\n**Key features:**\n- **Thread-based Discussions** - Create and reply to organized discussion threads\n- **Category Organization** - Structure your forum with customizable categories and headers\n- **Rich Text Formatting** - Use BBCode for formatting posts with bold, italic, links, images, code blocks and more\n- **File Attachments** - Attach files from your Nextcloud storage to posts\n- **Post Reactions** - React to posts with emoji reactions\n- **Read/Unread Tracking** - Keep track of which threads you've read\n- **Search** - Find discussions quickly with built-in search\n- **User Profiles** - View user post history and statistics\n- **Role-Based Permissions** - Control access and moderation with flexible roles\n- **Guest Access**: Optional public access for unauthenticated users with configurable permissions\n- **Admin Tools** - Manage categories, roles, BBCodes and forum settings\n- **Moderation Tools** - Pin, lock and manage threads and posts\n\n**Perfect for:**\n- Team discussions and collaboration\n- Community forums\n- Support channels\n- Knowledge bases\n- Project discussions\n- Internal communication\n\nThe forum integrates seamlessly with your Nextcloud instance, using your existing users and groups for authentication and access control." : "Crie discussões, partilhe ideias e colabore com a sua comunidade diretamente no Nextcloud.\n\n**⚠️ Aviso de Desenvolvimento Inicial:**\nEsta aplicação está em fase inicial de desenvolvimento. Embora funcional, pode encontrar bugs ou funcionalidades incompletas. Por favor, comunique quaisquer problemas no GitHub e considere fazer cópias de segurança dos seus dados regularmente.\n\n**Principais características:**\n- **Discussões sobre tópicos** - Crie e responda a tópicos de discussão organizados\n- **Organização por categorias** - Estruture o seu fórum com categorias e cabeçalhos personalizáveis\n- **Formatação de texto avançada** - Utilize o BBCode para formatar as publicações com negrito, itálico, links, imagens, blocos de código e muito mais\n- **Anexos de ficheiros** - Anexe ficheiros do seu armazenamento Nextcloud às publicações\n- **Reações às publicações** - Reagir às publicações com emojis\n- **Controlo de leitura/não leitura** - Acompanhe quais os tópicos que já leu\n- **Pesquisa** - Encontre discussões rapidamente com a pesquisa integrada\n- **Perfis de utilizador** - Visualize o histórico de publicações e as estatísticas do utilizador\n- **Permissões baseadas em funções** - Controle o acesso e a moderação com funções flexíveis\n- **Acesso de convidado**: Acesso público opcional para utilizadores não autenticados com permissões configuráveis\n- **Ferramentas de administração** - Gerir categorias, funções, BBCodes e definições do fórum\n- **Ferramentas de moderação** - Fixar, bloquear e gerir tópicos e publicações\n\n**Ideal para:**\n- Discussões e colaboração em equipa\n- Fóruns da comunidade\n- Canais de suporte\n- Bases de conhecimento\n- Discussões sobre projetos\n- Comunicação interna\n\nO fórum integra-se perfeitamente na sua instância do Nextcloud, utilizando os seus utilizadores e grupos existentes para autenticação e controlo de acesso.",
|
||||
"Search" : "Pesquisa sobre",
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
"Hidden content" : "Conteúdo oculto",
|
||||
"Spoilers" : "Contém spoilers",
|
||||
"Attachment" : "Anexo",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo aos Fóruns Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Bem-vindo aos Fóruns do Nextcloud!",
|
||||
"Bold text" : "Texto a negrito",
|
||||
"Underlined text" : "Texto sublinhado",
|
||||
"Welcome to Nextcloud Forums" : "Bem-vindo aos Fóruns Nextcloud",
|
||||
"Forum" : "Fórum",
|
||||
"Create discussions, share ideas and collaborate with your community directly in Nextcloud.\n\n**⚠️ Early Development Notice:**\nThis app is in early stages of development. While functional, you may encounter bugs or incomplete features. Please report any issues on GitHub and consider backing up your data regularly.\n\n**Key features:**\n- **Thread-based Discussions** - Create and reply to organized discussion threads\n- **Category Organization** - Structure your forum with customizable categories and headers\n- **Rich Text Formatting** - Use BBCode for formatting posts with bold, italic, links, images, code blocks and more\n- **File Attachments** - Attach files from your Nextcloud storage to posts\n- **Post Reactions** - React to posts with emoji reactions\n- **Read/Unread Tracking** - Keep track of which threads you've read\n- **Search** - Find discussions quickly with built-in search\n- **User Profiles** - View user post history and statistics\n- **Role-Based Permissions** - Control access and moderation with flexible roles\n- **Guest Access**: Optional public access for unauthenticated users with configurable permissions\n- **Admin Tools** - Manage categories, roles, BBCodes and forum settings\n- **Moderation Tools** - Pin, lock and manage threads and posts\n\n**Perfect for:**\n- Team discussions and collaboration\n- Community forums\n- Support channels\n- Knowledge bases\n- Project discussions\n- Internal communication\n\nThe forum integrates seamlessly with your Nextcloud instance, using your existing users and groups for authentication and access control." : "Crie discussões, partilhe ideias e colabore com a sua comunidade diretamente no Nextcloud.\n\n**⚠️ Aviso de Desenvolvimento Inicial:**\nEsta aplicação está em fase inicial de desenvolvimento. Embora funcional, pode encontrar bugs ou funcionalidades incompletas. Por favor, comunique quaisquer problemas no GitHub e considere fazer cópias de segurança dos seus dados regularmente.\n\n**Principais características:**\n- **Discussões sobre tópicos** - Crie e responda a tópicos de discussão organizados\n- **Organização por categorias** - Estruture o seu fórum com categorias e cabeçalhos personalizáveis\n- **Formatação de texto avançada** - Utilize o BBCode para formatar as publicações com negrito, itálico, links, imagens, blocos de código e muito mais\n- **Anexos de ficheiros** - Anexe ficheiros do seu armazenamento Nextcloud às publicações\n- **Reações às publicações** - Reagir às publicações com emojis\n- **Controlo de leitura/não leitura** - Acompanhe quais os tópicos que já leu\n- **Pesquisa** - Encontre discussões rapidamente com a pesquisa integrada\n- **Perfis de utilizador** - Visualize o histórico de publicações e as estatísticas do utilizador\n- **Permissões baseadas em funções** - Controle o acesso e a moderação com funções flexíveis\n- **Acesso de convidado**: Acesso público opcional para utilizadores não autenticados com permissões configuráveis\n- **Ferramentas de administração** - Gerir categorias, funções, BBCodes e definições do fórum\n- **Ferramentas de moderação** - Fixar, bloquear e gerir tópicos e publicações\n\n**Ideal para:**\n- Discussões e colaboração em equipa\n- Fóruns da comunidade\n- Canais de suporte\n- Bases de conhecimento\n- Discussões sobre projetos\n- Comunicação interna\n\nO fórum integra-se perfeitamente na sua instância do Nextcloud, utilizando os seus utilizadores e grupos existentes para autenticação e controlo de acesso.",
|
||||
"Search" : "Pesquisa sobre",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Скрытый контент",
|
||||
"Spoilers" : "Спойлеры",
|
||||
"Attachment" : "Вложение",
|
||||
"Welcome to Nextcloud Forums" : "Добро пожаловать на форумы Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Добро пожаловать на форумы Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Это форум, созданный сообществом и встроенный прямо в ваш экземпляр Nextcloud. Здесь вы можете обсуждать темы, делиться идеями и сотрудничать с другими пользователями.",
|
||||
"Features:" : "Функции:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Курсивный текст",
|
||||
"Underlined text" : "Подчеркнутый текст",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Не стесняйтесь начинать новое обсуждение или отвечать на уже существующие. Удачной публикации!",
|
||||
"Welcome to Nextcloud Forums" : "Добро пожаловать на форумы Nextcloud",
|
||||
"Forum" : "Форум",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} новый ответ в {thread}","{count} новых ответов в {thread}","{count} новых ответов в {thread}","{count} новых ответов в {thread}"],
|
||||
"Welcome to the forum!" : "Добро пожаловать на форум!",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Скрытый контент",
|
||||
"Spoilers" : "Спойлеры",
|
||||
"Attachment" : "Вложение",
|
||||
"Welcome to Nextcloud Forums" : "Добро пожаловать на форумы Nextcloud",
|
||||
"Welcome to the Nextcloud Forums!" : "Добро пожаловать на форумы Nextcloud!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Это форум, созданный сообществом и встроенный прямо в ваш экземпляр Nextcloud. Здесь вы можете обсуждать темы, делиться идеями и сотрудничать с другими пользователями.",
|
||||
"Features:" : "Функции:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Курсивный текст",
|
||||
"Underlined text" : "Подчеркнутый текст",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Не стесняйтесь начинать новое обсуждение или отвечать на уже существующие. Удачной публикации!",
|
||||
"Welcome to Nextcloud Forums" : "Добро пожаловать на форумы Nextcloud",
|
||||
"Forum" : "Форум",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} новый ответ в {thread}","{count} новых ответов в {thread}","{count} новых ответов в {thread}","{count} новых ответов в {thread}"],
|
||||
"Welcome to the forum!" : "Добро пожаловать на форум!",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Maudhui yaliyofichika",
|
||||
"Spoilers" : "Waharibifu",
|
||||
"Attachment" : "Kiambatisho",
|
||||
"Welcome to Nextcloud Forums" : "Karibu kwenye jukwaa la Nextcloud ",
|
||||
"Welcome to the Nextcloud Forums!" : "Karibu kwenye majukwaa ya Nextcloud! ",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Hili ni jukwaa linaloendeshwa na jamii lililojengwa ndani ya mfano wako wa Nextcloud. Hapa unaweza kujadili mada, kushiriki mawazo na kushirikiana na watumiaji wengine.",
|
||||
"Features:" : "Sifa:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Maandishi ya italiki",
|
||||
"Underlined text" : "Maandishi yaliyopigiwa mstari",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Jisikie huru kuanzisha mjadala mpya au kujibu mazungumzo yaliyopo. Furaha ya kuchapisha!",
|
||||
"Welcome to Nextcloud Forums" : "Karibu kwenye jukwaa la Nextcloud ",
|
||||
"Forum" : "Jukwaa",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} new reply in {thread}","{count} majibu mapya ndani {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} amekutaja katika {thread}",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Maudhui yaliyofichika",
|
||||
"Spoilers" : "Waharibifu",
|
||||
"Attachment" : "Kiambatisho",
|
||||
"Welcome to Nextcloud Forums" : "Karibu kwenye jukwaa la Nextcloud ",
|
||||
"Welcome to the Nextcloud Forums!" : "Karibu kwenye majukwaa ya Nextcloud! ",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Hili ni jukwaa linaloendeshwa na jamii lililojengwa ndani ya mfano wako wa Nextcloud. Hapa unaweza kujadili mada, kushiriki mawazo na kushirikiana na watumiaji wengine.",
|
||||
"Features:" : "Sifa:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Maandishi ya italiki",
|
||||
"Underlined text" : "Maandishi yaliyopigiwa mstari",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Jisikie huru kuanzisha mjadala mpya au kujibu mazungumzo yaliyopo. Furaha ya kuchapisha!",
|
||||
"Welcome to Nextcloud Forums" : "Karibu kwenye jukwaa la Nextcloud ",
|
||||
"Forum" : "Jukwaa",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{count} new reply in {thread}","{count} majibu mapya ndani {thread}"],
|
||||
"{user} mentioned you in {thread}" : "{user} amekutaja katika {thread}",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "Gizli içerik",
|
||||
"Spoilers" : "Alıntılar",
|
||||
"Attachment" : "Ek dosya",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud forumuna hoş geldiniz",
|
||||
"Welcome to the Nextcloud Forums!" : "Nextcloud forumuna hoş geldiniz!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Bu, doğrudan Nextcloud kopyanızda bulunan topluluk odaklı bir forumdur. Burada konuları tartışabilir, fikirleri paylaşabilir ve diğer kullanıcılarla iş birliği yapabilirsiniz.",
|
||||
"Features:" : "Özellikler:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "Yatık yazı",
|
||||
"Underlined text" : "Altı çizili yazı",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Yeni bir tartışma başlatmaktan veya var olan konulara yanıt vermekten çekinmeyin. Mutlu yazışmalar!",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud forumuna hoş geldiniz",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} konusunda {count} yeni yanıt","{thread} konusunda {count} yeni yanıt"],
|
||||
"{user} mentioned you in {thread}" : "{user} sizi {thread} konusunda andı",
|
||||
@@ -282,6 +282,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "Bildirimleri nasıl almak istediğinizi ayarlayın",
|
||||
"Auto-subscribe to threads I create" : "Oluşturduğum konulara otomatik olarak abone olayım",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Açıldığında, oluşturduğunuz konulara verilen yanıtlar için bildirimleri otomatik olarak alırsınız",
|
||||
"Auto-subscribe to threads I reply to" : "Yanıtladığım konulara otomatik olarak abone olayım",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Açıldığında, yanıtladığınız konulara verilen yanıtlar için bildirimleri otomatik olarak alırsınız",
|
||||
"Files" : "Dosyalar",
|
||||
"Configure file upload settings" : "Dosya yükleme ayarlarını yapılandırın",
|
||||
"Upload directory" : "Yükleme klasörü",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "Gizli içerik",
|
||||
"Spoilers" : "Alıntılar",
|
||||
"Attachment" : "Ek dosya",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud forumuna hoş geldiniz",
|
||||
"Welcome to the Nextcloud Forums!" : "Nextcloud forumuna hoş geldiniz!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "Bu, doğrudan Nextcloud kopyanızda bulunan topluluk odaklı bir forumdur. Burada konuları tartışabilir, fikirleri paylaşabilir ve diğer kullanıcılarla iş birliği yapabilirsiniz.",
|
||||
"Features:" : "Özellikler:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "Yatık yazı",
|
||||
"Underlined text" : "Altı çizili yazı",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "Yeni bir tartışma başlatmaktan veya var olan konulara yanıt vermekten çekinmeyin. Mutlu yazışmalar!",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud forumuna hoş geldiniz",
|
||||
"Forum" : "Forum",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} konusunda {count} yeni yanıt","{thread} konusunda {count} yeni yanıt"],
|
||||
"{user} mentioned you in {thread}" : "{user} sizi {thread} konusunda andı",
|
||||
@@ -280,6 +280,8 @@
|
||||
"Configure how you receive notifications" : "Bildirimleri nasıl almak istediğinizi ayarlayın",
|
||||
"Auto-subscribe to threads I create" : "Oluşturduğum konulara otomatik olarak abone olayım",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "Açıldığında, oluşturduğunuz konulara verilen yanıtlar için bildirimleri otomatik olarak alırsınız",
|
||||
"Auto-subscribe to threads I reply to" : "Yanıtladığım konulara otomatik olarak abone olayım",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "Açıldığında, yanıtladığınız konulara verilen yanıtlar için bildirimleri otomatik olarak alırsınız",
|
||||
"Files" : "Dosyalar",
|
||||
"Configure file upload settings" : "Dosya yükleme ayarlarını yapılandırın",
|
||||
"Upload directory" : "Yükleme klasörü",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "يوشۇرۇن مەزمۇن",
|
||||
"Spoilers" : "بۇزغۇنچىلار",
|
||||
"Attachment" : "قوشۇمچە",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud مۇنبىرىگە خۇش كەپسىز",
|
||||
"Welcome to the Nextcloud Forums!" : "Nextcloud مۇنبىرىگە خۇش كەپسىز!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "بۇ Nextcloud ئۈلگىڭىزگە قۇرۇلغان مەھەللە باشلامچىلىق قىلغان مۇنبەر. بۇ يەردە سىز تېمىلارنى مۇزاكىرە قىلالايسىز، پىكىر ئورتاقلىشالايسىز ۋە باشقا ئىشلەتكۈچىلەر بىلەن ھەمكارلىشالايسىز.",
|
||||
"Features:" : "ئالاھىدىلىكلىرى:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "قىيسىق تېكىست",
|
||||
"Underlined text" : "ئاستى سىزىقلىق تېكىست",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "يېڭى مۇنازىرە باشلىسىڭىز ياكى مەۋجۇت تېمىلارغا جاۋاب قايتۇرسىڭىز بولىدۇ. خەيرلىك يوللاڭ!",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud مۇنبىرىگە خۇش كەپسىز",
|
||||
"Forum" : "مۇنبەر",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} دا {count} يېڭى جاۋاپ","{thread} دا {count} يېڭى جاۋاپ"],
|
||||
"{user} mentioned you in {thread}" : "{user} سىزنى {thread} دا تىلغا ئالدى",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "يوشۇرۇن مەزمۇن",
|
||||
"Spoilers" : "بۇزغۇنچىلار",
|
||||
"Attachment" : "قوشۇمچە",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud مۇنبىرىگە خۇش كەپسىز",
|
||||
"Welcome to the Nextcloud Forums!" : "Nextcloud مۇنبىرىگە خۇش كەپسىز!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "بۇ Nextcloud ئۈلگىڭىزگە قۇرۇلغان مەھەللە باشلامچىلىق قىلغان مۇنبەر. بۇ يەردە سىز تېمىلارنى مۇزاكىرە قىلالايسىز، پىكىر ئورتاقلىشالايسىز ۋە باشقا ئىشلەتكۈچىلەر بىلەن ھەمكارلىشالايسىز.",
|
||||
"Features:" : "ئالاھىدىلىكلىرى:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "قىيسىق تېكىست",
|
||||
"Underlined text" : "ئاستى سىزىقلىق تېكىست",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "يېڭى مۇنازىرە باشلىسىڭىز ياكى مەۋجۇت تېمىلارغا جاۋاب قايتۇرسىڭىز بولىدۇ. خەيرلىك يوللاڭ!",
|
||||
"Welcome to Nextcloud Forums" : "Nextcloud مۇنبىرىگە خۇش كەپسىز",
|
||||
"Forum" : "مۇنبەر",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} دا {count} يېڭى جاۋاپ","{thread} دا {count} يېڭى جاۋاپ"],
|
||||
"{user} mentioned you in {thread}" : "{user} سىزنى {thread} دا تىلغا ئالدى",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "隱藏的內容",
|
||||
"Spoilers" : "劇透",
|
||||
"Attachment" : "附件",
|
||||
"Welcome to Nextcloud Forums" : "歡迎來到 Nextcloud 論壇",
|
||||
"Welcome to the Nextcloud Forums!" : "歡迎來到 Nextcloud 論壇!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "這是一個直接建置在你 Nextcloud 實例之中的社群主導論壇。你可以在這裡討論各種主題、分享意見,並與其他用戶協作。",
|
||||
"Features:" : "功能:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "斜體文字",
|
||||
"Underlined text" : "下面畫線的文字",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "歡迎隨時開啟新討論或回覆現有主題,玩得開心!",
|
||||
"Welcome to Nextcloud Forums" : "歡迎來到 Nextcloud 論壇",
|
||||
"Forum" : "論壇",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} 中有 {count} 個新回覆"],
|
||||
"{user} mentioned you in {thread}" : "{user} 在 {thread} 中提及您",
|
||||
@@ -282,6 +282,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "設定你接收通知的方式",
|
||||
"Auto-subscribe to threads I create" : "自動訂閱由我建立的主題",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "啟用後,你會自動收到針對你所建立主題之回覆通知。",
|
||||
"Auto-subscribe to threads I reply to" : "自動訂閱我回覆的討論串",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "啟用後,您將會自動收到您回覆過的討論串中的新回覆通知",
|
||||
"Files" : "檔案",
|
||||
"Configure file upload settings" : "配置檔案上傳設定",
|
||||
"Upload directory" : "上載目錄",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "隱藏的內容",
|
||||
"Spoilers" : "劇透",
|
||||
"Attachment" : "附件",
|
||||
"Welcome to Nextcloud Forums" : "歡迎來到 Nextcloud 論壇",
|
||||
"Welcome to the Nextcloud Forums!" : "歡迎來到 Nextcloud 論壇!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "這是一個直接建置在你 Nextcloud 實例之中的社群主導論壇。你可以在這裡討論各種主題、分享意見,並與其他用戶協作。",
|
||||
"Features:" : "功能:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "斜體文字",
|
||||
"Underlined text" : "下面畫線的文字",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "歡迎隨時開啟新討論或回覆現有主題,玩得開心!",
|
||||
"Welcome to Nextcloud Forums" : "歡迎來到 Nextcloud 論壇",
|
||||
"Forum" : "論壇",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} 中有 {count} 個新回覆"],
|
||||
"{user} mentioned you in {thread}" : "{user} 在 {thread} 中提及您",
|
||||
@@ -280,6 +280,8 @@
|
||||
"Configure how you receive notifications" : "設定你接收通知的方式",
|
||||
"Auto-subscribe to threads I create" : "自動訂閱由我建立的主題",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "啟用後,你會自動收到針對你所建立主題之回覆通知。",
|
||||
"Auto-subscribe to threads I reply to" : "自動訂閱我回覆的討論串",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "啟用後,您將會自動收到您回覆過的討論串中的新回覆通知",
|
||||
"Files" : "檔案",
|
||||
"Configure file upload settings" : "配置檔案上傳設定",
|
||||
"Upload directory" : "上載目錄",
|
||||
|
||||
@@ -20,7 +20,6 @@ OC.L10N.register(
|
||||
"Hidden content" : "隱藏內容",
|
||||
"Spoilers" : "劇透",
|
||||
"Attachment" : "附件",
|
||||
"Welcome to Nextcloud Forums" : "歡迎使用 Nextcloud 論壇",
|
||||
"Welcome to the Nextcloud Forums!" : "歡迎使用 Nextcloud 論壇!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "這是內建於您的 Nextcloud 站台中的社群驅動論壇。在此您可以討論主題、分享想法,並與其他使用者協作。",
|
||||
"Features:" : "功能:",
|
||||
@@ -36,6 +35,7 @@ OC.L10N.register(
|
||||
"Italic text" : "斜體文字",
|
||||
"Underlined text" : "有底線的文字",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "歡迎隨時開啟新討論或回覆現有主題。祝您發文愉快!",
|
||||
"Welcome to Nextcloud Forums" : "歡迎使用 Nextcloud 論壇",
|
||||
"Forum" : "論壇",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} 中有 {count} 個新回覆"],
|
||||
"{user} mentioned you in {thread}" : "{user} 在 {thread} 中提及您",
|
||||
@@ -282,6 +282,8 @@ OC.L10N.register(
|
||||
"Configure how you receive notifications" : "設定您要如何接收通知",
|
||||
"Auto-subscribe to threads I create" : "自動訂閱我建立的討論串",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "啟用後,您會自動收到您所建立的討論串回覆的通知",
|
||||
"Auto-subscribe to threads I reply to" : "自動訂閱我回覆的討論串",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "啟用後,您將會自動收到您回覆過的討論串中的新回覆通知",
|
||||
"Files" : "檔案",
|
||||
"Configure file upload settings" : "設定檔案上傳設定",
|
||||
"Upload directory" : "上傳目錄",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"Hidden content" : "隱藏內容",
|
||||
"Spoilers" : "劇透",
|
||||
"Attachment" : "附件",
|
||||
"Welcome to Nextcloud Forums" : "歡迎使用 Nextcloud 論壇",
|
||||
"Welcome to the Nextcloud Forums!" : "歡迎使用 Nextcloud 論壇!",
|
||||
"This is a community-driven forum built right into your Nextcloud instance. Here you can discuss topics, share ideas and collaborate with other users." : "這是內建於您的 Nextcloud 站台中的社群驅動論壇。在此您可以討論主題、分享想法,並與其他使用者協作。",
|
||||
"Features:" : "功能:",
|
||||
@@ -34,6 +33,7 @@
|
||||
"Italic text" : "斜體文字",
|
||||
"Underlined text" : "有底線的文字",
|
||||
"Feel free to start a new discussion or reply to existing threads. Happy posting!" : "歡迎隨時開啟新討論或回覆現有主題。祝您發文愉快!",
|
||||
"Welcome to Nextcloud Forums" : "歡迎使用 Nextcloud 論壇",
|
||||
"Forum" : "論壇",
|
||||
"_{count} new reply in {thread}_::_{count} new replies in {thread}_" : ["{thread} 中有 {count} 個新回覆"],
|
||||
"{user} mentioned you in {thread}" : "{user} 在 {thread} 中提及您",
|
||||
@@ -280,6 +280,8 @@
|
||||
"Configure how you receive notifications" : "設定您要如何接收通知",
|
||||
"Auto-subscribe to threads I create" : "自動訂閱我建立的討論串",
|
||||
"When enabled, you will automatically receive notifications for replies to threads you create" : "啟用後,您會自動收到您所建立的討論串回覆的通知",
|
||||
"Auto-subscribe to threads I reply to" : "自動訂閱我回覆的討論串",
|
||||
"When enabled, you will automatically receive notifications for new replies in threads you have replied to" : "啟用後,您將會自動收到您回覆過的討論串中的新回覆通知",
|
||||
"Files" : "檔案",
|
||||
"Configure file upload settings" : "設定檔案上傳設定",
|
||||
"Upload directory" : "上傳目錄",
|
||||
|
||||
@@ -87,7 +87,8 @@ class RepairSeeds extends Command {
|
||||
}
|
||||
};
|
||||
|
||||
SeedHelper::seedAll($migrationOutput);
|
||||
// Pass throwOnError=true so users get proper error feedback
|
||||
SeedHelper::seedAll($migrationOutput, true);
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln('<info>Forum data repair/seed completed successfully!</info>');
|
||||
|
||||
@@ -192,6 +192,7 @@ class CategoryController extends OCSController {
|
||||
* Update a category
|
||||
*
|
||||
* @param int $id Category ID
|
||||
* @param int|null $headerId Category header ID
|
||||
* @param string|null $name Category name
|
||||
* @param string|null $description Category description
|
||||
* @param string|null $slug Category slug
|
||||
@@ -203,10 +204,13 @@ class CategoryController extends OCSController {
|
||||
#[NoAdminRequired]
|
||||
#[RequirePermission('canEditCategories')]
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/categories/{id}')]
|
||||
public function update(int $id, ?string $name = null, ?string $description = null, ?string $slug = null, ?int $sortOrder = null): DataResponse {
|
||||
public function update(int $id, ?int $headerId = null, ?string $name = null, ?string $description = null, ?string $slug = null, ?int $sortOrder = null): DataResponse {
|
||||
try {
|
||||
$category = $this->categoryMapper->find($id);
|
||||
|
||||
if ($headerId !== null) {
|
||||
$category->setHeaderId($headerId);
|
||||
}
|
||||
if ($name !== null) {
|
||||
$category->setName($name);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@ use OCA\Forum\Db\PostMapper;
|
||||
use OCA\Forum\Db\ReactionMapper;
|
||||
use OCA\Forum\Db\ReadMarkerMapper;
|
||||
use OCA\Forum\Db\ThreadMapper;
|
||||
use OCA\Forum\Db\ThreadSubscriptionMapper;
|
||||
use OCA\Forum\Service\BBCodeService;
|
||||
use OCA\Forum\Service\NotificationService;
|
||||
use OCA\Forum\Service\PermissionService;
|
||||
use OCA\Forum\Service\PostEnrichmentService;
|
||||
use OCA\Forum\Service\PostHistoryService;
|
||||
use OCA\Forum\Service\UserPreferencesService;
|
||||
use OCA\Forum\Service\UserService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Http;
|
||||
@@ -49,6 +51,8 @@ class PostController extends OCSController {
|
||||
private PostEnrichmentService $postEnrichmentService,
|
||||
private PostHistoryService $postHistoryService,
|
||||
private UserService $userService,
|
||||
private UserPreferencesService $userPreferencesService,
|
||||
private ThreadSubscriptionMapper $threadSubscriptionMapper,
|
||||
private IUserSession $userSession,
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
@@ -400,6 +404,21 @@ class PostController extends OCSController {
|
||||
// Don't fail the request if mention notification sending fails
|
||||
}
|
||||
|
||||
// Auto-subscribe the user to the thread if preference is enabled and not already subscribed
|
||||
try {
|
||||
$autoSubscribe = $this->userPreferencesService->getPreference(
|
||||
$user->getUID(),
|
||||
UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS
|
||||
);
|
||||
|
||||
if ($autoSubscribe && !$this->threadSubscriptionMapper->isUserSubscribed($user->getUID(), $threadId)) {
|
||||
$this->threadSubscriptionMapper->subscribe($user->getUID(), $threadId);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->warning('Failed to auto-subscribe user to thread: ' . $e->getMessage());
|
||||
// Don't fail the request if auto-subscribe fails
|
||||
}
|
||||
|
||||
return new DataResponse($this->postEnrichmentService->enrichPost($createdPost), Http::STATUS_CREATED);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Error creating post: ' . $e->getMessage());
|
||||
|
||||
@@ -31,7 +31,6 @@ use OCP\AppFramework\Db\Entity;
|
||||
* @method void setUpdatedAt(int $updatedAt)
|
||||
*/
|
||||
class ForumUser extends Entity implements JsonSerializable {
|
||||
public $id;
|
||||
protected string $userId = '';
|
||||
protected int $postCount = 0;
|
||||
protected int $threadCount = 0;
|
||||
|
||||
@@ -17,33 +17,106 @@ class SeedHelper {
|
||||
* Each function checks its own state and returns early if already seeded
|
||||
*
|
||||
* @param \OCP\Migration\IOutput|null $output Optional output for console messages
|
||||
* @param bool $throwOnError If true, throws exceptions on failure. If false (default), logs errors and continues.
|
||||
* Set to false when called from migrations to avoid PostgreSQL transaction abort issues.
|
||||
*/
|
||||
public static function seedAll($output = null): void {
|
||||
public static function seedAll($output = null, bool $throwOnError = false): void {
|
||||
$logger = \OC::$server->get(\Psr\Log\LoggerInterface::class);
|
||||
$db = \OC::$server->get(\OCP\IDBConnection::class);
|
||||
$logger->info('Forum seeding: Starting data seed/repair');
|
||||
|
||||
if ($output) {
|
||||
$output->info('Forum: Starting data seed/repair...');
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
|
||||
// Ensure forum_users table exists (handle rename from forum_user_stats if needed)
|
||||
self::ensureForumUsersTable($output);
|
||||
// This is critical and should fail early if it cannot be done
|
||||
try {
|
||||
self::ensureForumUsersTable($output);
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = 'ensureForumUsersTable: ' . $e->getMessage();
|
||||
$logger->error('Forum seeding: Failed to ensure forum_users table', ['exception' => $e->getMessage()]);
|
||||
if ($output) {
|
||||
$output->warning(' Failed to ensure forum_users table: ' . $e->getMessage());
|
||||
}
|
||||
// Try to recover connection state for PostgreSQL
|
||||
self::recoverConnectionState($db, $logger);
|
||||
}
|
||||
|
||||
// Each function checks its own state and returns early if already seeded
|
||||
// They run independently so one failure doesn't block others
|
||||
self::seedDefaultRoles($output);
|
||||
self::seedCategoryHeaders($output);
|
||||
self::seedDefaultCategories($output);
|
||||
self::seedCategoryPermissions($output);
|
||||
self::seedGuestRolePermissions($output);
|
||||
self::seedDefaultBBCodes($output);
|
||||
self::assignUserRoles($output);
|
||||
self::seedWelcomeThread($output);
|
||||
// They run independently so one failure does not block others
|
||||
// This is especially important for PostgreSQL where a failed query aborts the transaction
|
||||
$seedOperations = [
|
||||
'seedDefaultRoles' => fn () => self::seedDefaultRoles($output),
|
||||
'seedCategoryHeaders' => fn () => self::seedCategoryHeaders($output),
|
||||
'seedDefaultCategories' => fn () => self::seedDefaultCategories($output),
|
||||
'seedCategoryPermissions' => fn () => self::seedCategoryPermissions($output),
|
||||
'seedGuestRolePermissions' => fn () => self::seedGuestRolePermissions($output),
|
||||
'seedDefaultBBCodes' => fn () => self::seedDefaultBBCodes($output),
|
||||
'assignUserRoles' => fn () => self::assignUserRoles($output),
|
||||
'seedWelcomeThread' => fn () => self::seedWelcomeThread($output),
|
||||
];
|
||||
|
||||
$logger->info('Forum seeding: Completed data seed/repair');
|
||||
foreach ($seedOperations as $name => $operation) {
|
||||
try {
|
||||
// Before each operation, ensure connection is in a clean state
|
||||
self::recoverConnectionState($db, $logger);
|
||||
$operation();
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = "$name: " . $e->getMessage();
|
||||
$logger->error("Forum seeding: $name failed", ['exception' => $e->getMessage()]);
|
||||
// Try to recover connection state for next operation (especially important for PostgreSQL)
|
||||
self::recoverConnectionState($db, $logger);
|
||||
// Continue with other operations - don't let one failure block others
|
||||
}
|
||||
}
|
||||
|
||||
if ($output) {
|
||||
$output->info('Forum: Data seed/repair completed');
|
||||
if (!empty($errors)) {
|
||||
$errorSummary = 'Some seeding operations failed: ' . implode('; ', $errors);
|
||||
$logger->warning('Forum seeding: Completed with errors', ['errors' => $errors]);
|
||||
|
||||
if ($output) {
|
||||
$output->warning('Forum: Data seed/repair completed with errors. Run "occ forum:repair-seeds" to retry failed operations.');
|
||||
}
|
||||
|
||||
if ($throwOnError) {
|
||||
throw new \RuntimeException($errorSummary);
|
||||
}
|
||||
} else {
|
||||
$logger->info('Forum seeding: Completed data seed/repair successfully');
|
||||
|
||||
if ($output) {
|
||||
$output->info('Forum: Data seed/repair completed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover database connection state after an error
|
||||
* On PostgreSQL, a failed query aborts the entire transaction, and subsequent queries fail.
|
||||
* This method attempts to rollback any open transactions to restore a usable connection state.
|
||||
*
|
||||
* @param \OCP\IDBConnection $db Database connection
|
||||
* @param \Psr\Log\LoggerInterface $logger Logger instance
|
||||
*/
|
||||
private static function recoverConnectionState(\OCP\IDBConnection $db, \Psr\Log\LoggerInterface $logger): void {
|
||||
try {
|
||||
// If we're in a transaction, try to roll back to recover the connection
|
||||
while ($db->inTransaction()) {
|
||||
try {
|
||||
$db->rollBack();
|
||||
$logger->debug('Forum seeding: Rolled back transaction to recover connection state');
|
||||
} catch (\Exception $e) {
|
||||
// If rollback fails, the connection might be in an unrecoverable state
|
||||
$logger->warning('Forum seeding: Failed to rollback transaction during recovery', ['exception' => $e->getMessage()]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Ignore errors when checking transaction state
|
||||
$logger->debug('Forum seeding: Error checking transaction state', ['exception' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +198,8 @@ class SeedHelper {
|
||||
|
||||
/**
|
||||
* Create the forum_users table from scratch
|
||||
* This mirrors the schema from Version1 + Version8 migrations
|
||||
* This mirrors the final schema from Version1 + Version2 + Version8 migrations
|
||||
* (id as primary key, user_id as unique, includes signature column)
|
||||
*/
|
||||
private static function createForumUsersTable(\OCP\IDBConnection $db): void {
|
||||
$platform = $db->getDatabasePlatform();
|
||||
@@ -138,6 +212,7 @@ class SeedHelper {
|
||||
// MySQL and MariaDB both extend MySQLPlatform
|
||||
$db->executeStatement("
|
||||
CREATE TABLE `{$tableName}` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` VARCHAR(64) NOT NULL,
|
||||
`post_count` INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
`thread_count` INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
@@ -146,14 +221,17 @@ class SeedHelper {
|
||||
`signature` TEXT DEFAULT NULL,
|
||||
`created_at` INT UNSIGNED NOT NULL,
|
||||
`updated_at` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`user_id`),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `forum_users_user_id_uniq` (`user_id`),
|
||||
INDEX `forum_users_post_count_idx` (`post_count`),
|
||||
INDEX `forum_users_thread_count_idx` (`thread_count`),
|
||||
INDEX `forum_users_deleted_at_idx` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
|
||||
");
|
||||
} elseif ($platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform) {
|
||||
$db->executeStatement("
|
||||
CREATE TABLE \"{$tableName}\" (
|
||||
\"id\" BIGSERIAL PRIMARY KEY,
|
||||
\"user_id\" VARCHAR(64) NOT NULL,
|
||||
\"post_count\" INTEGER NOT NULL DEFAULT 0,
|
||||
\"thread_count\" INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -161,16 +239,18 @@ class SeedHelper {
|
||||
\"deleted_at\" INTEGER DEFAULT NULL,
|
||||
\"signature\" TEXT DEFAULT NULL,
|
||||
\"created_at\" INTEGER NOT NULL,
|
||||
\"updated_at\" INTEGER NOT NULL,
|
||||
PRIMARY KEY (\"user_id\")
|
||||
\"updated_at\" INTEGER NOT NULL
|
||||
)
|
||||
");
|
||||
$db->executeStatement("CREATE UNIQUE INDEX \"forum_users_user_id_uniq\" ON \"{$tableName}\" (\"user_id\")");
|
||||
$db->executeStatement("CREATE INDEX \"forum_users_post_count_idx\" ON \"{$tableName}\" (\"post_count\")");
|
||||
$db->executeStatement("CREATE INDEX \"forum_users_thread_count_idx\" ON \"{$tableName}\" (\"thread_count\")");
|
||||
$db->executeStatement("CREATE INDEX \"forum_users_deleted_at_idx\" ON \"{$tableName}\" (\"deleted_at\")");
|
||||
} else {
|
||||
// SQLite (and any other platform as fallback)
|
||||
$db->executeStatement("
|
||||
CREATE TABLE \"{$tableName}\" (
|
||||
\"id\" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
\"user_id\" VARCHAR(64) NOT NULL,
|
||||
\"post_count\" INTEGER NOT NULL DEFAULT 0,
|
||||
\"thread_count\" INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -178,11 +258,12 @@ class SeedHelper {
|
||||
\"deleted_at\" INTEGER DEFAULT NULL,
|
||||
\"signature\" TEXT DEFAULT NULL,
|
||||
\"created_at\" INTEGER NOT NULL,
|
||||
\"updated_at\" INTEGER NOT NULL,
|
||||
PRIMARY KEY (\"user_id\")
|
||||
\"updated_at\" INTEGER NOT NULL
|
||||
)
|
||||
");
|
||||
$db->executeStatement("CREATE UNIQUE INDEX \"forum_users_user_id_uniq\" ON \"{$tableName}\" (\"user_id\")");
|
||||
$db->executeStatement("CREATE INDEX \"forum_users_post_count_idx\" ON \"{$tableName}\" (\"post_count\")");
|
||||
$db->executeStatement("CREATE INDEX \"forum_users_thread_count_idx\" ON \"{$tableName}\" (\"thread_count\")");
|
||||
$db->executeStatement("CREATE INDEX \"forum_users_deleted_at_idx\" ON \"{$tableName}\" (\"deleted_at\")");
|
||||
}
|
||||
}
|
||||
@@ -262,7 +343,8 @@ class SeedHelper {
|
||||
$output->info(' → Creating default roles...');
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
// Note: We don't use explicit transactions here to avoid PostgreSQL transaction abort cascade.
|
||||
// Each INSERT is independent and idempotent, so partial success is acceptable.
|
||||
$rolesCreated = 0;
|
||||
|
||||
// Define roles by role_type (not hardcoded IDs)
|
||||
@@ -307,26 +389,29 @@ class SeedHelper {
|
||||
|
||||
foreach ($rolesToCreate as $roleType => $roleData) {
|
||||
if (!in_array($roleType, $existingTypes)) {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_roles')
|
||||
->values([
|
||||
'name' => $qb->createNamedParameter($roleData['name']),
|
||||
'description' => $qb->createNamedParameter($roleData['description']),
|
||||
'can_access_admin_tools' => $qb->createNamedParameter($roleData['can_access_admin_tools'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_edit_roles' => $qb->createNamedParameter($roleData['can_edit_roles'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_edit_categories' => $qb->createNamedParameter($roleData['can_edit_categories'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'is_system_role' => $qb->createNamedParameter($roleData['is_system_role'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'role_type' => $qb->createNamedParameter($roleData['role_type'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_STR),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$rolesCreated++;
|
||||
$logger->info("Forum seeding: Created role with type '$roleType'");
|
||||
try {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_roles')
|
||||
->values([
|
||||
'name' => $qb->createNamedParameter($roleData['name']),
|
||||
'description' => $qb->createNamedParameter($roleData['description']),
|
||||
'can_access_admin_tools' => $qb->createNamedParameter($roleData['can_access_admin_tools'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_edit_roles' => $qb->createNamedParameter($roleData['can_edit_roles'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_edit_categories' => $qb->createNamedParameter($roleData['can_edit_categories'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'is_system_role' => $qb->createNamedParameter($roleData['is_system_role'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'role_type' => $qb->createNamedParameter($roleData['role_type'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_STR),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$rolesCreated++;
|
||||
$logger->info("Forum seeding: Created role with type '$roleType'");
|
||||
} catch (\Exception $e) {
|
||||
// Log but continue - other roles might succeed
|
||||
$logger->warning("Forum seeding: Failed to create role '$roleType': " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
// Validate that critical roles can be found by role_type after creation
|
||||
// Note: We query directly instead of using RoleMapper to avoid MultipleObjectsReturnedException
|
||||
// if duplicates somehow exist (the cleanup migration should have removed them, but be defensive)
|
||||
@@ -361,9 +446,6 @@ class SeedHelper {
|
||||
$output->info(" ✓ Created $rolesCreated default roles (Admin, Moderator, User, Guest)");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
$logger->error('Forum seeding: Failed to create default roles', [
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
@@ -456,7 +538,7 @@ class SeedHelper {
|
||||
$userAccessibleCategories = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
|
||||
$db->beginTransaction();
|
||||
// Note: No explicit transaction - each INSERT auto-commits to avoid PostgreSQL transaction abort cascade
|
||||
$categoriesGranted = 0;
|
||||
foreach ($userAccessibleCategories as $categoryRow) {
|
||||
$categoryId = (int)$categoryRow['category_id'];
|
||||
@@ -472,30 +554,31 @@ class SeedHelper {
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$permExists) {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_category_perms')
|
||||
->values([
|
||||
'category_id' => $qb->createNamedParameter($categoryId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'role_id' => $qb->createNamedParameter($guestRoleId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'can_view' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_post' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_reply' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_moderate' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
])
|
||||
->executeStatement();
|
||||
$categoriesGranted++;
|
||||
try {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_category_perms')
|
||||
->values([
|
||||
'category_id' => $qb->createNamedParameter($categoryId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'role_id' => $qb->createNamedParameter($guestRoleId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'can_view' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_post' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_reply' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_moderate' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
])
|
||||
->executeStatement();
|
||||
$categoriesGranted++;
|
||||
} catch (\Exception $e) {
|
||||
// Log but continue - other categories might succeed
|
||||
$logger->warning("Forum seeding: Failed to set guest permission for category $categoryId: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
$logger->info('Forum seeding: Set guest role view-only permissions for ' . $categoriesGranted . ' categories (matching User role access)');
|
||||
if ($output) {
|
||||
$output->info(' ✓ Set guest role view-only permissions for ' . $categoriesGranted . ' categories');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
$logger->error('Forum seeding: Failed to set guest role permissions', [
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
@@ -539,8 +622,7 @@ class SeedHelper {
|
||||
$output->info(' → Creating category headers...');
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
// Note: No explicit transaction - single INSERT auto-commits
|
||||
// Create "General" category header
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_cat_headers')
|
||||
@@ -552,15 +634,11 @@ class SeedHelper {
|
||||
])
|
||||
->executeStatement();
|
||||
|
||||
$db->commit();
|
||||
$logger->info('Forum seeding: Created category headers');
|
||||
if ($output) {
|
||||
$output->info(' ✓ Created category headers');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
$logger->error('Forum seeding: Failed to create category headers', [
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
@@ -606,7 +684,7 @@ class SeedHelper {
|
||||
}
|
||||
|
||||
$headerId = (int)$header['id'];
|
||||
$db->beginTransaction();
|
||||
// Note: No explicit transaction - each INSERT auto-commits to avoid PostgreSQL transaction abort cascade
|
||||
$categoriesCreated = 0;
|
||||
|
||||
// Check if "General Discussions" category exists
|
||||
@@ -619,22 +697,26 @@ class SeedHelper {
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$exists) {
|
||||
// Create "General Discussions" category
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_categories')
|
||||
->values([
|
||||
'header_id' => $qb->createNamedParameter($headerId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'name' => $qb->createNamedParameter($l->t('General discussions')),
|
||||
'description' => $qb->createNamedParameter($l->t('A place for general conversations and discussions')),
|
||||
'slug' => $qb->createNamedParameter('general-discussions'),
|
||||
'sort_order' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'thread_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'post_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'updated_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$categoriesCreated++;
|
||||
try {
|
||||
// Create "General Discussions" category
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_categories')
|
||||
->values([
|
||||
'header_id' => $qb->createNamedParameter($headerId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'name' => $qb->createNamedParameter($l->t('General discussions')),
|
||||
'description' => $qb->createNamedParameter($l->t('A place for general conversations and discussions')),
|
||||
'slug' => $qb->createNamedParameter('general-discussions'),
|
||||
'sort_order' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'thread_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'post_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'updated_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$categoriesCreated++;
|
||||
} catch (\Exception $e) {
|
||||
$logger->warning('Forum seeding: Failed to create General Discussions category: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if "Support" category exists
|
||||
@@ -647,33 +729,33 @@ class SeedHelper {
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$exists) {
|
||||
// Create "Support" category
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_categories')
|
||||
->values([
|
||||
'header_id' => $qb->createNamedParameter($headerId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'name' => $qb->createNamedParameter($l->t('Support')),
|
||||
'description' => $qb->createNamedParameter($l->t('Ask questions about the forum, provide feedback or report issues.')),
|
||||
'slug' => $qb->createNamedParameter('support'),
|
||||
'sort_order' => $qb->createNamedParameter(1, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'thread_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'post_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'updated_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$categoriesCreated++;
|
||||
try {
|
||||
// Create "Support" category
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_categories')
|
||||
->values([
|
||||
'header_id' => $qb->createNamedParameter($headerId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'name' => $qb->createNamedParameter($l->t('Support')),
|
||||
'description' => $qb->createNamedParameter($l->t('Ask questions about the forum, provide feedback or report issues.')),
|
||||
'slug' => $qb->createNamedParameter('support'),
|
||||
'sort_order' => $qb->createNamedParameter(1, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'thread_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'post_count' => $qb->createNamedParameter(0, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'updated_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$categoriesCreated++;
|
||||
} catch (\Exception $e) {
|
||||
$logger->warning('Forum seeding: Failed to create Support category: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
$logger->info("Forum seeding: Created $categoriesCreated default categories");
|
||||
if ($output) {
|
||||
$output->info(" ✓ Created $categoriesCreated default categories (General Discussions, Support)");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
$logger->error('Forum seeding: Failed to create default categories', [
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
@@ -743,7 +825,7 @@ class SeedHelper {
|
||||
$output->info(' → Creating category permissions...');
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
// Note: No explicit transaction - each INSERT auto-commits to avoid PostgreSQL transaction abort cascade
|
||||
$permissionsCreated = 0;
|
||||
|
||||
// Create permissions for Moderator and User roles (Admin has implicit permissions)
|
||||
@@ -761,18 +843,22 @@ class SeedHelper {
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$exists) {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_category_perms')
|
||||
->values([
|
||||
'category_id' => $qb->createNamedParameter($categoryId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'role_id' => $qb->createNamedParameter($moderatorRoleId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'can_view' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_post' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_reply' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_moderate' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
])
|
||||
->executeStatement();
|
||||
$permissionsCreated++;
|
||||
try {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_category_perms')
|
||||
->values([
|
||||
'category_id' => $qb->createNamedParameter($categoryId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'role_id' => $qb->createNamedParameter($moderatorRoleId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'can_view' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_post' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_reply' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_moderate' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
])
|
||||
->executeStatement();
|
||||
$permissionsCreated++;
|
||||
} catch (\Exception $e) {
|
||||
$logger->warning("Forum seeding: Failed to create moderator permission for category $categoryId: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Check and create User role permissions
|
||||
@@ -786,30 +872,30 @@ class SeedHelper {
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$exists) {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_category_perms')
|
||||
->values([
|
||||
'category_id' => $qb->createNamedParameter($categoryId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'role_id' => $qb->createNamedParameter($userRoleId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'can_view' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_post' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_reply' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_moderate' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
])
|
||||
->executeStatement();
|
||||
$permissionsCreated++;
|
||||
try {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_category_perms')
|
||||
->values([
|
||||
'category_id' => $qb->createNamedParameter($categoryId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'role_id' => $qb->createNamedParameter($userRoleId, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
'can_view' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_post' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_reply' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'can_moderate' => $qb->createNamedParameter(false, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
])
|
||||
->executeStatement();
|
||||
$permissionsCreated++;
|
||||
} catch (\Exception $e) {
|
||||
$logger->warning("Forum seeding: Failed to create user permission for category $categoryId: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
$logger->info("Forum seeding: Created $permissionsCreated category permissions");
|
||||
if ($output) {
|
||||
$output->info(" ✓ Created $permissionsCreated category permissions for " . count($categories) . ' categories');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
$logger->error('Forum seeding: Failed to create category permissions', [
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
@@ -836,8 +922,7 @@ class SeedHelper {
|
||||
$output->info(' → Creating default BBCodes...');
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
// Note: No explicit transaction - each INSERT auto-commits to avoid PostgreSQL transaction abort cascade
|
||||
$bbcodes = [
|
||||
[
|
||||
'tag' => 'icode',
|
||||
@@ -880,33 +965,33 @@ class SeedHelper {
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$exists) {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_bbcodes')
|
||||
->values([
|
||||
'tag' => $qb->createNamedParameter($bbcode['tag']),
|
||||
'replacement' => $qb->createNamedParameter($bbcode['replacement']),
|
||||
'example' => $qb->createNamedParameter($bbcode['example']),
|
||||
'description' => $qb->createNamedParameter($bbcode['description']),
|
||||
'enabled' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'parse_inner' => $qb->createNamedParameter($bbcode['parse_inner'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'is_builtin' => $qb->createNamedParameter($bbcode['is_builtin'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'special_handler' => $qb->createNamedParameter($bbcode['special_handler']),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$bbcodesCreated++;
|
||||
try {
|
||||
$qb = $db->getQueryBuilder();
|
||||
$qb->insert('forum_bbcodes')
|
||||
->values([
|
||||
'tag' => $qb->createNamedParameter($bbcode['tag']),
|
||||
'replacement' => $qb->createNamedParameter($bbcode['replacement']),
|
||||
'example' => $qb->createNamedParameter($bbcode['example']),
|
||||
'description' => $qb->createNamedParameter($bbcode['description']),
|
||||
'enabled' => $qb->createNamedParameter(true, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'parse_inner' => $qb->createNamedParameter($bbcode['parse_inner'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'is_builtin' => $qb->createNamedParameter($bbcode['is_builtin'], \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_BOOL),
|
||||
'special_handler' => $qb->createNamedParameter($bbcode['special_handler']),
|
||||
'created_at' => $qb->createNamedParameter($timestamp, \OCP\DB\QueryBuilder\IQueryBuilder::PARAM_INT),
|
||||
])
|
||||
->executeStatement();
|
||||
$bbcodesCreated++;
|
||||
} catch (\Exception $e) {
|
||||
$logger->warning("Forum seeding: Failed to create BBCode '{$bbcode['tag']}': " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
$logger->info("Forum seeding: Created $bbcodesCreated default BBCodes");
|
||||
if ($output) {
|
||||
$output->info(" ✓ Created $bbcodesCreated default BBCodes (icode, spoiler, attachment)");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
$logger->error('Forum seeding: Failed to create default BBCodes', [
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
@@ -1061,6 +1146,9 @@ class SeedHelper {
|
||||
$logger = \OC::$server->get(\Psr\Log\LoggerInterface::class);
|
||||
$timestamp = time();
|
||||
|
||||
// Recover connection state before starting (important for PostgreSQL)
|
||||
self::recoverConnectionState($db, $logger);
|
||||
|
||||
try {
|
||||
// Check if welcome thread already exists
|
||||
$qb = $db->getQueryBuilder();
|
||||
@@ -1112,6 +1200,42 @@ class SeedHelper {
|
||||
}
|
||||
});
|
||||
|
||||
// Check if slug column still exists BEFORE starting transaction
|
||||
// (for backwards compatibility with old migrations)
|
||||
// On PostgreSQL, a failed query inside a transaction aborts the entire transaction,
|
||||
// so we must check column existence outside the transaction
|
||||
$hasSlugColumn = true;
|
||||
try {
|
||||
$checkQb = $db->getQueryBuilder();
|
||||
$checkQb->select('slug')->from('forum_posts')->setMaxResults(1);
|
||||
$checkQb->executeQuery()->closeCursor();
|
||||
} catch (\Exception $e) {
|
||||
$hasSlugColumn = false;
|
||||
// Recover connection state after the failed query (important for PostgreSQL)
|
||||
self::recoverConnectionState($db, $logger);
|
||||
}
|
||||
|
||||
// Prepare welcome post content
|
||||
$welcomeContent = $l->t('Welcome to the Nextcloud Forums!') . "\n\n"
|
||||
. $l->t('This is a community-driven forum built right into your Nextcloud instance. '
|
||||
. 'Here you can discuss topics, share ideas and collaborate with other users.') . "\n\n"
|
||||
. '[b]' . $l->t('Features:') . "[/b]\n"
|
||||
. "[list]\n"
|
||||
. '[*]' . $l->t('Create and reply to threads') . "\n"
|
||||
. '[*]' . $l->t('Organize discussions by categories') . "\n"
|
||||
. '[*]' . $l->t('Use BBCode for rich text formatting') . "\n"
|
||||
. '[*]' . $l->t('Attach files from your Nextcloud storage') . "\n"
|
||||
. '[*]' . $l->t('React to posts') . "\n"
|
||||
. '[*]' . $l->t('Track read/unread threads') . "\n\n"
|
||||
. "[/list]\n"
|
||||
. '[b]' . $l->t('BBCode examples:') . "[/b]\n"
|
||||
. "[list]\n"
|
||||
. '[*][b]' . $l->t('Bold text') . '[/b] - ' . $l->t('Use %1$stext%2$s', ['[icode][b]', '[/b][/icode]']) . "\n"
|
||||
. '[*][i]' . $l->t('Italic text') . '[/i] - ' . $l->t('Use %1$stext%2$s', ['[icode][i]', '[/i][/icode]']) . "\n"
|
||||
. '[*][u]' . $l->t('Underlined text') . '[/u] - ' . $l->t('Use %1$stext%2$s', ['[icode][u]', '[/u][/icode]']) . "\n\n"
|
||||
. "[/list]\n"
|
||||
. $l->t('Feel free to start a new discussion or reply to existing threads. Happy posting!');
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
// Create welcome thread
|
||||
@@ -1134,38 +1258,6 @@ class SeedHelper {
|
||||
->executeStatement();
|
||||
$threadId = $qb->getLastInsertId();
|
||||
|
||||
// Create welcome post
|
||||
$welcomeContent = $l->t('Welcome to the Nextcloud Forums!') . "\n\n"
|
||||
. $l->t('This is a community-driven forum built right into your Nextcloud instance. '
|
||||
. 'Here you can discuss topics, share ideas and collaborate with other users.') . "\n\n"
|
||||
. '[b]' . $l->t('Features:') . "[/b]\n"
|
||||
. "[list]\n"
|
||||
. '[*]' . $l->t('Create and reply to threads') . "\n"
|
||||
. '[*]' . $l->t('Organize discussions by categories') . "\n"
|
||||
. '[*]' . $l->t('Use BBCode for rich text formatting') . "\n"
|
||||
. '[*]' . $l->t('Attach files from your Nextcloud storage') . "\n"
|
||||
. '[*]' . $l->t('React to posts') . "\n"
|
||||
. '[*]' . $l->t('Track read/unread threads') . "\n\n"
|
||||
. "[/list]\n"
|
||||
. '[b]' . $l->t('BBCode examples:') . "[/b]\n"
|
||||
. "[list]\n"
|
||||
. '[*][b]' . $l->t('Bold text') . '[/b] - ' . $l->t('Use %1$stext%2$s', ['[icode][b]', '[/b][/icode]']) . "\n"
|
||||
. '[*][i]' . $l->t('Italic text') . '[/i] - ' . $l->t('Use %1$stext%2$s', ['[icode][i]', '[/i][/icode]']) . "\n"
|
||||
. '[*][u]' . $l->t('Underlined text') . '[/u] - ' . $l->t('Use %1$stext%2$s', ['[icode][u]', '[/u][/icode]']) . "\n\n"
|
||||
. "[/list]\n"
|
||||
. $l->t('Feel free to start a new discussion or reply to existing threads. Happy posting!');
|
||||
|
||||
// Check if slug column still exists (for backwards compatibility with old migrations)
|
||||
// Use a query to check column existence since schema introspection APIs vary
|
||||
$hasSlugColumn = true;
|
||||
try {
|
||||
$checkQb = $db->getQueryBuilder();
|
||||
$checkQb->select('slug')->from('forum_posts')->setMaxResults(1);
|
||||
$checkQb->executeQuery()->closeCursor();
|
||||
} catch (\Exception $e) {
|
||||
$hasSlugColumn = false;
|
||||
}
|
||||
|
||||
// Build post values - slug is optional (removed in Version8)
|
||||
$qb = $db->getQueryBuilder();
|
||||
$postValues = [
|
||||
@@ -1265,8 +1357,13 @@ class SeedHelper {
|
||||
$output->info(' ✓ Created welcome thread');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
// Try to rollback if we're in a transaction - important for PostgreSQL recovery
|
||||
try {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
} catch (\Exception $rollbackEx) {
|
||||
$logger->debug('Forum seeding: Failed to rollback after welcome thread error', ['exception' => $rollbackEx->getMessage()]);
|
||||
}
|
||||
$logger->error('Forum seeding: Failed to create welcome thread', [
|
||||
'exception' => $e->getMessage(),
|
||||
|
||||
@@ -75,8 +75,7 @@ class Version15Date20260103000000 extends SimpleMigrationStep {
|
||||
* @param array $options
|
||||
*/
|
||||
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
|
||||
// Re-run seeding to ensure all required data exists
|
||||
SeedHelper::seedAll($output);
|
||||
// No-op: Seeding moved to Version16 which removes the incorrect unique constraint first
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
72
lib/Migration/Version16Date20260117000000.php
Normal file
72
lib/Migration/Version16Date20260117000000.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// SPDX-FileCopyrightText: Chen Asraf <contact@casraf.dev>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\Forum\Migration;
|
||||
|
||||
use Closure;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Version 16 Migration:
|
||||
* - Remove unique constraint on role_type column to allow multiple custom roles
|
||||
* - Re-run seeding to ensure all required data exists
|
||||
*
|
||||
* The unique constraint was incorrectly added in Version15, which prevented
|
||||
* creating more than one custom role (since all custom roles have role_type='custom').
|
||||
*/
|
||||
class Version16Date20260117000000 extends SimpleMigrationStep {
|
||||
public function __construct(
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure
|
||||
* @param array $options
|
||||
* @return ISchemaWrapper|null
|
||||
*/
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if ($schema->hasTable('forum_roles')) {
|
||||
$table = $schema->getTable('forum_roles');
|
||||
|
||||
// Remove the unique index on role_type if it exists
|
||||
// This was incorrectly added in Version15 and prevents creating multiple custom roles
|
||||
if ($table->hasIndex('forum_roles_role_type_uniq')) {
|
||||
$output->info('Forum: Removing unique constraint on role_type to allow multiple custom roles...');
|
||||
$table->dropIndex('forum_roles_role_type_uniq');
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure
|
||||
* @param array $options
|
||||
*/
|
||||
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
|
||||
// Re-run seeding to ensure all required data exists
|
||||
// Pass throwOnError=false to avoid PostgreSQL transaction abort issues
|
||||
// If seeding fails, users can run "occ forum:repair-seeds" to retry
|
||||
try {
|
||||
SeedHelper::seedAll($output, false);
|
||||
} catch (\Exception $e) {
|
||||
// This should not happen with throwOnError=false, but handle it gracefully
|
||||
$this->logger->error('Forum migration: Seeding failed unexpectedly', ['exception' => $e->getMessage()]);
|
||||
$output->warning('Forum: Seeding failed. Run "occ forum:repair-seeds" after enabling the app to complete setup.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,12 +85,30 @@ class Version1Date20251106004226 extends SimpleMigrationStep {
|
||||
$table->addIndex(['name'], 'forum_roles_name_idx');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create forum_users table (formerly forum_user_stats)
|
||||
* Note: On fresh installs, this creates forum_users directly with the final schema.
|
||||
* For progressive installs where forum_user_stats already exists,
|
||||
* SeedHelper::ensureForumUsersTable() handles the rename.
|
||||
*
|
||||
* The table structure matches what Version2 transforms it to:
|
||||
* - id: auto-increment primary key
|
||||
* - user_id: unique string
|
||||
* - signature: added in Version8
|
||||
*/
|
||||
private function createUserStatsTable(ISchemaWrapper $schema): void {
|
||||
if ($schema->hasTable('forum_user_stats')) {
|
||||
// Skip if either table already exists (handles both fresh and progressive installs)
|
||||
if ($schema->hasTable('forum_users') || $schema->hasTable('forum_user_stats')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$table = $schema->createTable('forum_user_stats');
|
||||
// Create forum_users directly with the final schema (matching Version2's transformation)
|
||||
$table = $schema->createTable('forum_users');
|
||||
$table->addColumn('id', 'bigint', [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('user_id', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 64,
|
||||
@@ -115,6 +133,10 @@ class Version1Date20251106004226 extends SimpleMigrationStep {
|
||||
'unsigned' => true,
|
||||
'default' => null,
|
||||
]);
|
||||
$table->addColumn('signature', 'text', [
|
||||
'notnull' => false,
|
||||
'default' => null,
|
||||
]);
|
||||
$table->addColumn('created_at', 'integer', [
|
||||
'notnull' => true,
|
||||
'unsigned' => true,
|
||||
@@ -123,9 +145,11 @@ class Version1Date20251106004226 extends SimpleMigrationStep {
|
||||
'notnull' => true,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->setPrimaryKey(['user_id']);
|
||||
$table->addIndex(['post_count'], 'user_stats_post_count_idx');
|
||||
$table->addIndex(['deleted_at'], 'user_stats_deleted_at_idx');
|
||||
$table->setPrimaryKey(['id']);
|
||||
$table->addUniqueIndex(['user_id'], 'forum_users_user_id_uniq');
|
||||
$table->addIndex(['post_count'], 'forum_users_post_count_idx');
|
||||
$table->addIndex(['thread_count'], 'forum_users_thread_count_idx');
|
||||
$table->addIndex(['deleted_at'], 'forum_users_deleted_at_idx');
|
||||
}
|
||||
|
||||
private function createForumUserRolesTable(ISchemaWrapper $schema): void {
|
||||
|
||||
@@ -67,18 +67,38 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
$table->addUniqueIndex(['user_id', 'thread_id'], 'thread_subs_uniq_idx');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix forum_user_stats or forum_users table structure
|
||||
* Handles both old table name (progressive installs) and new table name (fresh installs)
|
||||
*/
|
||||
private function fixForumUserStatsTable(ISchemaWrapper $schema): void {
|
||||
if (!$schema->hasTable('forum_user_stats')) {
|
||||
// Determine which table exists (handles both fresh and progressive installs)
|
||||
$tableName = null;
|
||||
if ($schema->hasTable('forum_user_stats')) {
|
||||
$tableName = 'forum_user_stats';
|
||||
} elseif ($schema->hasTable('forum_users')) {
|
||||
$tableName = 'forum_users';
|
||||
}
|
||||
|
||||
if ($tableName === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$table = $schema->getTable('forum_user_stats');
|
||||
$table = $schema->getTable($tableName);
|
||||
|
||||
// Check if already fixed (has id column)
|
||||
// Note: On fresh installs, forum_users uses user_id as primary key (no id column needed)
|
||||
// This fix is only needed for progressive installs with old forum_user_stats structure
|
||||
if ($table->hasColumn('id')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only add id column to forum_user_stats (old structure)
|
||||
// forum_users created in Version1 uses user_id as primary key and doesn't need this fix
|
||||
if ($tableName !== 'forum_user_stats') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add id column as auto-increment
|
||||
$table->addColumn('id', 'bigint', [
|
||||
'autoincrement' => true,
|
||||
@@ -123,8 +143,7 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild user stats using the old table name (forum_user_stats)
|
||||
* This is needed because Version8 hasn't renamed the table yet
|
||||
* Rebuild user stats - handles both old (forum_user_stats) and new (forum_users) table names
|
||||
*/
|
||||
private function rebuildAllUserStatsLegacy(): array {
|
||||
// Get all user IDs from Nextcloud
|
||||
@@ -133,11 +152,22 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
$users[] = $user->getUID();
|
||||
});
|
||||
|
||||
// Determine which table to use
|
||||
$tableName = $this->getUserStatsTableName();
|
||||
if ($tableName === null) {
|
||||
// No table exists yet - this shouldn't happen but handle gracefully
|
||||
return [
|
||||
'users' => count($users),
|
||||
'updated' => 0,
|
||||
'created' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$updated = 0;
|
||||
$created = 0;
|
||||
|
||||
foreach ($users as $userId) {
|
||||
$wasCreated = $this->rebuildUserStatsLegacy($userId);
|
||||
$wasCreated = $this->rebuildUserStatsLegacy($userId, $tableName);
|
||||
if ($wasCreated) {
|
||||
$created++;
|
||||
} else {
|
||||
@@ -153,9 +183,36 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild stats for a single user using the old table name
|
||||
* Get the user stats table name (handles both old and new names)
|
||||
*/
|
||||
private function rebuildUserStatsLegacy(string $userId): bool {
|
||||
private function getUserStatsTableName(): ?string {
|
||||
// Check forum_users first (new name, for fresh installs)
|
||||
// Then check forum_user_stats (old name, for progressive installs)
|
||||
try {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('user_id')->from('forum_users')->setMaxResults(1);
|
||||
$qb->executeQuery()->closeCursor();
|
||||
return 'forum_users';
|
||||
} catch (\Exception $e) {
|
||||
// Table doesn't exist, try old name
|
||||
}
|
||||
|
||||
try {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('user_id')->from('forum_user_stats')->setMaxResults(1);
|
||||
$qb->executeQuery()->closeCursor();
|
||||
return 'forum_user_stats';
|
||||
} catch (\Exception $e) {
|
||||
// Neither table exists
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild stats for a single user
|
||||
*/
|
||||
private function rebuildUserStatsLegacy(string $userId, string $tableName): bool {
|
||||
// Count non-deleted threads created by this user
|
||||
$threadQb = $this->db->getQueryBuilder();
|
||||
$threadQb->select($threadQb->func()->count('*', 'count'))
|
||||
@@ -194,10 +251,10 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
$lastPostAt = $lastPostResult->fetchOne();
|
||||
$lastPostResult->closeCursor();
|
||||
|
||||
// Check if forum user record already exists (using OLD table name)
|
||||
// Check if forum user record already exists
|
||||
$checkQb = $this->db->getQueryBuilder();
|
||||
$checkQb->select('user_id')
|
||||
->from('forum_user_stats') // OLD table name!
|
||||
->from($tableName)
|
||||
->where($checkQb->expr()->eq('user_id', $checkQb->createNamedParameter($userId)));
|
||||
$checkResult = $checkQb->executeQuery();
|
||||
$exists = $checkResult->fetch();
|
||||
@@ -206,9 +263,9 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
$timestamp = time();
|
||||
|
||||
if ($exists) {
|
||||
// Update existing record (using OLD table name)
|
||||
// Update existing record
|
||||
$updateQb = $this->db->getQueryBuilder();
|
||||
$updateQb->update('forum_user_stats') // OLD table name!
|
||||
$updateQb->update($tableName)
|
||||
->set('thread_count', $updateQb->createNamedParameter($threadCount, IQueryBuilder::PARAM_INT))
|
||||
->set('post_count', $updateQb->createNamedParameter($postCount, IQueryBuilder::PARAM_INT))
|
||||
->set('updated_at', $updateQb->createNamedParameter($timestamp, IQueryBuilder::PARAM_INT))
|
||||
@@ -221,9 +278,9 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
$updateQb->executeStatement();
|
||||
return false;
|
||||
} else {
|
||||
// Create new record (using OLD table name)
|
||||
// Create new record
|
||||
$insertQb = $this->db->getQueryBuilder();
|
||||
$insertQb->insert('forum_user_stats') // OLD table name!
|
||||
$insertQb->insert($tableName)
|
||||
->values([
|
||||
'user_id' => $insertQb->createNamedParameter($userId),
|
||||
'thread_count' => $insertQb->createNamedParameter($threadCount, IQueryBuilder::PARAM_INT),
|
||||
@@ -239,7 +296,7 @@ class Version2Date20251114222614 extends SimpleMigrationStep {
|
||||
} catch (\Exception $e) {
|
||||
// If insert fails (race condition), try updating instead
|
||||
$updateQb = $this->db->getQueryBuilder();
|
||||
$updateQb->update('forum_user_stats') // OLD table name!
|
||||
$updateQb->update($tableName)
|
||||
->set('thread_count', $updateQb->createNamedParameter($threadCount, IQueryBuilder::PARAM_INT))
|
||||
->set('post_count', $updateQb->createNamedParameter($postCount, IQueryBuilder::PARAM_INT))
|
||||
->set('updated_at', $updateQb->createNamedParameter($timestamp, IQueryBuilder::PARAM_INT))
|
||||
|
||||
@@ -43,9 +43,18 @@ class Version8Date20251128000000 extends SimpleMigrationStep {
|
||||
}
|
||||
}
|
||||
|
||||
// Add signature column to user stats
|
||||
if ($schema->hasTable('forum_user_stats')) {
|
||||
$table = $schema->getTable('forum_user_stats');
|
||||
// Add signature column to forum_users table (handles both old and new table names)
|
||||
// On fresh installs: forum_users is created with signature column in Version1
|
||||
// On progressive installs: forum_user_stats may still exist and needs signature added
|
||||
$userTableName = null;
|
||||
if ($schema->hasTable('forum_users')) {
|
||||
$userTableName = 'forum_users';
|
||||
} elseif ($schema->hasTable('forum_user_stats')) {
|
||||
$userTableName = 'forum_user_stats';
|
||||
}
|
||||
|
||||
if ($userTableName !== null) {
|
||||
$table = $schema->getTable($userTableName);
|
||||
|
||||
if (!$table->hasColumn('signature')) {
|
||||
$table->addColumn('signature', 'text', [
|
||||
|
||||
@@ -17,6 +17,9 @@ class UserPreferencesService {
|
||||
/** Preference key for auto-subscribing to created threads */
|
||||
public const PREF_AUTO_SUBSCRIBE_CREATED_THREADS = 'auto_subscribe_created_threads';
|
||||
|
||||
/** Preference key for auto-subscribing to threads when replying */
|
||||
public const PREF_AUTO_SUBSCRIBE_REPLIED_THREADS = 'auto_subscribe_replied_threads';
|
||||
|
||||
/** Preference key for upload directory path */
|
||||
public const PREF_UPLOAD_DIRECTORY = 'upload_directory';
|
||||
|
||||
@@ -26,6 +29,7 @@ class UserPreferencesService {
|
||||
/** @var array<string, mixed> Default preference values */
|
||||
private const DEFAULTS = [
|
||||
self::PREF_AUTO_SUBSCRIBE_CREATED_THREADS => true,
|
||||
self::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS => false,
|
||||
self::PREF_UPLOAD_DIRECTORY => 'Forum',
|
||||
self::PREF_SIGNATURE => '',
|
||||
];
|
||||
@@ -33,6 +37,7 @@ class UserPreferencesService {
|
||||
/** @var array<string> List of valid preference keys */
|
||||
private const VALID_KEYS = [
|
||||
self::PREF_AUTO_SUBSCRIBE_CREATED_THREADS,
|
||||
self::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS,
|
||||
self::PREF_UPLOAD_DIRECTORY,
|
||||
self::PREF_SIGNATURE,
|
||||
];
|
||||
|
||||
@@ -2729,6 +2729,13 @@
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"headerId": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"nullable": true,
|
||||
"default": null,
|
||||
"description": "Category header ID"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
|
||||
10
package.json
10
package.json
@@ -28,7 +28,7 @@
|
||||
"@nextcloud/l10n": "^3.4.1",
|
||||
"@nextcloud/router": "^3.1.0",
|
||||
"@nextcloud/vite-config": "2.3.5",
|
||||
"@nextcloud/vue": "^9.3.1",
|
||||
"@nextcloud/vue": "^9.3.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"linkifyjs": "^4.3.2",
|
||||
"vue": "^3.5.26",
|
||||
@@ -43,16 +43,16 @@
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"eslint": "^9.39.2",
|
||||
"happy-dom": "^20.0.11",
|
||||
"happy-dom": "^20.1.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.2.7",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-vue": "^1.1.6",
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"sass": "^1.97.1",
|
||||
"sass-embedded": "^1.97.1",
|
||||
"sass": "^1.97.2",
|
||||
"sass-embedded": "^1.97.2",
|
||||
"typescript": "5.9.2",
|
||||
"typescript-eslint": "^8.51.0",
|
||||
"typescript-eslint": "^8.52.0",
|
||||
"vite": "^6.4.1",
|
||||
"vite-plugin-checker": "^0.12.0",
|
||||
"vitest": "^4.0.16",
|
||||
|
||||
806
pnpm-lock.yaml
generated
806
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -445,6 +445,12 @@ export default defineComponent({
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
// Images ([img]) - auto-scale to fit content width
|
||||
:deep(img) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
||||
@@ -304,6 +304,12 @@ export default defineComponent({
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
// Images - auto-scale to fit content width
|
||||
:deep(img) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -54,12 +54,12 @@ describe('bbcode utilities', () => {
|
||||
expect(result.cursorPosition).toBe(18)
|
||||
})
|
||||
|
||||
it('inserts template at cursor when no selection', () => {
|
||||
it('inserts template at cursor when no selection, cursor between tags', () => {
|
||||
const selection: TextSelection = { text: 'Hello world', start: 6, end: 6 }
|
||||
const result = applyBBCodeTemplate(selection, { template: '[b]{text}[/b]' })
|
||||
|
||||
expect(result.text).toBe('Hello [b][/b]world')
|
||||
expect(result.cursorPosition).toBe(13) // cursor after [/b]
|
||||
expect(result.cursorPosition).toBe(9) // cursor between tags (after [b]) for immediate typing
|
||||
})
|
||||
|
||||
it('uses fallback text when no selection', () => {
|
||||
@@ -130,6 +130,83 @@ describe('bbcode utilities', () => {
|
||||
|
||||
expect(result.text).toBe('[size=]text[/size]')
|
||||
})
|
||||
|
||||
describe('cursor positioning', () => {
|
||||
it('places cursor between tags when no selection (simple tag)', () => {
|
||||
const selection: TextSelection = { text: '', start: 0, end: 0 }
|
||||
const result = applyBBCodeTemplate(selection, { template: '[b]{text}[/b]' })
|
||||
|
||||
expect(result.text).toBe('[b][/b]')
|
||||
expect(result.cursorPosition).toBe(3) // right after [b]
|
||||
})
|
||||
|
||||
it('places cursor between tags when no selection (tag with value)', () => {
|
||||
const selection: TextSelection = { text: '', start: 0, end: 0 }
|
||||
const result = applyBBCodeTemplate(selection, {
|
||||
template: '[url={value}]{text}[/url]',
|
||||
value: 'http://example.com',
|
||||
})
|
||||
|
||||
expect(result.text).toBe('[url=http://example.com][/url]')
|
||||
expect(result.cursorPosition).toBe(24) // right after the ]
|
||||
})
|
||||
|
||||
it('places cursor between tags when no selection (color tag)', () => {
|
||||
const selection: TextSelection = { text: '', start: 0, end: 0 }
|
||||
const result = applyBBCodeTemplate(selection, {
|
||||
template: '[color={value}]{text}[/color]',
|
||||
value: '#ff0000',
|
||||
})
|
||||
|
||||
expect(result.text).toBe('[color=#ff0000][/color]')
|
||||
expect(result.cursorPosition).toBe(15) // right after [color=#ff0000]
|
||||
})
|
||||
|
||||
it('places cursor after closing tag when selection exists', () => {
|
||||
const selection: TextSelection = { text: 'hello', start: 0, end: 5 }
|
||||
const result = applyBBCodeTemplate(selection, { template: '[b]{text}[/b]' })
|
||||
|
||||
expect(result.text).toBe('[b]hello[/b]')
|
||||
expect(result.cursorPosition).toBe(12) // after [/b]
|
||||
})
|
||||
|
||||
it('places cursor after closing tag when fallback text is used', () => {
|
||||
const selection: TextSelection = { text: '', start: 0, end: 0 }
|
||||
const result = applyBBCodeTemplate(selection, {
|
||||
template: '[b]{text}[/b]',
|
||||
fallbackText: 'bold',
|
||||
})
|
||||
|
||||
expect(result.text).toBe('[b]bold[/b]')
|
||||
expect(result.cursorPosition).toBe(11) // after [/b]
|
||||
})
|
||||
|
||||
it('places cursor between tags for quote tag without selection', () => {
|
||||
const selection: TextSelection = { text: 'Some text', start: 9, end: 9 }
|
||||
const result = applyBBCodeTemplate(selection, { template: '[quote]{text}[/quote]' })
|
||||
|
||||
expect(result.text).toBe('Some text[quote][/quote]')
|
||||
expect(result.cursorPosition).toBe(16) // right after [quote]
|
||||
})
|
||||
|
||||
it('places cursor between tags for code tag without selection', () => {
|
||||
const selection: TextSelection = { text: '', start: 0, end: 0 }
|
||||
const result = applyBBCodeTemplate(selection, { template: '[code]{text}[/code]' })
|
||||
|
||||
expect(result.text).toBe('[code][/code]')
|
||||
expect(result.cursorPosition).toBe(6) // right after [code]
|
||||
})
|
||||
|
||||
it('handles template with newlines - cursor between tags', () => {
|
||||
const selection: TextSelection = { text: '', start: 0, end: 0 }
|
||||
const result = applyBBCodeTemplate(selection, {
|
||||
template: '[list]\n[*]{text}\n[/list]',
|
||||
})
|
||||
|
||||
expect(result.text).toBe('[list]\n[*]\n[/list]')
|
||||
expect(result.cursorPosition).toBe(10) // right after [*]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('insertTextAtSelection', () => {
|
||||
@@ -183,12 +260,12 @@ describe('bbcode utilities', () => {
|
||||
expect(result.cursorPosition).toBe(18)
|
||||
})
|
||||
|
||||
it('inserts empty tags when no selection', () => {
|
||||
it('inserts empty tags when no selection, cursor between tags', () => {
|
||||
const selection: TextSelection = { text: 'Hello', start: 5, end: 5 }
|
||||
const result = wrapSelection(selection, '[i]', '[/i]')
|
||||
|
||||
expect(result.text).toBe('Hello[i][/i]')
|
||||
expect(result.cursorPosition).toBe(12)
|
||||
expect(result.cursorPosition).toBe(8) // cursor between tags (after [i]) for immediate typing
|
||||
})
|
||||
|
||||
it('uses fallback text when no selection', () => {
|
||||
@@ -419,12 +496,12 @@ describe('bbcode utilities', () => {
|
||||
expect(result.text).toBe('Use [code][b][/code] for bold')
|
||||
})
|
||||
|
||||
it('handles empty string', () => {
|
||||
it('handles empty string, cursor between tags', () => {
|
||||
const selection: TextSelection = { text: '', start: 0, end: 0 }
|
||||
const result = wrapSelection(selection, '[b]', '[/b]')
|
||||
|
||||
expect(result.text).toBe('[b][/b]')
|
||||
expect(result.cursorPosition).toBe(7)
|
||||
expect(result.cursorPosition).toBe(3) // cursor between tags (after [b]) for immediate typing
|
||||
})
|
||||
|
||||
it('handles very long text', () => {
|
||||
|
||||
@@ -74,6 +74,11 @@ export function getSelectedText(selection: TextSelection): string {
|
||||
* 2. Replaces the selected text with the BBCode-wrapped version
|
||||
* 3. Returns the new text and cursor position
|
||||
*
|
||||
* Cursor positioning:
|
||||
* - With selected text or fallback text: cursor is placed after the closing tag
|
||||
* - Without any content: cursor is placed between the opening and closing tags
|
||||
* so the user can immediately start typing
|
||||
*
|
||||
* Template placeholders:
|
||||
* - {text}: Replaced with selected text (or fallbackText if nothing selected)
|
||||
* - {value}: Replaced with the provided value (for tags like [url=...], [color=...])
|
||||
@@ -83,7 +88,7 @@ export function getSelectedText(selection: TextSelection): string {
|
||||
* @returns The insertion result with new text and cursor position
|
||||
*
|
||||
* @example
|
||||
* // Simple wrap with [b] tags
|
||||
* // Simple wrap with [b] tags - cursor after closing tag
|
||||
* applyBBCodeTemplate(
|
||||
* { text: 'Hello world', start: 6, end: 11 },
|
||||
* { template: '[b]{text}[/b]' }
|
||||
@@ -91,6 +96,14 @@ export function getSelectedText(selection: TextSelection): string {
|
||||
* // Returns: { text: 'Hello [b]world[/b]', cursorPosition: 18 }
|
||||
*
|
||||
* @example
|
||||
* // No selection - cursor between tags for immediate typing
|
||||
* applyBBCodeTemplate(
|
||||
* { text: 'Hello ', start: 6, end: 6 },
|
||||
* { template: '[b]{text}[/b]' }
|
||||
* )
|
||||
* // Returns: { text: 'Hello [b][/b]', cursorPosition: 9 }
|
||||
*
|
||||
* @example
|
||||
* // URL with value
|
||||
* applyBBCodeTemplate(
|
||||
* { text: 'Check this', start: 6, end: 10 },
|
||||
@@ -115,7 +128,27 @@ export function applyBBCodeTemplate(
|
||||
.replace('{text}', contentText)
|
||||
|
||||
const newText = beforeText + insertText + afterText
|
||||
const cursorPosition = beforeText.length + insertText.length
|
||||
|
||||
// Calculate cursor position:
|
||||
// - If there's content (selected text or fallback), place cursor after the closing tag
|
||||
// - If no content, place cursor between tags (at the {text} placeholder position)
|
||||
// so user can immediately start typing
|
||||
let cursorPosition: number
|
||||
if (contentText) {
|
||||
// Cursor after the entire inserted text (after closing tag)
|
||||
cursorPosition = beforeText.length + insertText.length
|
||||
} else {
|
||||
// No content - find where {text} was in the template and place cursor there
|
||||
const templateWithValue = template.template.replace('{value}', template.value || '')
|
||||
const textPlaceholderIndex = templateWithValue.indexOf('{text}')
|
||||
if (textPlaceholderIndex !== -1) {
|
||||
// Place cursor where {text} placeholder was (between tags)
|
||||
cursorPosition = beforeText.length + textPlaceholderIndex
|
||||
} else {
|
||||
// Fallback: place cursor at end of inserted text
|
||||
cursorPosition = beforeText.length + insertText.length
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
text: newText,
|
||||
|
||||
@@ -43,9 +43,16 @@
|
||||
|
||||
<div class="preference-item">
|
||||
<NcCheckboxRadioSwitch v-model="formData.auto_subscribe_created_threads">
|
||||
{{ strings.autoSubscribeLabel }}
|
||||
{{ strings.autoSubscribeCreatedLabel }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<p class="preference-hint">{{ strings.autoSubscribeHint }}</p>
|
||||
<p class="preference-hint">{{ strings.autoSubscribeCreatedHint }}</p>
|
||||
</div>
|
||||
|
||||
<div class="preference-item">
|
||||
<NcCheckboxRadioSwitch v-model="formData.auto_subscribe_replied_threads">
|
||||
{{ strings.autoSubscribeRepliedLabel }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<p class="preference-hint">{{ strings.autoSubscribeRepliedHint }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -134,6 +141,7 @@ import { getFilePickerBuilder, FilePickerType } from '@nextcloud/dialogs'
|
||||
|
||||
interface UserPreferences {
|
||||
auto_subscribe_created_threads: boolean
|
||||
auto_subscribe_replied_threads: boolean
|
||||
upload_directory: string
|
||||
signature: string
|
||||
}
|
||||
@@ -162,11 +170,13 @@ export default defineComponent({
|
||||
error: null as string | null,
|
||||
originalData: {
|
||||
auto_subscribe_created_threads: true,
|
||||
auto_subscribe_replied_threads: false,
|
||||
upload_directory: 'Forum',
|
||||
signature: '',
|
||||
} as UserPreferences,
|
||||
formData: {
|
||||
auto_subscribe_created_threads: true,
|
||||
auto_subscribe_replied_threads: false,
|
||||
upload_directory: 'Forum',
|
||||
signature: '',
|
||||
} as UserPreferences,
|
||||
@@ -180,11 +190,16 @@ export default defineComponent({
|
||||
retry: t('forum', 'Retry'),
|
||||
subscriptionsTitle: t('forum', 'Notifications'),
|
||||
subscriptionsDesc: t('forum', 'Configure how you receive notifications'),
|
||||
autoSubscribeLabel: t('forum', 'Auto-subscribe to threads I create'),
|
||||
autoSubscribeHint: t(
|
||||
autoSubscribeCreatedLabel: t('forum', 'Auto-subscribe to threads I create'),
|
||||
autoSubscribeCreatedHint: t(
|
||||
'forum',
|
||||
'When enabled, you will automatically receive notifications for replies to threads you create',
|
||||
),
|
||||
autoSubscribeRepliedLabel: t('forum', 'Auto-subscribe to threads I reply to'),
|
||||
autoSubscribeRepliedHint: t(
|
||||
'forum',
|
||||
'When enabled, you will automatically receive notifications for new replies in threads you have replied to',
|
||||
),
|
||||
filesTitle: t('forum', 'Files'),
|
||||
filesDesc: t('forum', 'Configure file upload settings'),
|
||||
uploadDirectoryLabel: t('forum', 'Upload directory'),
|
||||
@@ -212,6 +227,8 @@ export default defineComponent({
|
||||
return (
|
||||
this.formData.auto_subscribe_created_threads !==
|
||||
this.originalData.auto_subscribe_created_threads ||
|
||||
this.formData.auto_subscribe_replied_threads !==
|
||||
this.originalData.auto_subscribe_replied_threads ||
|
||||
this.formData.upload_directory !== this.originalData.upload_directory ||
|
||||
this.formData.signature !== this.originalData.signature
|
||||
)
|
||||
|
||||
406
tests/integration/Migration/SeedHelperTest.php
Normal file
406
tests/integration/Migration/SeedHelperTest.php
Normal file
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// SPDX-FileCopyrightText: Chen Asraf <contact@casraf.dev>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace OCA\Forum\Tests\Integration\Migration;
|
||||
|
||||
use OCA\Forum\Db\Role;
|
||||
use OCA\Forum\Migration\SeedHelper;
|
||||
use OCP\IDBConnection;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Integration tests for SeedHelper
|
||||
*
|
||||
* These tests run against a real database to ensure seeding works correctly
|
||||
* on both MySQL and PostgreSQL. They verify:
|
||||
* - Seeding creates required data on a clean database
|
||||
* - Seeding is idempotent (running twice doesn't cause errors)
|
||||
* - Individual seed operations can fail without breaking others
|
||||
* - Transaction handling works correctly on PostgreSQL
|
||||
*
|
||||
* Note: These tests require the Forum app tables to exist. They will be skipped
|
||||
* if run in an environment where migrations haven't been run (e.g., local dev with SQLite).
|
||||
* In CI, the app is properly installed with tables before tests run.
|
||||
*
|
||||
* @group integration
|
||||
* @group database
|
||||
*/
|
||||
class SeedHelperTest extends TestCase {
|
||||
private IDBConnection $db;
|
||||
private bool $tablesExist = false;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->db = \OC::$server->get(IDBConnection::class);
|
||||
|
||||
// Check if forum tables exist (they might not in local dev environment)
|
||||
$this->tablesExist = $this->checkTablesExist();
|
||||
|
||||
if (!$this->tablesExist) {
|
||||
$this->markTestSkipped('Forum tables do not exist. Run these tests in CI or with a fully installed Nextcloud instance.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the forum tables exist in the database
|
||||
*/
|
||||
private function checkTablesExist(): bool {
|
||||
try {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('id')
|
||||
->from('forum_roles')
|
||||
->setMaxResults(1);
|
||||
$qb->executeQuery()->closeCursor();
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
// Clean up test data after each test
|
||||
$this->cleanupTestData();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all forum data to start fresh
|
||||
*/
|
||||
private function cleanupTestData(): void {
|
||||
$tables = [
|
||||
'forum_thread_subs',
|
||||
'forum_read_markers',
|
||||
'forum_reactions',
|
||||
'forum_posts',
|
||||
'forum_threads',
|
||||
'forum_category_perms',
|
||||
'forum_categories',
|
||||
'forum_cat_headers',
|
||||
'forum_user_roles',
|
||||
'forum_users',
|
||||
'forum_bbcodes',
|
||||
'forum_roles',
|
||||
];
|
||||
|
||||
foreach ($tables as $table) {
|
||||
try {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete($table)->executeStatement();
|
||||
} catch (\Exception $e) {
|
||||
// Table might not exist, ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedAll creates all required data on a clean database
|
||||
*/
|
||||
public function testSeedAllCreatesRequiredData(): void {
|
||||
// Clean state
|
||||
$this->cleanupTestData();
|
||||
|
||||
// Run seeding
|
||||
SeedHelper::seedAll(null, false);
|
||||
|
||||
// Verify roles were created
|
||||
$this->assertRolesExist();
|
||||
|
||||
// Verify category headers were created
|
||||
$this->assertCategoryHeadersExist();
|
||||
|
||||
// Verify categories were created
|
||||
$this->assertCategoriesExist();
|
||||
|
||||
// Verify BBCodes were created
|
||||
$this->assertBBCodesExist();
|
||||
|
||||
// Verify category permissions were created
|
||||
$this->assertCategoryPermissionsExist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedAll is idempotent - running twice doesn't cause errors
|
||||
*/
|
||||
public function testSeedAllIsIdempotent(): void {
|
||||
// Clean state
|
||||
$this->cleanupTestData();
|
||||
|
||||
// Run seeding twice
|
||||
SeedHelper::seedAll(null, false);
|
||||
SeedHelper::seedAll(null, false);
|
||||
|
||||
// Verify data exists (and isn't duplicated)
|
||||
$this->assertRolesExist();
|
||||
$this->assertNoDuplicateRoles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedDefaultRoles creates all four roles
|
||||
*/
|
||||
public function testSeedDefaultRolesCreatesAllRoles(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
SeedHelper::seedDefaultRoles(null);
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('role_type')
|
||||
->from('forum_roles');
|
||||
$result = $qb->executeQuery();
|
||||
$roles = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
|
||||
$roleTypes = array_column($roles, 'role_type');
|
||||
|
||||
$this->assertContains(Role::ROLE_TYPE_ADMIN, $roleTypes);
|
||||
$this->assertContains(Role::ROLE_TYPE_MODERATOR, $roleTypes);
|
||||
$this->assertContains(Role::ROLE_TYPE_DEFAULT, $roleTypes);
|
||||
$this->assertContains(Role::ROLE_TYPE_GUEST, $roleTypes);
|
||||
$this->assertCount(4, $roles, 'Should have exactly 4 roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedDefaultRoles is idempotent
|
||||
*/
|
||||
public function testSeedDefaultRolesIsIdempotent(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
SeedHelper::seedDefaultRoles(null);
|
||||
SeedHelper::seedDefaultRoles(null);
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select($qb->func()->count('*', 'count'))
|
||||
->from('forum_roles');
|
||||
$result = $qb->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertEquals(4, (int)$row['count'], 'Should still have exactly 4 roles after running twice');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedCategoryHeaders creates the General header
|
||||
*/
|
||||
public function testSeedCategoryHeadersCreatesHeader(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
SeedHelper::seedCategoryHeaders(null);
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from('forum_cat_headers');
|
||||
$result = $qb->executeQuery();
|
||||
$headers = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertCount(1, $headers);
|
||||
$this->assertNotEmpty($headers[0]['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedDefaultCategories creates categories
|
||||
*/
|
||||
public function testSeedDefaultCategoriesCreatesCategories(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
// Categories need headers first
|
||||
SeedHelper::seedCategoryHeaders(null);
|
||||
SeedHelper::seedDefaultCategories(null);
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('slug')
|
||||
->from('forum_categories');
|
||||
$result = $qb->executeQuery();
|
||||
$categories = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
|
||||
$slugs = array_column($categories, 'slug');
|
||||
|
||||
$this->assertContains('general-discussions', $slugs);
|
||||
$this->assertContains('support', $slugs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedDefaultBBCodes creates all default BBCodes
|
||||
*/
|
||||
public function testSeedDefaultBBCodesCreatesBBCodes(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
SeedHelper::seedDefaultBBCodes(null);
|
||||
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('tag')
|
||||
->from('forum_bbcodes');
|
||||
$result = $qb->executeQuery();
|
||||
$bbcodes = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
|
||||
$tags = array_column($bbcodes, 'tag');
|
||||
|
||||
$this->assertContains('icode', $tags);
|
||||
$this->assertContains('spoiler', $tags);
|
||||
$this->assertContains('attachment', $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seeding continues after individual operation failure
|
||||
* This is crucial for PostgreSQL where transaction abort cascades
|
||||
*/
|
||||
public function testSeedingContinuesAfterIndividualFailure(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
// Create a partial state - roles exist but categories don't have permissions
|
||||
SeedHelper::seedDefaultRoles(null);
|
||||
SeedHelper::seedCategoryHeaders(null);
|
||||
SeedHelper::seedDefaultCategories(null);
|
||||
// Skip category permissions intentionally
|
||||
|
||||
// Now run full seedAll - it should complete remaining operations
|
||||
SeedHelper::seedAll(null, false);
|
||||
|
||||
// Verify BBCodes were still created despite any issues
|
||||
$this->assertBBCodesExist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that seedWelcomeThread creates thread and post
|
||||
*/
|
||||
public function testSeedWelcomeThreadCreatesContent(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
// Welcome thread needs roles, headers, and categories
|
||||
SeedHelper::seedDefaultRoles(null);
|
||||
SeedHelper::seedCategoryHeaders(null);
|
||||
SeedHelper::seedDefaultCategories(null);
|
||||
SeedHelper::seedWelcomeThread(null);
|
||||
|
||||
// Verify thread exists
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('id')
|
||||
->from('forum_threads')
|
||||
->where($qb->expr()->eq('slug', $qb->createNamedParameter('welcome-to-nextcloud-forums')));
|
||||
$result = $qb->executeQuery();
|
||||
$thread = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertNotFalse($thread, 'Welcome thread should exist');
|
||||
|
||||
// Verify post exists
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('id')
|
||||
->from('forum_posts')
|
||||
->where($qb->expr()->eq('thread_id', $qb->createNamedParameter($thread['id'])));
|
||||
$result = $qb->executeQuery();
|
||||
$post = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertNotFalse($post, 'Welcome post should exist');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that throwOnError parameter works correctly
|
||||
*/
|
||||
public function testThrowOnErrorParameter(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
// With throwOnError=false, should not throw even if something fails
|
||||
// (though in a clean state nothing should fail)
|
||||
SeedHelper::seedAll(null, false);
|
||||
|
||||
// Verify it completed
|
||||
$this->assertRolesExist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test database connection recovery between operations
|
||||
* This specifically tests the PostgreSQL transaction abort fix
|
||||
*/
|
||||
public function testConnectionRecoveryBetweenOperations(): void {
|
||||
$this->cleanupTestData();
|
||||
|
||||
// Run seedAll which now includes connection recovery
|
||||
SeedHelper::seedAll(null, false);
|
||||
|
||||
// If we got here without exception, connection recovery worked
|
||||
// Verify data exists
|
||||
$this->assertRolesExist();
|
||||
$this->assertCategoryHeadersExist();
|
||||
$this->assertCategoriesExist();
|
||||
$this->assertBBCodesExist();
|
||||
}
|
||||
|
||||
// ========== Helper assertions ==========
|
||||
|
||||
private function assertRolesExist(): void {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select($qb->func()->count('*', 'count'))
|
||||
->from('forum_roles');
|
||||
$result = $qb->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertGreaterThanOrEqual(4, (int)$row['count'], 'Should have at least 4 roles');
|
||||
}
|
||||
|
||||
private function assertNoDuplicateRoles(): void {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('role_type')
|
||||
->selectAlias($qb->func()->count('*'), 'count')
|
||||
->from('forum_roles')
|
||||
->groupBy('role_type');
|
||||
$result = $qb->executeQuery();
|
||||
$groups = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$this->assertEquals(1, (int)$group['count'], "Role type '{$group['role_type']}' should not be duplicated");
|
||||
}
|
||||
}
|
||||
|
||||
private function assertCategoryHeadersExist(): void {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select($qb->func()->count('*', 'count'))
|
||||
->from('forum_cat_headers');
|
||||
$result = $qb->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertGreaterThanOrEqual(1, (int)$row['count'], 'Should have at least 1 category header');
|
||||
}
|
||||
|
||||
private function assertCategoriesExist(): void {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select($qb->func()->count('*', 'count'))
|
||||
->from('forum_categories');
|
||||
$result = $qb->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertGreaterThanOrEqual(2, (int)$row['count'], 'Should have at least 2 categories');
|
||||
}
|
||||
|
||||
private function assertBBCodesExist(): void {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select($qb->func()->count('*', 'count'))
|
||||
->from('forum_bbcodes');
|
||||
$result = $qb->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertGreaterThanOrEqual(3, (int)$row['count'], 'Should have at least 3 BBCodes');
|
||||
}
|
||||
|
||||
private function assertCategoryPermissionsExist(): void {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select($qb->func()->count('*', 'count'))
|
||||
->from('forum_category_perms');
|
||||
$result = $qb->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertGreaterThanOrEqual(1, (int)$row['count'], 'Should have at least 1 category permission');
|
||||
}
|
||||
}
|
||||
21
tests/phpunit.integration.xml
Normal file
21
tests/phpunit.integration.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
bootstrap="bootstrap.php"
|
||||
timeoutForSmallTests="900"
|
||||
timeoutForMediumTests="900"
|
||||
timeoutForLargeTests="900"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Forum Integration Tests">
|
||||
<directory suffix="Test.php">integration</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../appinfo</directory>
|
||||
<directory suffix=".php">../lib</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
@@ -225,7 +225,7 @@ class CategoryControllerTest extends TestCase {
|
||||
return $updatedCategory;
|
||||
});
|
||||
|
||||
$response = $this->controller->update($categoryId, $newName);
|
||||
$response = $this->controller->update($categoryId, null, $newName);
|
||||
|
||||
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
||||
$data = $response->getData();
|
||||
@@ -256,11 +256,37 @@ class CategoryControllerTest extends TestCase {
|
||||
return $updatedCategory;
|
||||
});
|
||||
|
||||
$response = $this->controller->update($categoryId, $newName, $newDescription, $newSlug, $newSortOrder);
|
||||
$response = $this->controller->update($categoryId, null, $newName, $newDescription, $newSlug, $newSortOrder);
|
||||
|
||||
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
||||
}
|
||||
|
||||
public function testUpdateCategoryHeaderId(): void {
|
||||
$categoryId = 1;
|
||||
$originalHeaderId = 1;
|
||||
$newHeaderId = 2;
|
||||
|
||||
$category = $this->createCategory($categoryId, $originalHeaderId, 'Test Category');
|
||||
|
||||
$this->categoryMapper->expects($this->once())
|
||||
->method('find')
|
||||
->with($categoryId)
|
||||
->willReturn($category);
|
||||
|
||||
$this->categoryMapper->expects($this->once())
|
||||
->method('update')
|
||||
->willReturnCallback(function ($updatedCategory) use ($newHeaderId) {
|
||||
$this->assertEquals($newHeaderId, $updatedCategory->getHeaderId());
|
||||
return $updatedCategory;
|
||||
});
|
||||
|
||||
$response = $this->controller->update($categoryId, $newHeaderId);
|
||||
|
||||
$this->assertEquals(Http::STATUS_OK, $response->getStatus());
|
||||
$data = $response->getData();
|
||||
$this->assertEquals($newHeaderId, $data['headerId']);
|
||||
}
|
||||
|
||||
public function testUpdateCategoryReturnsNotFoundWhenCategoryDoesNotExist(): void {
|
||||
$categoryId = 999;
|
||||
|
||||
@@ -269,7 +295,7 @@ class CategoryControllerTest extends TestCase {
|
||||
->with($categoryId)
|
||||
->willThrowException(new DoesNotExistException('Category not found'));
|
||||
|
||||
$response = $this->controller->update($categoryId, 'New Name');
|
||||
$response = $this->controller->update($categoryId, null, 'New Name');
|
||||
|
||||
$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
|
||||
$this->assertEquals(['error' => 'Category not found'], $response->getData());
|
||||
|
||||
@@ -20,11 +20,13 @@ use OCA\Forum\Db\ReadMarker;
|
||||
use OCA\Forum\Db\ReadMarkerMapper;
|
||||
use OCA\Forum\Db\Thread;
|
||||
use OCA\Forum\Db\ThreadMapper;
|
||||
use OCA\Forum\Db\ThreadSubscriptionMapper;
|
||||
use OCA\Forum\Service\BBCodeService;
|
||||
use OCA\Forum\Service\NotificationService;
|
||||
use OCA\Forum\Service\PermissionService;
|
||||
use OCA\Forum\Service\PostEnrichmentService;
|
||||
use OCA\Forum\Service\PostHistoryService;
|
||||
use OCA\Forum\Service\UserPreferencesService;
|
||||
use OCA\Forum\Service\UserService;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Http;
|
||||
@@ -63,6 +65,10 @@ class PostControllerTest extends TestCase {
|
||||
private PostHistoryService $postHistoryService;
|
||||
/** @var UserService&MockObject */
|
||||
private UserService $userService;
|
||||
/** @var UserPreferencesService&MockObject */
|
||||
private UserPreferencesService $userPreferencesService;
|
||||
/** @var ThreadSubscriptionMapper&MockObject */
|
||||
private ThreadSubscriptionMapper $threadSubscriptionMapper;
|
||||
/** @var IUserSession&MockObject */
|
||||
private IUserSession $userSession;
|
||||
/** @var LoggerInterface&MockObject */
|
||||
@@ -85,6 +91,8 @@ class PostControllerTest extends TestCase {
|
||||
$this->postEnrichmentService = $this->createMock(PostEnrichmentService::class);
|
||||
$this->postHistoryService = $this->createMock(PostHistoryService::class);
|
||||
$this->userService = $this->createMock(UserService::class);
|
||||
$this->userPreferencesService = $this->createMock(UserPreferencesService::class);
|
||||
$this->threadSubscriptionMapper = $this->createMock(ThreadSubscriptionMapper::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
@@ -113,6 +121,8 @@ class PostControllerTest extends TestCase {
|
||||
$this->postEnrichmentService,
|
||||
$this->postHistoryService,
|
||||
$this->userService,
|
||||
$this->userPreferencesService,
|
||||
$this->threadSubscriptionMapper,
|
||||
$this->userSession,
|
||||
$this->logger
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ class UserPreferencesServiceTest extends TestCase {
|
||||
$userId = 'user1';
|
||||
|
||||
// Only config-based preferences (signature is from forum_users)
|
||||
$this->config->expects($this->exactly(2))
|
||||
$this->config->expects($this->exactly(3))
|
||||
->method('getUserValue')
|
||||
->willReturnCallback(function ($uid, $appId, $key, $default) use ($userId) {
|
||||
$this->assertEquals($userId, $uid);
|
||||
@@ -50,6 +50,7 @@ class UserPreferencesServiceTest extends TestCase {
|
||||
|
||||
return match ($key) {
|
||||
UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS => 'true',
|
||||
UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS => 'false',
|
||||
UserPreferencesService::PREF_UPLOAD_DIRECTORY => 'Forum',
|
||||
default => $default,
|
||||
};
|
||||
@@ -58,8 +59,9 @@ class UserPreferencesServiceTest extends TestCase {
|
||||
$result = $this->service->getAllPreferences($userId);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertTrue($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS]);
|
||||
$this->assertFalse($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS]);
|
||||
$this->assertEquals('Forum', $result[UserPreferencesService::PREF_UPLOAD_DIRECTORY]);
|
||||
$this->assertEquals('', $result[UserPreferencesService::PREF_SIGNATURE]);
|
||||
}
|
||||
@@ -144,7 +146,7 @@ class UserPreferencesServiceTest extends TestCase {
|
||||
}
|
||||
});
|
||||
|
||||
$this->config->expects($this->exactly(2))
|
||||
$this->config->expects($this->exactly(3))
|
||||
->method('getUserValue')
|
||||
->willReturnCallback(function ($uid, $appId, $key, $default) use ($userId) {
|
||||
$this->assertEquals($userId, $uid);
|
||||
@@ -152,6 +154,7 @@ class UserPreferencesServiceTest extends TestCase {
|
||||
|
||||
return match ($key) {
|
||||
UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS => 'false',
|
||||
UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS => 'false',
|
||||
UserPreferencesService::PREF_UPLOAD_DIRECTORY => 'Documents',
|
||||
default => $default,
|
||||
};
|
||||
@@ -160,8 +163,9 @@ class UserPreferencesServiceTest extends TestCase {
|
||||
$result = $this->service->updatePreferences($userId, $preferences);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertCount(4, $result);
|
||||
$this->assertFalse($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS]);
|
||||
$this->assertFalse($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS]);
|
||||
$this->assertEquals('Documents', $result[UserPreferencesService::PREF_UPLOAD_DIRECTORY]);
|
||||
$this->assertEquals('', $result[UserPreferencesService::PREF_SIGNATURE]);
|
||||
}
|
||||
|
||||
12
vendor-bin/cs-fixer/composer.lock
generated
12
vendor-bin/cs-fixer/composer.lock
generated
@@ -106,16 +106,16 @@
|
||||
},
|
||||
{
|
||||
"name": "php-cs-fixer/shim",
|
||||
"version": "v3.92.4",
|
||||
"version": "v3.92.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/shim.git",
|
||||
"reference": "42511441af95955f9d40312f83da870018f0fb10"
|
||||
"reference": "b13b4ad0a1daa80cf036c70488e86516928a5af0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/42511441af95955f9d40312f83da870018f0fb10",
|
||||
"reference": "42511441af95955f9d40312f83da870018f0fb10",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/b13b4ad0a1daa80cf036c70488e86516928a5af0",
|
||||
"reference": "b13b4ad0a1daa80cf036c70488e86516928a5af0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -152,9 +152,9 @@
|
||||
"description": "A tool to automatically fix PHP code style",
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-CS-Fixer/shim/issues",
|
||||
"source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.92.4"
|
||||
"source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.92.5"
|
||||
},
|
||||
"time": "2026-01-04T00:39:15+00:00"
|
||||
"time": "2026-01-08T21:58:02+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.19.7
|
||||
0.20.5
|
||||
|
||||
Reference in New Issue
Block a user