Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing bookmarks #1095

Merged
merged 7 commits into from
Oct 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ on:
branches:
- main
- develop
- dev/new_features
push:
branches:
- main
- dev/new_features
tags:
- 'v*'

Expand Down
40 changes: 40 additions & 0 deletions assets/controllers/subject_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,46 @@ export default class extends Controller {
}
}

/**
* Calls the address attached to the nearest link node. Replaces the outer html of the nearest `cssclass` parameter
* with the response from the link
*/
async linkCallback(event) {
const { cssclass: cssClass, refreshlink: refreshLink, refreshselector: refreshSelector } = event.params
event.preventDefault();

const a = event.target.closest('a');

try {
this.loadingValue = true;

let response = await fetch(a.href, {
method: 'GET',
});

response = await ok(response);
response = await response.json();

event.target.closest(`.${cssClass}`).outerHTML = response.html;

const refreshElement = this.element.querySelector(refreshSelector)
console.log("linkCallback refresh stuff", refreshLink, refreshSelector, refreshElement)

if (!!refreshLink && refreshLink !== "" && !!refreshElement) {
let response = await fetch(refreshLink, {
method: 'GET',
});

response = await ok(response);
response = await response.json();
refreshElement.outerHTML = response.html;
}
} catch (e) {
} finally {
this.loadingValue = false;
}
}

loadingValueChanged(val) {
const submitButton = this.containerTarget.querySelector('form button[type="submit"]');

Expand Down
3 changes: 3 additions & 0 deletions assets/styles/app.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import '@fortawesome/fontawesome-free/scss/fontawesome';
@import '@fortawesome/fontawesome-free/scss/solid';
@import '@fortawesome/fontawesome-free/scss/regular';
@import '@fortawesome/fontawesome-free/scss/brands';
@import 'simple-icons-font/font/simple-icons';
@import 'glightbox/dist/css/glightbox.min.css';
Expand All @@ -21,6 +22,7 @@
@import 'layout/alerts';
@import 'layout/forms';
@import 'layout/images';
@import 'layout/icons';
@import 'components/announcement';
@import 'components/topbar';
@import 'components/header';
Expand Down Expand Up @@ -51,6 +53,7 @@
@import 'components/settings_row';
@import 'pages/post_single';
@import 'pages/post_front';
@import 'pages/page_bookmarks';
@import 'themes/kbin';
@import 'themes/default';
@import 'themes/solarized';
Expand Down
3 changes: 3 additions & 0 deletions assets/styles/layout/_icons.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
i.active {
color: var(--kbin-color-icon-active, orange);
}
6 changes: 6 additions & 0 deletions assets/styles/pages/page_bookmarks.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.page-bookmarks {
.entry, .entry-comment, .post, .post-comment, .comment {
margin-top: 0!important;
margin-bottom: .5em!important;
}
}
71 changes: 71 additions & 0 deletions config/kbin_routes/bookmark.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
bookmark_front:
controller: App\Controller\BookmarkListController::front
defaults: { sortBy: hot, time: '∞', federation: all }
path: /bookmark-lists/show/{list}/{sortBy}/{time}/{federation}
methods: [GET]
requirements: &front_requirement
sortBy: "%default_sort_options%"
time: "%default_time_options%"
federation: "%default_federation_options%"

bookmark_lists:
controller: App\Controller\BookmarkListController::list
path: /bookmark-lists
methods: [GET, POST]

bookmark_lists_menu_refresh_status:
controller: App\Controller\BookmarkListController::subjectBookmarkMenuListRefresh
path: /blr/{subject_id}/{subject_type}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]

bookmark_lists_make_default:
controller: App\Controller\BookmarkListController::makeDefault
path: /bookmark-lists/makeDefault
methods: [GET]

bookmark_lists_edit_list:
controller: App\Controller\BookmarkListController::editList
path: /bookmark-lists/editList/{list}
methods: [GET, POST]

bookmark_lists_delete_list:
controller: App\Controller\BookmarkListController::deleteList
path: /bookmark-lists/deleteList/{list}
methods: [GET]

subject_bookmark_standard:
controller: App\Controller\BookmarkController::subjectBookmarkStandard
path: /bos/{subject_id}/{subject_type}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]

subject_bookmark_refresh_status:
controller: App\Controller\BookmarkController::subjectBookmarkRefresh
path: /bor/{subject_id}/{subject_type}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]

subject_bookmark_to_list:
controller: App\Controller\BookmarkController::subjectBookmarkToList
path: /bol/{subject_id}/{subject_type}/{list}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]

subject_remove_bookmarks:
controller: App\Controller\BookmarkController::subjectRemoveBookmarks
path: /rbo/{subject_id}/{subject_type}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]

subject_remove_bookmark_from_list:
controller: App\Controller\BookmarkController::subjectRemoveBookmarkFromList
path: /rbol/{subject_id}/{subject_type}/{list}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]
61 changes: 61 additions & 0 deletions config/kbin_routes/bookmark_api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
api_bookmark_front:
controller: App\Controller\Api\Bookmark\BookmarkListApiController::front
path: /api/bookmark-lists/show
methods: [GET]
format: json

api_bookmark_lists:
controller: App\Controller\Api\Bookmark\BookmarkListApiController::list
path: /api/bookmark-lists
methods: [GET]
format: json

api_bookmark_lists_make_default:
controller: App\Controller\Api\Bookmark\BookmarkListApiController::makeDefault
path: /api/bookmark-lists/{list_name}/makeDefault
methods: [GET]
format: json

api_bookmark_lists_edit_list:
controller: App\Controller\Api\Bookmark\BookmarkListApiController::editList
path: /api/bookmark-lists/{list_name}
methods: [POST]
format: json

api_bookmark_lists_delete_list:
controller: App\Controller\Api\Bookmark\BookmarkListApiController::deleteList
path: /api/bookmark-lists/{list_name}
methods: [DELETE]
format: json

api_subject_bookmark_standard:
controller: App\Controller\Api\Bookmark\BookmarkApiController::subjectBookmarkStandard
path: /api/bos/{subject_id}/{subject_type}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]
format: json

api_subject_bookmark_to_list:
controller: App\Controller\Api\Bookmark\BookmarkApiController::subjectBookmarkToList
path: /api/bol/{subject_id}/{subject_type}/{list_name}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]
format: json

api_subject_remove_bookmarks:
controller: App\Controller\Api\Bookmark\BookmarkApiController::subjectRemoveBookmarks
path: /api/rbo/{subject_id}/{subject_type}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]
format: json

api_subject_remove_bookmark_from_list:
controller: App\Controller\Api\Bookmark\BookmarkApiController::subjectRemoveBookmarkFromList
path: /api/rbol/{subject_id}/{subject_type}/{list_name}
requirements:
subject_type: "%default_subject_type_options%"
methods: [ GET ]
format: json
7 changes: 7 additions & 0 deletions config/packages/league_oauth2_server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ league_oauth2_server:
"user:profile",
"user:profile:read",
"user:profile:edit",
"user:bookmark",
"user:bookmark:add",
"user:bookmark:remove",
"user:bookmark:list",
"user:bookmark:list:read",
"user:bookmark:list:edit",
"user:bookmark:list:delete",
"user:message",
"user:message:read",
"user:message:create",
Expand Down
11 changes: 11 additions & 0 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ security:
'ROLE_OAUTH2_USER:OAUTH_CLIENTS:READ',
'ROLE_OAUTH2_USER:OAUTH_CLIENTS:EDIT',
]
'ROLE_OAUTH2_USER:BOOKMARK':
[
'ROLE_OAUTH2_USER:BOOKMARK:ADD',
'ROLE_OAUTH2_USER:BOOKMARK:REMOVE',
]
'ROLE_OAUTH2_USER:BOOKMARK_LIST':
[
'ROLE_OAUTH2_USER:BOOKMARK_LIST:READ',
'ROLE_OAUTH2_USER:BOOKMARK_LIST:EDIT',
'ROLE_OAUTH2_USER:BOOKMARK_LIST:DELETE',
]
'ROLE_OAUTH2_MODERATE':
[
'ROLE_OAUTH2_MODERATE:ENTRY',
Expand Down
1 change: 1 addition & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ parameters:
default_subscription_options: sub|fav|mod|all|home
default_federation_options: local|all
default_content_options: threads|microblog
default_subject_type_options: entry|entry_comment|post|post_comment

comment_sort_options: top|hot|active|newest|oldest

Expand Down
56 changes: 56 additions & 0 deletions migrations/Version20240831151328.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20240831151328 extends AbstractMigration
{
public function getDescription(): string
{
return 'This adds the bookmark and bookmark list tables';
}

public function up(Schema $schema): void
{
$this->addSql('CREATE SEQUENCE bookmark_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE SEQUENCE bookmark_list_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE bookmark (id INT NOT NULL, list_id INT NOT NULL, user_id INT NOT NULL, entry_id INT DEFAULT NULL, entry_comment_id INT DEFAULT NULL, post_id INT DEFAULT NULL, post_comment_id INT DEFAULT NULL, created_at TIMESTAMP(0) WITH TIME ZONE NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_DA62921D3DAE168B ON bookmark (list_id)');
$this->addSql('CREATE INDEX IDX_DA62921DA76ED395 ON bookmark (user_id)');
$this->addSql('CREATE INDEX IDX_DA62921DBA364942 ON bookmark (entry_id)');
$this->addSql('CREATE INDEX IDX_DA62921D60C33421 ON bookmark (entry_comment_id)');
$this->addSql('CREATE INDEX IDX_DA62921D4B89032C ON bookmark (post_id)');
$this->addSql('CREATE INDEX IDX_DA62921DDB1174D2 ON bookmark (post_comment_id)');
$this->addSql('CREATE UNIQUE INDEX bookmark_list_entry_entryComment_post_postComment_idx ON bookmark (list_id, entry_id, entry_comment_id, post_id, post_comment_id)');
$this->addSql('COMMENT ON COLUMN bookmark.created_at IS \'(DC2Type:datetimetz_immutable)\'');
$this->addSql('CREATE TABLE bookmark_list (id INT NOT NULL, user_id INT NOT NULL, name VARCHAR(255) NOT NULL, is_default BOOLEAN NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_A650C0C4A76ED395 ON bookmark_list (user_id)');
$this->addSql('CREATE UNIQUE INDEX UNIQ_A650C0C4A76ED3955E237E06 ON bookmark_list (user_id, name)');
$this->addSql('ALTER TABLE bookmark ADD CONSTRAINT FK_DA62921D3DAE168B FOREIGN KEY (list_id) REFERENCES bookmark_list (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE bookmark ADD CONSTRAINT FK_DA62921DA76ED395 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE bookmark ADD CONSTRAINT FK_DA62921DBA364942 FOREIGN KEY (entry_id) REFERENCES entry (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE bookmark ADD CONSTRAINT FK_DA62921D60C33421 FOREIGN KEY (entry_comment_id) REFERENCES entry_comment (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE bookmark ADD CONSTRAINT FK_DA62921D4B89032C FOREIGN KEY (post_id) REFERENCES post (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE bookmark ADD CONSTRAINT FK_DA62921DDB1174D2 FOREIGN KEY (post_comment_id) REFERENCES post_comment (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE bookmark_list ADD CONSTRAINT FK_A650C0C4A76ED395 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
}

public function down(Schema $schema): void
{
$this->addSql('DROP SEQUENCE bookmark_id_seq CASCADE');
$this->addSql('DROP SEQUENCE bookmark_list_id_seq CASCADE');
$this->addSql('ALTER TABLE bookmark DROP CONSTRAINT FK_DA62921D3DAE168B');
$this->addSql('ALTER TABLE bookmark DROP CONSTRAINT FK_DA62921DA76ED395');
$this->addSql('ALTER TABLE bookmark DROP CONSTRAINT FK_DA62921DBA364942');
$this->addSql('ALTER TABLE bookmark DROP CONSTRAINT FK_DA62921D60C33421');
$this->addSql('ALTER TABLE bookmark DROP CONSTRAINT FK_DA62921D4B89032C');
$this->addSql('ALTER TABLE bookmark DROP CONSTRAINT FK_DA62921DDB1174D2');
$this->addSql('ALTER TABLE bookmark_list DROP CONSTRAINT FK_A650C0C4A76ED395');
$this->addSql('DROP TABLE bookmark');
$this->addSql('DROP TABLE bookmark_list');
}
}
10 changes: 8 additions & 2 deletions src/Controller/Api/BaseApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
use App\Factory\PostCommentFactory;
use App\Factory\PostFactory;
use App\Form\Constraint\ImageConstraint;
use App\Repository\BookmarkListRepository;
use App\Repository\BookmarkRepository;
use App\Repository\Criteria;
use App\Repository\EntryCommentRepository;
use App\Repository\EntryRepository;
Expand All @@ -39,12 +41,13 @@
use App\Repository\PostRepository;
use App\Repository\TagLinkRepository;
use App\Schema\PaginationSchema;
use App\Service\BookmarkManager;
use App\Service\IpResolver;
use App\Service\ReportManager;
use Doctrine\ORM\EntityManagerInterface;
use League\Bundle\OAuth2ServerBundle\Model\AccessToken;
use League\Bundle\OAuth2ServerBundle\Security\Authentication\Token\OAuth2Token;
use Pagerfanta\Pagerfanta;
use Pagerfanta\PagerfantaInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
Expand Down Expand Up @@ -85,6 +88,9 @@ public function __construct(
protected readonly EntryCommentRepository $entryCommentRepository,
protected readonly PostRepository $postRepository,
protected readonly PostCommentRepository $postCommentRepository,
protected readonly BookmarkListRepository $bookmarkListRepository,
protected readonly BookmarkRepository $bookmarkRepository,
protected readonly BookmarkManager $bookmarkManager,
private readonly ImageRepository $imageRepository,
private readonly ReportManager $reportManager,
private readonly OAuth2ClientAccessRepository $clientAccessRepository,
Expand Down Expand Up @@ -189,7 +195,7 @@ public function getAccessToken(?OAuth2Token $oAuth2Token): ?AccessToken
->findOneBy(['identifier' => $oAuth2Token->getAttribute('access_token_id')]);
}

public function serializePaginated(array $serializedItems, Pagerfanta $pagerfanta): array
public function serializePaginated(array $serializedItems, PagerfantaInterface $pagerfanta): array
{
return [
'items' => $serializedItems,
Expand Down
Loading