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

feat: Fully permissions based links #70

Merged
merged 32 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9ed2c94
chore: refactor to start using model visibility, add first tests
imorland Sep 19, 2024
8aaac32
Apply fixes from StyleCI
StyleCIBot Sep 19, 2024
57465f5
fix: phpstan error
imorland Sep 19, 2024
acf9f1d
simply test
imorland Sep 19, 2024
20b7f2e
Apply fixes from StyleCI
StyleCIBot Sep 19, 2024
c5ec219
caching for guest users only
imorland Sep 19, 2024
7e22378
remove comment
imorland Sep 19, 2024
92cea20
create link test
imorland Sep 19, 2024
8f831f7
Apply fixes from StyleCI
StyleCIBot Sep 19, 2024
9b1c5ae
more tests
imorland Sep 19, 2024
e4b3c47
Apply fixes from StyleCI
StyleCIBot Sep 19, 2024
4b75ecc
add minimal db entry
imorland Sep 19, 2024
8b4044b
wip
imorland Oct 10, 2024
46cb7b2
Apply fixes from StyleCI
StyleCIBot Oct 10, 2024
bab506a
format
imorland Oct 10, 2024
f51f7bd
seems to be working
imorland Oct 10, 2024
d7484b1
Apply fixes from StyleCI
StyleCIBot Oct 10, 2024
c02e537
now working correctly
imorland Oct 11, 2024
952a89c
Apply fixes from StyleCI
StyleCIBot Oct 11, 2024
bfa2aa3
cleanup, remove commented code, etc
imorland Oct 11, 2024
84a309a
add translations
imorland Oct 11, 2024
2a7e91c
add migrate data migration, drop visibility col
imorland Oct 11, 2024
8b6a45a
Apply fixes from StyleCI
StyleCIBot Oct 11, 2024
883a816
add guestOnly property
imorland Oct 11, 2024
94c6d43
add translation, clean up
imorland Oct 11, 2024
dc975af
Apply fixes from StyleCI
StyleCIBot Oct 11, 2024
45c17c1
chore: update copy to reflect actual behaiour
imorland Oct 11, 2024
da0c9e1
Update js/src/admin/components/EditLinkModal.js
imorland Oct 14, 2024
a929984
Update js/src/admin/components/EditLinkModal.js
imorland Oct 14, 2024
7af61ef
Update js/src/admin/components/EditLinkModal.js
imorland Oct 14, 2024
ee97387
use translations where possible
imorland Oct 14, 2024
9385875
fix: check for existence of column
imorland Oct 14, 2024
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: 1 addition & 1 deletion .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
run:
uses: flarum/framework/.github/workflows/[email protected]
with:
enable_backend_testing: false
enable_backend_testing: true
enable_phpstan: true

backend_directory: .
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
composer.lock
vendor
.phpunit.result.cache
28 changes: 23 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.8.3"
"flarum/core": "^1.8.6"
},
"authors": [
{
Expand Down Expand Up @@ -51,19 +51,37 @@
},
"flarum-cli": {
"modules": {
"githubActions": true
"githubActions": true,
"backendTesting": true
}
}
},
"require-dev": {
"flarum/phpstan": "*",
"flarum/tags": "*"
"flarum/tags": "*",
"flarum/testing": "^1.0.0"
},
"scripts": {
"analyse:phpstan": "phpstan analyse",
"clear-cache:phpstan": "phpstan clear-result-cache"
"clear-cache:phpstan": "phpstan clear-result-cache",
"test": [
"@test:unit",
"@test:integration"
],
"test:unit": "phpunit -c tests/phpunit.unit.xml",
"test:integration": "phpunit -c tests/phpunit.integration.xml",
"test:setup": "@php tests/integration/setup.php"
},
"scripts-descriptions": {
"analyse:phpstan": "Run static analysis"
"analyse:phpstan": "Run static analysis",
"test": "Runs all tests.",
"test:unit": "Runs all unit tests.",
"test:integration": "Runs all integration tests.",
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
},
"autoload-dev": {
"psr-4": {
"FoF\\Links\\Tests\\": "tests/"
}
}
}
17 changes: 15 additions & 2 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
* file that was distributed with this source code.
*/

namespace FoF\Links;

use Flarum\Api\Controller\ShowForumController;
use Flarum\Api\Serializer\ForumSerializer;
use Flarum\Extend;
use FoF\Links\Api\Controller;
use FoF\Links\Api\Serializer\LinkSerializer;
use FoF\Links\LoadForumLinksRelationship;
use FoF\Links\Event\PermissionChanged;

return [
new Extend\Locales(__DIR__.'/locale'),
Expand All @@ -31,11 +33,16 @@
->post('/links', 'links.create', Controller\CreateLinkController::class)
->post('/links/order', 'links.order', Controller\OrderLinksController::class)
->patch('/links/{id}', 'links.update', Controller\UpdateLinkController::class)
->delete('/links/{id}', 'links.delete', Controller\DeleteLinkController::class),
->delete('/links/{id}', 'links.delete', Controller\DeleteLinkController::class)
->remove('permission')
->post('/permission', 'permission', Controller\SetPermissionController::class),

(new Extend\ApiSerializer(ForumSerializer::class))
->hasMany('links', LinkSerializer::class),

(new Extend\Event())
->listen(PermissionChanged::class, Listener\LinkPermissionChanged::class),

(new Extend\ApiController(ShowForumController::class))
->addInclude(['links', 'links.parent'])
->prepareDataForSerialization(LoadForumLinksRelationship::class),
Expand All @@ -44,4 +51,10 @@
->registerLessConfigVar('fof-links-show-only-icons-on-mobile', 'fof-links.show_icons_only_on_mobile', function ($value) {
return $value ? 'true' : 'false';
}),

(new Extend\ModelVisibility(Link::class))
->scope(Access\ScopeLinkVisibility::class),

(new Extend\Policy())
->modelPolicy(Link::class, Access\LinkPolicy::class),
];
84 changes: 54 additions & 30 deletions js/src/admin/components/EditLinkModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import Stream from 'flarum/common/utils/Stream';
import icon from 'flarum/common/helpers/icon';
import withAttr from 'flarum/common/utils/withAttr';
import ItemList from 'flarum/common/utils/ItemList';
import Select from 'flarum/common/components/Select';

import PermissionDropdown from 'flarum/admin/components/PermissionDropdown';
import Alert from 'flarum/common/components/Alert';
import Group from 'flarum/common/models/Group';
import Link from 'flarum/common/components/Link';
/**
* The `EditlinksModal` component shows a modal dialog which allows the user
* to create or edit a link.
Expand All @@ -26,7 +28,7 @@ export default class EditlinksModal extends Modal {
this.isInternal = Stream(this.link.isInternal() && true);
this.isNewtab = Stream(this.link.isNewtab() && true);
this.useRelMe = Stream(this.link.useRelMe() && true);
this.visibility = Stream(this.link.visibility() || 'everyone');
this.guestOnly = Stream(this.link.guestOnly() && true);

if (this.isInternal()) {
this.updateInternalUrl();
Expand Down Expand Up @@ -65,9 +67,54 @@ export default class EditlinksModal extends Modal {
);
}

getGroup(id) {
return app.store.getById('groups', id);
}

items() {
const items = new ItemList();

const permissionPriority = 200;
if (this.link.exists) {
const adminLabel = this.getGroup(Group.ADMINISTRATOR_ID).nameSingular();
const guestLabel = this.getGroup(Group.GUEST_ID).namePlural();
const everyoneLabel = app.translator.trans('core.admin.permissions_controls.everyone_button');

items.add(
'visibility-permission',
[
<div className="Form-group">
<label>{app.translator.trans('fof-links.admin.edit_link.visibility.label')}</label>
<p className="helpText">{app.translator.trans('fof-links.admin.edit_link.visibility.help', { admin: adminLabel })}</p>
<PermissionDropdown permission={`link${this.link.id()}.view`} allowGuest={true} />
</div>,
<div className="Form-group">
<label className="checkbox">
<input type="checkbox" value="1" bidi={this.guestOnly} />
{app.translator.trans('fof-links.admin.edit_link.visibility.guest-only.label', { guest: guestLabel })}
</label>
<p className="helpText">
{app.translator.trans('fof-links.admin.edit_link.visibility.guest-only.help', { guest: guestLabel, everyone: everyoneLabel })}
</p>
</div>,
],
permissionPriority
);
} else {
items.add(
'visibility-permission-disabled',
[
<div className="Form-group">
<label>{app.translator.trans('fof-links.admin.edit_link.visibility.label')}</label>
<Alert dismissible={false} type="warning">
{app.translator.trans('fof-links.admin.edit_link.visibility.help-disabled')}
</Alert>
</div>,
],
permissionPriority
);
}

items.add(
'title',
[
Expand All @@ -86,7 +133,9 @@ export default class EditlinksModal extends Modal {
<label>{app.translator.trans('fof-links.admin.edit_link.icon_label')}</label>
<div className="helpText">
{app.translator.trans('fof-links.admin.edit_link.icon_text', {
a: <a href="https://fontawesome.com/v5/search?o=r&m=free" tabindex="-1" />,
a: (
<Link className="Button--link" href={app.refs.fontawesome} tabindex="-1" external={true} target="_blank" rel="noopener noreferrer" />
),
})}
<br />
{app.translator.trans('fof-links.admin.edit_link.icon_additional_text')}
Expand Down Expand Up @@ -170,21 +219,6 @@ export default class EditlinksModal extends Modal {
40
);

items.add(
'visibility',
[
<div className="Form-group">
<label>{app.translator.trans('fof-links.admin.edit_link.visibility')}</label>
{Select.component({
value: this.visibility(),
onchange: this.visibility,
options: this.typeOptions(),
})}
</div>,
],
20
);

items.add(
'actions',
[
Expand Down Expand Up @@ -212,16 +246,6 @@ export default class EditlinksModal extends Modal {
return items;
}

typeOptions() {
let opts;
opts = ['everyone', 'members', 'guests'].reduce((o, key) => {
o[key] = app.translator.trans(`fof-links.admin.edit_link.${key}-label`);

return o;
}, {});
return opts;
}

submitData() {
return {
title: this.itemTitle(),
Expand All @@ -230,7 +254,7 @@ export default class EditlinksModal extends Modal {
isInternal: this.isInternal(),
isNewtab: this.isNewtab(),
useRelMe: this.useRelMe(),
visibility: this.visibility(),
guestOnly: this.guestOnly(),
};
}

Expand Down
8 changes: 6 additions & 2 deletions js/src/common/models/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export default class Link extends Model {
return Model.hasOne<Link>('parent').call(this);
}

visibility() {
return Model.attribute<string>('visibility').call(this);
isRestricted() {
return Model.attribute<boolean>('isRestricted').call(this);
}

guestOnly() {
return Model.attribute<boolean>('guestOnly').call(this);
}
}
6 changes: 6 additions & 0 deletions js/src/forum/extendHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ export default function extendHeader() {
extend(HeaderPrimary.prototype, 'items', function (items: ItemList<Mithril.Children>) {
const allLinks = app.store.all<Link>('links');
const links = allLinks.filter((link) => !link.isChild());

const addLink = (parent: Link | null | undefined) => {
const hasChildren = allLinks.some((link) => link.parent() == parent);

// If the link has no URL and no children, do not display it.
if (!parent?.url() && !hasChildren) {
return;
}

items.add(`link${parent?.id()}`, hasChildren ? LinkDropdown.component({ link: parent }) : LinkItem.component({ link: parent }));
};

Expand Down
11 changes: 7 additions & 4 deletions locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ fof-links:
edit_link:
delete_link_button: Delete Link
delete_link_confirmation: "Are you sure you want to delete this link?"
everyone-label: Everyone
guests-label: Guests
internal_link: "Is it an internal link?"
members-label: Registered users
open_newtab: "Open link in new tab"
submit_button: => core.ref.save_changes
title: => fof-links.ref.create_link
Expand All @@ -27,7 +24,13 @@ fof-links:
url_label: => fof-links.ref.url
url_placeholder: => fof-links.ref.url
use_rel_me: Add <code>rel="me"</code> attribute for identity verification on other sites
visibility: Link visibility
visibility:
help: Links by default are visible to only <code>{admin}</code> users. Adjust the permissions to specify who can see this link.
help-disabled: Save the link before changing visibility settings.
label: Link visibility
guest-only:
label: "{guest} only?"
help: "Only {guest} can see this link. The permission above should be set to '{everyone}'."

# These strings are used in the Links page.
links:
Expand Down
4 changes: 4 additions & 0 deletions migrations/2020_03_16_000000_add_child_links.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
});
},
'down' => function (Builder $schema) {
if (!$schema->hasColumn('links', 'parent_id')) {
return;
}

$schema->table('links', function (Blueprint $table) {
$table->dropForeign(['parent_id']);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/*
* This file is part of fof/links.
*
* Copyright (c) FriendsOfFlarum.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use Flarum\Database\Migration;

return Migration::addColumns('links', [
'is_restricted' => ['boolean', 'default' => 0],
]);
16 changes: 16 additions & 0 deletions migrations/2024_10_11_000000_add_guest_only_to_links_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/*
* This file is part of fof/links.
*
* Copyright (c) FriendsOfFlarum.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use Flarum\Database\Migration;

return Migration::addColumns('links', [
'guest_only' => ['boolean', 'default' => 0],
]);
Loading
Loading