Skip to content

Commit

Permalink
feat: Fully permissions based links (#70)
Browse files Browse the repository at this point in the history
* chore: refactor to start using model visibility, add first tests

* Apply fixes from StyleCI

* fix: phpstan error

* simply test

* Apply fixes from StyleCI

* caching for guest users only

* remove comment

* create link test

* Apply fixes from StyleCI

* more tests

* Apply fixes from StyleCI

* add minimal db entry

* wip

* Apply fixes from StyleCI

* format

* seems to be working

* Apply fixes from StyleCI

* now working correctly

* Apply fixes from StyleCI

* cleanup, remove commented code, etc

* add translations

* add migrate data migration, drop visibility col

* Apply fixes from StyleCI

* add guestOnly property

* add translation, clean up

* Apply fixes from StyleCI

* chore: update copy to reflect actual behaiour

* Update js/src/admin/components/EditLinkModal.js

Co-authored-by: Davide Iadeluca <[email protected]>

* Update js/src/admin/components/EditLinkModal.js

Co-authored-by: Davide Iadeluca <[email protected]>

* Update js/src/admin/components/EditLinkModal.js

Co-authored-by: Davide Iadeluca <[email protected]>

* use translations where possible

* fix: check for existence of column

---------

Co-authored-by: StyleCI Bot <[email protected]>
Co-authored-by: Davide Iadeluca <[email protected]>
  • Loading branch information
3 people authored Oct 14, 2024
1 parent 8087cea commit 652a948
Show file tree
Hide file tree
Showing 37 changed files with 1,463 additions and 109 deletions.
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
16 changes: 16 additions & 0 deletions migrations/2024_10_10_000000-add_is_restricted_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', [
'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

0 comments on commit 652a948

Please sign in to comment.