Merge pull request #1558 from nextcloud/artonge/fix/build_linkify

Make CI green
pull/1564/head
Louis 2023-01-05 10:45:11 +01:00 zatwierdzone przez GitHub
commit 5ec03e2db5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
36 zmienionych plików z 5348 dodań i 1164 usunięć

Wyświetl plik

@ -11,50 +11,78 @@ env:
APP_NAME: social
BRANCH: ${{ github.base_ref }}
CYPRESS_baseUrl: http://127.0.0.1:8082/index.php
TESTING: true
jobs:
cypress:
init:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# run 2 copies of the current job in parallel
containers: [1, 2]
node-version: ['12']
php-versions: ['7.4']
name: Runner ${{ matrix.containers }}
steps:
- name: Checkout app
uses: actions/checkout@v2
- name: Setup server
run: |
cd cypress
docker-compose up -d
- name: Set up node ${{ matrix.node-version }}
uses: actions/setup-node@v1
- name: Read package.json node and npm engines version
uses: skjnldsv/read-package-engines-version-actions@v1.1
id: versions
with:
node-version: ${{ matrix.node-version }}
fallbackNode: "^12"
fallbackNpm: "^6"
- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
uses: actions/setup-node@v3
with:
cache: "npm"
node-version: ${{ steps.versions.outputs.nodeVersion }}
- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
- name: Install dependencies & build app
run: |
npm ci
composer install
TESTING=true npm run build --if-present
- name: Wait for server
- name: Save context
uses: actions/cache@v3
with:
key: cypress-context-${{ github.run_id }}
path: /home/runner/work/social
cypress:
runs-on: ubuntu-latest
needs: init
strategy:
fail-fast: false
matrix:
# run multiple copies of the current job in parallel
containers: [1, 2, 3, 4, 5, 6, 7, 8]
name: runner ${{ matrix.containers }}
steps:
- name: Restore context
uses: actions/cache@v3
with:
key: cypress-context-${{ github.run_id }}
path: /home/runner/work/social
- name: Setup server
run: |
npm install -g wait-on
wait-on -i 500 -t 240000 $CYPRESS_baseUrl
cd cypress
docker-compose up -d
- name: Wait for server
run: npm run wait-on $CYPRESS_baseUrl
- name: Enable app & configure server
run: |
cd cypress
docker-compose exec --env APP_NAME=${{ env.APP_NAME }} -T nextcloud bash /initserver.sh
docker-compose exec --env APP_NAME=${{ env.APP_NAME }} --env BRANCH=${{ env.BRANCH }} -T nextcloud bash /initserver.sh
- name: Cypress run
uses: cypress-io/github-action@v1
uses: cypress-io/github-action@v4
with:
record: true
parallel: true

28
cypress.config.js 100644
Wyświetl plik

@ -0,0 +1,28 @@
const { defineConfig } = require('cypress')
const browserify = require('@cypress/browserify-preprocessor')
module.exports = defineConfig({
projectId: '7mqhfh',
viewportWidth: 1280,
viewportHeight: 720,
defaultCommandTimeout: 6000,
retries: 1,
env: {
failSilently: false,
type: 'actual',
},
screenshotsFolder: 'cypress/snapshots/actual',
trashAssetsBeforeRuns: true,
e2e: {
baseUrl: 'http://localhost:8082/index.php',
setupNodeEvents(on, config) {
// Fix browserslist extend https://github.com/cypress-io/cypress/issues/2983#issuecomment-570616682
on('file:preprocessor', browserify())
},
},
})

Wyświetl plik

@ -1,7 +0,0 @@
{
"baseUrl": "http://localhost:8082/index.php/",
"projectId": "7mqhfh",
"viewportWidth": 1280,
"viewportHeight": 720,
"defaultCommandTimeout": 6000
}

Wyświetl plik

@ -1,16 +1,18 @@
version: '3'
version: '3.7'
services:
nextcloud:
image: nextcloudci/server
image: ghcr.io/nextcloud/continuous-integration-shallow-server
ports:
- 8082:80
environment:
CYPRESS_baseUrl: "http://127.0.0.1:8082/index.php"
BRANCH: master
BRANCH: "${BRANCH:-master}"
volumes:
- ../:/var/www/html/apps/social
# Using fallback to make sure this script doesn't mess
# with the mounting if APP_NAME is not provided.
- ../:/var/www/html/apps/${APP_NAME:-social}
- ./initserver.sh:/initserver.sh

Wyświetl plik

@ -1,4 +1,4 @@
let userId = 'janedoe' + Date.now();
const userId = 'janedoe' + Date.now()
describe('Social app setup', function() {
before(function() {
@ -6,10 +6,6 @@ describe('Social app setup', function() {
cy.login(userId, 'p4ssw0rd')
})
beforeEach(() => {
Cypress.Cookies.preserveOnce('nc_username', 'nc_token', 'nc_session_id', 'oc_sessionPassphrase');
})
it('See the welcome message', function() {
cy.visit('/apps/social/')
cy.get('.social__welcome').should('contain', 'Nextcloud becomes part of the federated social networks!')
@ -26,7 +22,7 @@ describe('Social app setup', function() {
cy.get('.app-navigation').contains('Direct messages').click()
cy.get('.emptycontent').should('be.visible').contains('No direct messages found')
cy.get('.app-navigation').contains('Profile').click()
cy.get('.emptycontent').should('be.visible').contains('You haven\'t tooted yet')
cy.get('.emptycontent').should('be.visible').contains('You have not tooted yet')
})
})

Wyświetl plik

@ -20,15 +20,17 @@
*
*/
let userId = 'janedoe' + Date.now();
const userId = 'janedoe' + Date.now()
describe('Create posts', function() {
before(function() {
// ensure that the admin account is initialized for social
cy.login('admin', 'admin', '/apps/social/')
cy.nextcloudCreateUser(userId, 'p4ssw0rd')
cy.logout()
cy.login(userId, 'p4ssw0rd', '/apps/social/')
cy.get('.app-content').should('be.visible')
})
@ -37,10 +39,6 @@ describe('Create posts', function() {
cy.screenshot()
})
beforeEach(() => {
Cypress.Cookies.preserveOnce('nc_username', 'nc_token', 'nc_session_id', 'oc_sessionPassphrase');
})
it('See the empty content illustration', function() {
cy.get('.emptycontent').should('be.visible').contains('No posts found')
})
@ -49,19 +47,19 @@ describe('Create posts', function() {
cy.visit('/apps/social/')
cy.server()
cy.route('POST', '/index.php/apps/social/api/v1/post').as('postMessage')
cy.get('.new-post input[type=submit]')
cy.get('.new-post button[type=submit]')
.should('be.disabled')
cy.get('.new-post').find('[contenteditable]').type('Hello world')
cy.get('.new-post input[type=submit]')
cy.get('.new-post button[type=submit]')
.should('not.be.disabled')
cy.get('.new-post input[type=submit]')
cy.get('.new-post button[type=submit]')
.click()
cy.wait('@postMessage')
cy.get('.social__timeline div.timeline-entry:first-child').should('contain', 'Hello world')
})
it('No longer see the empty content illustration', function() {
cy.get('.emptycontent').should('not.be.visible')
cy.get('.emptycontent').should('not.exist')
})
it('Write a post to followers with shift enter', function() {
@ -78,11 +76,11 @@ describe('Create posts', function() {
cy.server()
cy.route('POST', '/index.php/apps/social/api/v1/post').as('postMessage')
cy.route('GET', '/index.php/apps/social/api/v1/global/accounts/search')
cy.get('.new-post').find('[contenteditable]').type('@adm', {delay: 500})
cy.get('.new-post').find('[contenteditable]').type('@adm', { delay: 500 })
cy.get('.tribute-container').should('be.visible')
cy.get('.tribute-container ul li:first').contains('admin')
cy.get('.new-post').find('[contenteditable]').type('{enter} Hello there', {delay: 100, force: true})
cy.get('.new-post input[type=submit]')
cy.get('.new-post').find('[contenteditable]').type('{enter} Hello there', { delay: 100, force: true })
cy.get('.new-post button[type=submit]')
.click()
cy.wait('@postMessage')
cy.get('.social__timeline div.timeline-entry:first-child').should('contain', '@admin')
@ -93,9 +91,9 @@ describe('Create posts', function() {
cy.server()
cy.route('POST', '/index.php/apps/social/api/v1/post').as('postMessage')
cy.route('GET', '/index.php/apps/social/api/v1/global/accounts/search')
cy.get('.new-post').find('[contenteditable]').click({force: true}).type('@adm{enter} Hello world', {delay: 500, force: true})
cy.get('.new-post').find('[contenteditable]').click({ force: true }).type('@adm{enter} Hello world', { delay: 500, force: true })
cy.wait(500)
cy.get('.new-post input[type=submit]').should('not.be.disabled')
cy.get('.new-post button[type=submit]').should('not.be.disabled')
const visibilityButton = cy.get('.new-post .options > div > button')
visibilityButton.should('have.class', 'icon-contacts-dark')
@ -105,7 +103,7 @@ describe('Create posts', function() {
visibilityButton.click()
cy.get('.new-post-form .popovermenu').should('not.be.visible')
cy.get('.new-post input[type=submit]')
cy.get('.new-post button[type=submit]')
.click()
cy.wait('@postMessage')
cy.get('.social__timeline div.timeline-entry:first-child').should('contain', 'Hello world').should('contain', '@admin')

Wyświetl plik

@ -1,6 +1,8 @@
#!/usr/bin/env bash
echo "APP_NAME: $APP_NAME"
echo "BRANCH: $BRANCH"
chown -R www-data:www-data /var/www/html/data
su www-data -c "

Wyświetl plik

@ -1,20 +0,0 @@
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
const {
addMatchImageSnapshotPlugin
} = require('cypress-image-snapshot/plugin')
module.exports = (on, config) => {
addMatchImageSnapshotPlugin(on, config)
}

Wyświetl plik

@ -1,6 +1,7 @@
#!/usr/bin/env bash
# RUN THIS SCRIPT FROM THE ROOT FOLDER OF YOUR APP
APP_NAME=${PWD##*/}
CYPRESS_baseUrl=http://127.0.0.1:8082/index.php
if [[ $APP_NAME == "cypress" ]]
then
@ -8,13 +9,11 @@ then
else
echo "Launching docker server for the $APP_NAME app"
cd cypress
docker-compose up -d
echo -n "Waiting for server start "
until [[ $(docker-compose exec -u www-data -T nextcloud php ./occ status --output=json) == *"\"installed\":true"* ]]
do
echo -n "."
done
echo ""
docker-compose pull
docker-compose up -d --force-recreate
npm run wait-on $CYPRESS_baseUrl
echo "Nextcloud successfully installed"
docker-compose exec --env APP_NAME=$APP_NAME -T nextcloud bash /initserver.sh
docker-compose exec -u www-data -T nextcloud php ./occ social:reset -n
docker-compose exec -u www-data -T nextcloud php ./occ social:reset -n
echo "Nextcloud successfully configured"
fi

Wyświetl plik

@ -20,37 +20,45 @@
*
*/
import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command'
import axios from '@nextcloud/axios'
addMatchImageSnapshotCommand()
const url = Cypress.config('baseUrl').replace(/\/index.php\/?$/g, '')
Cypress.env('baseUrl', url)
Cypress.Commands.add('login', (user, password, route = '/apps/files') => {
cy.clearCookies();
Cypress.Cookies.defaults({
preserve: /^(oc|nc)/,
})
cy.visit(route)
cy.get('input[name=user]').type(user)
cy.get('input[name=password]').type(password)
cy.get('#submit-wrapper input[type=submit]').click()
cy.get('form[name=login] [type=submit]').click()
cy.url().should('include', route)
})
Cypress.Commands.add('logout', () => {
if (Cypress.$("input[name=user]").length > 0) {
// already logged out
} else {
cy.get('#expanddiv li[data-id="logout"] a').then(logout => {
if (logout) {
cy.visit(logout[0].href)
cy.getCookies()
.then(cookies => {
if (cookies.length === 0) {
cy.log('Not logged, skipping logout...')
return
}
return cy.get('body')
.then($body => {
const $settingsButton = $body.find('#settings #expand')
if ($settingsButton.length === 0) {
cy.log('Not logged in.')
return
}
$settingsButton.click()
cy.contains('Log out').click()
})
})
}
})
Cypress.Commands.add('nextcloudCreateUser', (user, password) => {
cy.clearCookies();
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v1.php/cloud/users?format=json`,
@ -61,13 +69,12 @@ Cypress.Commands.add('nextcloudCreateUser', (user, password) => {
},
auth: { user: 'admin', pass: 'admin' },
headers: {
'OCS-ApiRequest': 'true',
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${btoa('admin:admin')}`,
'OCS-ApiRequest': 'true',
Authorization: `Basic ${Buffer.from('admin:admin').toString('base64')}`,
},
}).then(response => {
cy.log(`Created user ${user}`, response.status)
})
cy.clearCookies()
})
Cypress.Commands.add('uploadFile', (fileName, mimeType, path = '') => {
@ -82,7 +89,7 @@ Cypress.Commands.add('uploadFile', (fileName, mimeType, path = '') => {
headers: {
requesttoken: window.OC.requestToken,
'Content-Type': mimeType,
}
},
}).then(response => {
cy.log(`Uploaded ${fileName}`, response)
})
@ -122,7 +129,7 @@ Cypress.Commands.add('deleteFile', fileName => {
* Create a share link and return the share url
*
* @param {string} path the file/folder path
* @returns {string} the share link url
* @return {string} the share link url
*/
Cypress.Commands.add('createLinkShare', path => {
return cy.window().then(async window => {
@ -133,26 +140,15 @@ Cypress.Commands.add('createLinkShare', path => {
}, {
headers: {
requesttoken: window.OC.requestToken,
}
},
})
if (!('ocs' in request.data) || !('token' in request.data.ocs.data && request.data.ocs.data.token.length > 0)) {
throw request
}
cy.log('Share link created', request.data.ocs.data.token)
return cy.wrap(request.data.ocs.data.token)
} catch(error) {
} catch (error) {
console.error(error)
}
}).should('have.length', 15)
})
Cypress.Commands.overwrite('matchImageSnapshot', (originalFn, subject, name, options) => {
// hide avatar because random colour break the visual regression tests
cy.window().then(window => {
const avatarDiv = window.document.querySelector('.avatardiv')
if (avatarDiv) {
avatarDiv.remove()
}
})
return originalFn(subject, name, options)
})

Wyświetl plik

@ -1,5 +1,5 @@
// ***********************************************************
// This example support/index.js is processed and
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
@ -14,7 +14,4 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
import './commands.js'

Wyświetl plik

@ -185,7 +185,7 @@ class AP {
*/
public function getItemFromData(array $data, ACore $parent = null, int $level = 0): ACore {
if (++$level > self::REDUNDANCY_LIMIT) {
throw new RedundancyLimitException($level);
throw new RedundancyLimitException((string)$level);
}
$item = $this->getSimpleItemFromData($data);

Wyświetl plik

@ -50,7 +50,6 @@ use OCA\Social\Service\StreamQueueService;
use OCA\Social\Service\StreamService;
use OCA\Social\Tools\Traits\TAsync;
use OCA\Social\Tools\Traits\TNCDataResponse;
use OCA\Social\Tools\Traits\TNCLogger;
use OCA\Social\Tools\Traits\TStringTools;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
@ -63,7 +62,6 @@ class ActivityPubController extends Controller {
use TNCDataResponse;
use TStringTools;
use TAsync;
use TNCLogger;
private SocialPubController $socialPubController;
private FediverseService $fediverseService;

Wyświetl plik

@ -12,6 +12,9 @@ use OCP\EventDispatcher\IEventListener;
use OCP\Profile\BeforeTemplateRenderedEvent;
use OCP\Util;
/**
* @template-implements IEventListener<\OCP\EventDispatcher\Event>
*/
class ProfileSectionListener implements IEventListener {
public function handle(Event $event): void {
if (!($event instanceof BeforeTemplateRenderedEvent)) {

Wyświetl plik

@ -70,17 +70,12 @@ class ACore extends Item implements JsonSerializable {
private $parent = null;
private string $requestToken = '';
private array $entries = [];
private ?\OCA\Social\Model\ActivityPub\ACore $object = null;
private ?ACore $object = null;
private ?Document $icon = null;
private bool $displayW3ContextSecurity = false;
private ?LinkedDataSignature $signature = null;
private int $format = self::FORMAT_ACTIVITYPUB;
@ -151,9 +146,9 @@ class ACore extends Item implements JsonSerializable {
}
/**
* @return ACore
* @return null|self
*/
public function getObject(): ACore {
public function getObject(): ?ACore {
return $this->object;
}
@ -162,7 +157,7 @@ class ACore extends Item implements JsonSerializable {
*
* @return ACore
*/
public function setObject(ACore &$object): ACore {
public function setObject(ACore $object): self {
$object->setParent($this);
$this->object = $object;
@ -205,10 +200,7 @@ class ACore extends Item implements JsonSerializable {
return ($this->icon !== null);
}
/**
* @return Document
*/
public function getIcon(): Document {
public function getIcon(): ?Document {
return $this->icon;
}
@ -217,7 +209,7 @@ class ACore extends Item implements JsonSerializable {
*
* @return ACore
*/
public function setIcon(Document &$icon): ACore {
public function setIcon(Document $icon): ACore {
$icon->setParent($this);
$this->icon = $icon;
@ -255,14 +247,11 @@ class ACore extends Item implements JsonSerializable {
/**
* @return bool
*/
public function gotSignature(): bool {
public function hasSignature(): bool {
return ($this->signature !== null);
}
/**
* @return LinkedDataSignature
*/
public function getSignature(): LinkedDataSignature {
public function getSignature(): ?LinkedDataSignature {
return $this->signature;
}
@ -694,14 +683,14 @@ class ACore extends Item implements JsonSerializable {
* @return array
*/
public function exportAsActivityPub(): array {
if ($this->gotSignature()) {
if ($this->hasSignature()) {
$this->entries['signature'] = $this->getSignature();
}
if ($this->isRoot()) {
$context = [self::CONTEXT_ACTIVITYSTREAMS];
if ($this->gotSignature() || $this->isDisplayW3ContextSecurity()) {
if ($this->hasSignature() || $this->isDisplayW3ContextSecurity()) {
array_push($context, self::CONTEXT_SECURITY);
}

Wyświetl plik

@ -228,10 +228,7 @@ class Instance implements IQueryRow, JsonSerializable {
return ($this->contactAccount !== null);
}
/**
* @return Person
*/
public function getContactAccount(): Person {
public function getContactAccount(): ?Person {
return $this->contactAccount;
}

Wyświetl plik

@ -158,10 +158,7 @@ class RequestQueue implements JsonSerializable {
}
/**
* @return InstancePath
*/
public function getInstance(): InstancePath {
public function getInstance(): ?InstancePath {
return $this->instance;
}

Wyświetl plik

@ -30,7 +30,6 @@ declare(strict_types=1);
namespace OCA\Social\Search;
use OCA\Social\Tools\Traits\TNCLogger;
use OCA\Social\Tools\Traits\TArrayTools;
use Exception;
use OCA\Social\Exceptions\AccountDoesNotExistException;
@ -58,30 +57,17 @@ class UnifiedSearchProvider implements IProvider {
public const PROVIDER_ID = 'social';
public const ORDER = 12;
use TArrayTools;
use TNCLogger;
private IL10N $l10n;
private IURLGenerator $urlGenerator;
private StreamService $streamService;
private FollowService $followService;
private CacheActorService $cacheActorService;
private AccountService $accountService;
private SearchService $searchService;
private ConfigService $configService;
private MiscService $miscService;
private ?Person $viewer = null;

Wyświetl plik

@ -30,14 +30,6 @@ declare(strict_types=1);
namespace OCA\Social\Service;
use OCA\Social\Tools\Exceptions\MalformedArrayException;
use OCA\Social\Tools\Exceptions\RequestContentException;
use OCA\Social\Tools\Exceptions\RequestNetworkException;
use OCA\Social\Tools\Exceptions\RequestResultNotJsonException;
use OCA\Social\Tools\Exceptions\RequestResultSizeException;
use OCA\Social\Tools\Exceptions\RequestServerException;
use OCA\Social\Tools\Traits\TNCLogger;
use OCA\Social\Tools\Traits\TArrayTools;
use Exception;
use OCA\Social\AP;
use OCA\Social\Db\CacheActorsRequest;
@ -51,7 +43,15 @@ use OCA\Social\Exceptions\RetrieveAccountFormatException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UnauthorizedFediverseException;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Tools\Exceptions\MalformedArrayException;
use OCA\Social\Tools\Exceptions\RequestContentException;
use OCA\Social\Tools\Exceptions\RequestNetworkException;
use OCA\Social\Tools\Exceptions\RequestResultNotJsonException;
use OCA\Social\Tools\Exceptions\RequestResultSizeException;
use OCA\Social\Tools\Exceptions\RequestServerException;
use OCA\Social\Tools\Traits\TArrayTools;
use OCP\IURLGenerator;
use Psr\Log\LoggerInterface;
/**
* Class CacheActorService
@ -60,28 +60,31 @@ use OCP\IURLGenerator;
*/
class CacheActorService {
use TArrayTools;
use TNCLogger;
private \OCP\IURLGenerator $urlGenerator;
private CacheActorsRequest $cacheActorsRequest;
private CurlService $curlService;
private FediverseService $fediverseService;
private ConfigService $configService;
private MiscService $miscService;
private LoggerInterface $logger;
/**
* CacheService constructor.
* CacheActorService constructor.
*/
public function __construct(
IUrlGenerator $urlGenerator, CacheActorsRequest $cacheActorsRequest, CurlService $curlService,
FediverseService $fediverseService, ConfigService $configService, MiscService $miscService
IUrlGenerator $urlGenerator,
CacheActorsRequest $cacheActorsRequest,
CurlService $curlService,
FediverseService $fediverseService,
ConfigService $configService,
LoggerInterface $logger
) {
$this->urlGenerator = $urlGenerator;
$this->cacheActorsRequest = $cacheActorsRequest;
$this->curlService = $curlService;
$this->fediverseService = $fediverseService;
$this->configService = $configService;
$this->miscService = $miscService;
$this->logger = $logger;
}
@ -127,7 +130,7 @@ class CacheActorService {
} catch (CacheActorDoesNotExistException $e) {
$object = $this->curlService->retrieveObject($id);
$this->debug('object retrieved', ['id' => $id, 'object' => $object]);
$this->logger->debug('object retrieved', ['id' => $id, 'object' => $object]);
/** @var Person $actor */
$actor = AP::$activityPub->getItemFromData($object);
@ -205,14 +208,14 @@ class CacheActorService {
} catch (CacheActorDoesNotExistException $e) {
}
$this->debug('getFromAccount', ['account' => $account, 'retrieve' => $retrieve]);
$this->logger->debug('getFromAccount', ['account' => $account, 'retrieve' => $retrieve]);
try {
$actor = $this->cacheActorsRequest->getFromAccount($account);
$this->debug('Found Actor', ['account' => $account, 'actor' => $actor]);
$this->logger->debug('Found Actor', ['account' => $account, 'actor' => $actor]);
} catch (CacheActorDoesNotExistException $e) {
$this->debug('Actor not found', ['account' => $account]);
$this->logger->debug('Actor not found', ['account' => $account]);
if (!$retrieve) {
throw new CacheActorDoesNotExistException();
@ -221,7 +224,7 @@ class CacheActorService {
$actor = $this->curlService->retrieveAccount($account);
$actor->setAccount($account);
try {
$this->warning('Saving Actor', false, ['actor' => $actor]);
$this->logger->debug('Saving Actor', ['actor' => $actor]);
$this->save($actor);
} catch (Exception $e) {

Wyświetl plik

@ -203,16 +203,19 @@ class CacheDocumentService {
/**
* @param $content
* @param string $content
*/
private function resizeImage(&$content) {
private function resizeImage(string &$content) {
try {
$image = ImageResize::createFromString($content);
$image->quality_jpg = 100;
$image->quality_png = 9;
$image->resizeToBestFit(self::RESIZED_WIDTH, self::RESIZED_HEIGHT);
$content = $image->getImageAsString();
$newContent = $image->getImageAsString();
if (!$newContent) {
$content = $newContent;
}
} catch (ImageResizeException $e) {
}
}

Wyświetl plik

@ -276,8 +276,10 @@ class ConfigService {
* @param $key
*
* @return mixed
*
* @psalm-param string $key
*/
public function getSystemValue($key) {
public function getSystemValue(string $key) {
return $this->config->getSystemValue($key, '');
}

Wyświetl plik

@ -238,7 +238,7 @@ class CurlService {
* @throws SocialAppConfigException
* @throws UnauthorizedFediverseException
*/
public function retrieveObject($id): array {
public function retrieveObject(string $id): array {
$this->logger->debug('retrieveObject id=' . $id);
$url = parse_url($id);
$this->mustContains(['path', 'host', 'scheme'], $url);
@ -430,8 +430,6 @@ class CurlService {
/**
* @param Request $request
*
* @return resource
*/
private function generateCurlRequest(Request $request) {
$url = $request->getUsedProtocol() . '://' . $request->getHost() . $request->getParsedUrl();

Wyświetl plik

@ -232,7 +232,9 @@ class FollowService {
/**
* @param Person $actor
*
* @return Person[]
* @return Follow[]
*
* @psalm-return array<Follow>
*/
public function getFollowers(Person $actor): array {
return $this->followsRequest->getFollowersByActorId($actor->getId());
@ -257,7 +259,9 @@ class FollowService {
/**
* @param Person $actor
*
* @return Person[]
* @return Follow[]
*
* @psalm-return array<Follow>
*/
public function getFollowing(Person $actor): array {
return $this->followsRequest->getFollowingByActorId($actor->getId());

Wyświetl plik

@ -39,7 +39,6 @@ use OCA\Social\Exceptions\HashtagDoesNotExistException;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\ActivityPub\Object\Note;
use OCA\Social\Model\ActivityPub\Stream;
class HashtagService {
public const TREND_1H = 3600;
@ -155,10 +154,9 @@ class HashtagService {
/**
* @param int $timestamp
*
* @return Stream[]
* @return int[]
* @throws DateTimeException
* @throws ItemUnknownException
* @throws SocialAppConfigException
* @psalm-return array<int>
*/
private function getTrendSince(int $timestamp): array {
$result = [];

Wyświetl plik

@ -58,7 +58,7 @@ class MiscService {
* @param $message
* @param int $level
*/
public function log($message, $level = 2) {
public function log(string $message, $level = 2) {
$data = array(
'app' => Application::APP_NAME,
'level' => $level

Wyświetl plik

@ -97,7 +97,7 @@ class PostService {
* @throws StreamNotFoundException
* @throws UnauthorizedFediverseException
*/
public function createPost(Post $post, string &$token = ''): ACore {
public function createPost(Post $post, string &$token = ''): ?ACore {
$this->fixRecipientAndHashtags($post);
$note = new Note();
@ -130,10 +130,10 @@ class PostService {
private function generateDocumentsFromAttachments(Note $note, Post $post) {
$documents = [];
if (!isset($_FILES['attachments'])) {
return [];
return;
}
if (is_array($_FILES["attachments"]["error"])) {
foreach ($_FILES["attachments"]["error"] as $key => $error) {
if (is_array($_FILES['attachments']['error'])) {
foreach ($_FILES['attachments']['error'] as $key => $error) {
if ($error == UPLOAD_ERR_OK) {
try {
$document = $this->generateDocumentFromAttachment($note, $key);

Wyświetl plik

@ -31,10 +31,10 @@ declare(strict_types=1);
namespace OCA\Social\Service;
use OCA\Social\Tools\Traits\TNCLogger;
use OCA\Social\Tools\Traits\TArrayTools;
use Exception;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Tools\Traits\TArrayTools;
use Psr\Log\LoggerInterface;
/**
* Class SearchService
@ -43,7 +43,6 @@ use OCA\Social\Model\ActivityPub\Actor\Person;
*/
class SearchService {
use TArrayTools;
use TNCLogger;
public const SEARCH_ACCOUNTS = 1;
@ -51,14 +50,10 @@ class SearchService {
public const SEARCH_CONTENT = 4;
public const SEARCH_ALL = 7;
private CacheActorService $cacheActorService;
private HashtagService $hashtagService;
private ConfigService $configService;
private MiscService $miscService;
private LoggerInterface $logger;
/**
@ -67,16 +62,18 @@ class SearchService {
* @param CacheActorService $cacheActorService
* @param HashtagService $hashtagService
* @param ConfigService $configService
* @param MiscService $miscService
* @param LoggerInterface $logger
*/
public function __construct(
CacheActorService $cacheActorService, HashtagService $hashtagService,
ConfigService $configService, MiscService $miscService
CacheActorService $cacheActorService,
HashtagService $hashtagService,
ConfigService $configService,
LoggerInterface $logger
) {
$this->cacheActorService = $cacheActorService;
$this->hashtagService = $hashtagService;
$this->configService = $configService;
$this->miscService = $miscService;
$this->logger = $logger;
}
@ -100,7 +97,7 @@ class SearchService {
try {
$this->cacheActorService->getFromAccount($search);
} catch (Exception $e) {
$this->exception($e, self::$NOTICE, ['search' => $search]);
$this->logger->notice('searchAccounts', ['exception' => $e, 'search' => $search]);
}
return $this->cacheActorService->searchCachedAccounts($search);

Wyświetl plik

@ -451,7 +451,7 @@ class SignatureService {
*
* @return array
*/
private function parseSignatureHeader($signatureHeader) {
private function parseSignatureHeader(string $signatureHeader) {
$sign = [];
$entries = explode(',', $signatureHeader);
@ -504,7 +504,7 @@ class SignatureService {
* @return string
* @throws InvalidOriginException
*/
private function getKeyOrigin($id) {
private function getKeyOrigin(string $id) {
$host = parse_url($id, PHP_URL_HOST);
if (is_string($host) && ($host !== '')) {
return $host;

Wyświetl plik

@ -544,7 +544,7 @@ class StreamService {
* @throws RequestResultNotJsonException
* @throws UnauthorizedFediverseException
*/
public function getAuthorFromPostId($noteId) {
public function getAuthorFromPostId(string $noteId) {
$note = $this->streamRequest->getStreamById($noteId);
return $this->cacheActorService->getFromId($note->getAttributedTo());

Wyświetl plik

@ -455,7 +455,10 @@ class Request implements JsonSerializable {
}
public function addHeader($key, $value): Request {
/**
* @psalm-param string $key
*/
public function addHeader(string $key, string $value): Request {
$header = $this->get($key, $this->headers);
if ($header !== '') {
$header .= ', ' . $value;

Wyświetl plik

@ -1,200 +0,0 @@
<?php
declare(strict_types=1);
/**
* Some tools for myself.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2020, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Tools\Traits;
use Exception;
use OC\HintException;
use OCP\Server;
use Psr\Log\LoggerInterface;
use Throwable;
trait TNCLogger {
use TNCSetup;
public static int $EMERGENCY = 4;
public static int $ALERT = 3;
public static int $CRITICAL = 3;
public static int $ERROR = 3;
public static int $WARNING = 2;
public static int $NOTICE = 1;
public static int $INFO = 1;
public static int $DEBUG = 0;
/**
* @param Throwable $t
* @param array $serializable
*/
public function t(Throwable $t, array $serializable = []): void {
$this->throwable($t, self::$ERROR, $serializable);
}
/**
* @param Throwable $t
* @param int $level
* @param array $serializable
*/
public function throwable(Throwable $t, int $level = 3, array $serializable = []): void {
$message = '';
if (!empty($serializable)) {
$message = json_encode($serializable);
}
$this->logger()
->log(
$level,
$message,
[
'app' => $this->setup('app'),
'exception' => $t
]
);
}
/**
* @param Exception $e
* @param array $serializable
*/
public function e(Exception $e, array $serializable = []): void {
$this->exception($e, self::$ERROR, $serializable);
}
/**
* @param Exception $e
* @param int|array $level
* @param array $serializable
*/
public function exception(Exception $e, $level = 3, array $serializable = []): void {
if (is_array($level) && empty($serializable)) {
$serializable = $level;
$level = 3;
}
$message = '';
if (!empty($serializable)) {
$message = json_encode($serializable);
}
if ($level === self::$DEBUG) {
$level = (int)$this->appConfig('debug_level');
}
$this->logger()
->log(
$level,
$message,
[
'app' => $this->setup('app'),
'exception' => $e
]
);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function emergency(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$EMERGENCY, '[emergency] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function alert(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$ALERT, '[alert] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function warning(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$WARNING, '[warning] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function notice(string $message, bool $trace = false, array $serializable = []): void {
$this->log(self::$NOTICE, '[notice] ' . $message, $trace, $serializable);
}
/**
* @param string $message
* @param array $serializable
*/
public function debug(string $message, array $serializable = []): void {
$message = '[debug] ' . $message;
$debugLevel = (int)$this->appConfig('debug_level');
$this->log($debugLevel, $message, ($this->appConfig('debug_trace') === '1'), $serializable);
}
/**
* @param int $level
* @param string $message
* @param bool $trace
* @param array $serializable
*/
public function log(int $level, string $message, bool $trace = false, array $serializable = []): void {
$opts = ['app' => $this->setup('app')];
if ($trace) {
$opts['exception'] = new HintException($message, json_encode($serializable));
} elseif (!empty($serializable)) {
$message .= ' -- ' . json_encode($serializable);
}
$this->logger()
->log($level, $message, $opts);
}
/**
* @return LoggerInterface
*/
public function logger(): LoggerInterface {
if (isset($this->logger) && $this->logger instanceof LoggerInterface) {
return $this->logger;
} else {
return Server::get(LoggerInterface::class);
}
}
}

5782
package-lock.json wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -20,12 +20,14 @@
"dev": "NODE_ENV=development webpack --config webpack.common.js",
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.common.js",
"build": "NODE_ENV=production webpack --progress --config webpack.common.js",
"serve": "NODE_ENV=development webpack serve --progress --config webpack.common.js",
"lint": "eslint --ext .js,.vue src",
"lint:fix": "eslint --ext .js,.vue src --fix",
"test": "jest",
"test:coverage": "jest --coverage",
"cypress": "cypress run",
"cypress:gui": "cypress open"
"cypress": "./cypress/start.sh; cypress run; ./cypress/stop.sh",
"cypress:gui": "./cypress/start.sh; cypress open; ./cypress/stop.sh",
"wait-on": "wait-on -i 500 -t 300000"
},
"dependencies": {
"@nextcloud/auth": "^2.0.0",
@ -40,6 +42,8 @@
"@nextcloud/vue": "^7.3.0",
"@nextcloud/vue-richtext": "^2.0.4",
"he": "^1.2.0",
"linkify-plugin-mention": "^4.1.0",
"linkify-string": "^4.1.0",
"linkifyjs": "^4.0.2",
"sass": "^1.57.1",
"tributejs": "^5.1.3",
@ -69,13 +73,16 @@
"npm": "^7.0.0 || ^8.0.0"
},
"devDependencies": {
"@cypress/browserify-preprocessor": "^3.0.2",
"@nextcloud/babel-config": "^1.0.0",
"@nextcloud/browserslist-config": "^2.3.0",
"@nextcloud/eslint-config": "^8.1.4",
"@nextcloud/webpack-vue-config": "^5.4.0",
"cypress": "^11.2.0",
"jest": "^29.3.1",
"jest-serializer-vue": "^3.1.0",
"vue-template-compiler": "^2.7.10"
"vue-template-compiler": "^2.7.10",
"wait-on": "^7.0.1"
},
"jest": {
"moduleFileExtensions": [
@ -93,4 +100,4 @@
"<rootDir>/node_modules/jest-serializer-vue"
]
}
}
}

Wyświetl plik

@ -120,6 +120,7 @@
<div class="emptySpace" />
<NcButton :value="currentVisibilityPostLabel"
:disabled="!canPost"
native-type="submit"
type="primary"
@click.prevent="createPost">
<template #icon>

Wyświetl plik

@ -78,11 +78,10 @@
</template>
<script>
// eslint-disable-next-line no-unused-vars
import * as linkify from 'linkifyjs'
// eslint-disable-next-line
import pluginMention from 'linkifyjs/plugins/mention'
// eslint-disable-next-line
import 'linkifyjs/string'
import 'linkify-plugin-mention'
import 'linkify-string'
import currentUser from './../mixins/currentUserMixin.js'
import PostAttachment from './PostAttachment.vue'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
@ -97,8 +96,6 @@ import moment from '@nextcloud/moment'
import { generateUrl } from '@nextcloud/router'
import RichText from '@nextcloud/vue-richtext'
pluginMention(linkify)
export default {
name: 'TimelinePost',
components: {

Wyświetl plik

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.30.0@d0bc6e25d89f649e4f36a534f330f8bb4643dd69">
<file src="lib/AP.php">
<InvalidScalarArgument occurrences="1">
<code>$level</code>
</InvalidScalarArgument>
</file>
<files psalm-version="5.4.0@62db5d4f6a7ae0a20f7cc5a4952d730272fc0863">
<file src="lib/Model/ActivityPub/ACore.php">
<InvalidArgument occurrences="1">
<code>['a', 'p', 'span', 'br']</code>
@ -12,20 +7,15 @@
<InvalidClass occurrences="1">
<code>Acore</code>
</InvalidClass>
<InvalidNullableReturnType occurrences="4">
<InvalidNullableReturnType occurrences="1">
<code>ACore</code>
<code>ACore</code>
<code>Document</code>
<code>LinkedDataSignature</code>
</InvalidNullableReturnType>
<InvalidPropertyAssignmentValue occurrences="1">
<code>$parent</code>
</InvalidPropertyAssignmentValue>
<NullableReturnStatement occurrences="4">
<code>$this-&gt;icon</code>
<code>$this-&gt;object</code>
<NullableReturnStatement occurrences="1">
<code>$this-&gt;parent</code>
<code>$this-&gt;signature</code>
</NullableReturnStatement>
<TypeDoesNotContainNull occurrences="1">
<code>$v === null</code>
@ -40,28 +30,13 @@
<code>$object = $cache-&gt;getItem($this-&gt;getObjectId())</code>
</RedundantCondition>
</file>
<file src="lib/Model/Instance.php">
<InvalidNullableReturnType occurrences="1">
<code>Person</code>
</InvalidNullableReturnType>
<NullableReturnStatement occurrences="1">
<code>$this-&gt;contactAccount</code>
</NullableReturnStatement>
</file>
<file src="lib/Model/RequestQueue.php">
<InvalidNullableReturnType occurrences="1">
<code>InstancePath</code>
</InvalidNullableReturnType>
<NullableReturnStatement occurrences="1">
<code>$this-&gt;instance</code>
</NullableReturnStatement>
</file>
<file src="lib/Db/CoreRequestBuilder.php">
<UndefinedMethod occurrences="3">
<code>dropTable</code>
<code>hasTable</code>
<code>hasTable</code>
</UndefinedMethod>
<InvalidArgument occurrences="2"/>
</file>
<file src="lib/Service/CheckService.php">
<RedundantCast occurrences="1">
@ -81,36 +56,16 @@
<TypeDoesNotContainType occurrences="1">
<code>$this-&gt;maxDownloadSizeReached === true</code>
</TypeDoesNotContainType>
</file>
<file src="lib/Service/FollowService.php">
<InvalidReturnStatement occurrences="2">
<code>$this-&gt;followsRequest-&gt;getFollowersByActorId($actor-&gt;getId())</code>
<code>$this-&gt;followsRequest-&gt;getFollowingByActorId($actor-&gt;getId())</code>
</InvalidReturnStatement>
<InvalidReturnType occurrences="2">
<code>Person[]</code>
<code>Person[]</code>
</InvalidReturnType>
</file>
<file src="lib/Service/HashtagService.php">
<InvalidReturnStatement occurrences="1">
<code>$result</code>
</InvalidReturnStatement>
<InvalidReturnType occurrences="1">
<code>Stream[]</code>
</InvalidReturnType>
<InvalidArgument occurrences="8"/>
</file>
<file src="lib/Service/PostService.php">
<InvalidNullableReturnType occurrences="1">
<code>ACore</code>
</InvalidNullableReturnType>
<NullableReturnStatement occurrences="1">
<code>$activity</code>
</NullableReturnStatement>
<UndefinedMethod occurrences="2">
<code>setAttributedTo</code>
<code>setContent</code>
</UndefinedMethod>
<TypeDoesNotContainType occurrences="1">
<code>is_array($_FILES['attachments']['error'])</code>
</TypeDoesNotContainType>
</file>
<file src="lib/Service/SearchService.php">
<InvalidOperand occurrences="3">
@ -131,9 +86,4 @@
<code>$username === null</code>
</TypeDoesNotContainNull>
</file>
<file src="lib/Tools/Traits/TNCLogger.php">
<UndefinedClass occurrences="1">
<code>HintException</code>
</UndefinedClass>
</file>
</files>