Skip to content

Commit

Permalink
🔀 Merge pull request #173 from Lissy93/FIX/section-visibility
Browse files Browse the repository at this point in the history
[BUG-FIX] Conditional visibility of sections for users
Closes  #172
  • Loading branch information
Lissy93 authored Aug 21, 2021
2 parents fa56446 + 007a13d commit 053c55c
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 45 deletions.
47 changes: 47 additions & 0 deletions docs/alternate-views.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Alternate Views & Opening Methods

## Views
As well as the default start view, Dashy has several other start pages, for different tasks. You can switch views with the view-switcher button in the top-right, or set a default starting view using the `appConfig.startingView` attribute (can be `default`, `minimal` or `workspace`).

### Default
This is the main page that you will land on when you first launch the application. Here all of your sections and items will be visible, you can modify settings and search + launch your applications.

<p align="center">
<b>Example of Default View</b><br>
<img width="800" src="https://i.ibb.co/L8YbNNc/dashy-demo2.gif" alt="Demo" />
</p>

### Workspace
The workspace view displays your links in a sidebar on the left-hand side, and apps are launched within Dashy. This enables you to use all of your self-hosted apps from one place, and makes multi-tasking easy.

In the workspace view, you can keep previously opened websites/ apps open in the background, by setting `appConfig.enableMultiTasking: true`. This comes at the cost of performance, but does mean that your session with each app is preserved, enabling you to quickly switch between your apps.

<p align="center">
<b>Example of Workspace View</b><br>
<img alt="Workspace view demo" src="https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/workspace-demo.gif" width="800" />
</p>

### Minimal View
The minimal view aims to be super fast and simple, and can be used as a browser startpage. Items are grouped into a tab view, and the last opened tab will be remembered. Similar to the main view, you can search and launch items just by typing, and right-clicking will show more options.

<p align="center">
<b>Example of Minimal View</b><br>
<img alt="Workspace view demo" src="https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/minimal-view-demo.gif" width="800" />
</p>

## Opening Methods

Dashy supports several different ways to launch your apps. The default opening method for each app can be specified using the `target` attribute, with a value of one of the following:

- `sametab` - The app will be launched in the current tab
- `newtab` - The app will be launched in a new tab
- `modal` - Launch app in a resizable/ movable popup modal on the current page
- `workspace` - Changes to Workspace view, and launches app

Even if the target is not set (or is set to `sametab`), you can still launch any given app in an alternative method: Alt + Click will open the modal, and Ctrl + Click will open in a new tab. You can also right-click on any item to see all options (as seen in the screenshot below). This custom context menu can be disabled by setting `appConfig.disableContextMenu: true`.

<p align="center">
<img width="500" src="https://i.ibb.co/vmZdSRt/dashy-context-menu-2.png" />
</p>

If you get a 'Refused to Connect' error in the modal or workspace views, then the target app has it's X-Frame-Options HTTP set to block requests from embedded content. You can easily fix this by setting this header to ALLOW, for instructions on how to do so, see the [Troubleshooting Docs](/docs/troubleshooting.md#refused-to-connect-in-modal-or-workspace-view).
33 changes: 31 additions & 2 deletions docs/privacy.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ For privacy and security tips, check out another project of mine: **[Personal Se
By default, Dashy will not make any external requests, unless you configure it to. Some features (which are all off by default) do require internat access, and this section outlines those features, the services used, and links to their privacy policies.

### Font Awesome
If either sections or items are using font-awesome icons, then these will be fetched directly from font-awesome on page load.
If either any of your sections or items are using font-awesome icons, then these will be fetched directly from font-awesome on page load. See the [Font Awesome Privacy Policy](https://fontawesome.com/privacy) for more info.

### Favicon Fetching
If an item's icon is set to `favicon`, then it will be auto-fetched from the corresponding URL. Since not all websites have their icon located at `/favicon.ico`, and if they do, it's often very low resolution (like `16 x 16 px`). Therefore, the default behavior is for Dashy to check if the URL is public, and if so will use an API to fetch the favicon. For self-hosted services, the favion will be fetched from the default path, and no external requests will be made.
Expand Down Expand Up @@ -41,10 +41,39 @@ If you need to monitor bugs yourself, then you can [self-host your own Sentry Se

---

## Local Storage
In order for user preferences to be persisted between sessions, certain data needs to be stored in the browsers local storage. No personal info is kept here, none of this data can be accessed by other domains, and no data is ever sent to any server without your prior consent.
You can view your browsers session storage by opening up the dev tools (F12) --> Application --> Storage.

The following section outlines all data that is stored in the browsers, as cookies or local storage.

#### Cookies
- `AUTH_TOKEN` - A unique token, generated from a hash of users credentials, to verify they are authenticated. Only used when auth is enabled

#### Local Storage
- `LANGUAGE` - The locale to show app text in
- `HIDE_WELCOME_BANNER` - Set to true once user dismissed welcome message, so that it's not shown again
- `LAYOUT_ORIENTATION` - Preferred section layout, either horizontal, vertical or auto
- `COLLAPSE_STATE` - Remembers which sections are collapsed
- `ICON_SIZE` - Size of items, either small, medium or large
- `THEME: 'theme` - Users applied theme
- `CUSTOM_COLORS` - Any color modifications made to a given theme
- `BACKUP_ID` - If a backup has been made, the ID is stored here
- `BACKUP_HASH` - A unique hash of the previous backups meta data
- `HIDE_SETTINGS` - Lets user hide or show the settings menu
- `USERNAME` - If user logged in, store username in order to welcome them
- `CONF_SECTIONS` - Array of sections, only used when user applies changes locally
- `PAGE_INFO` - Config page info, only used when user applies changes locally
- `APP_CONFIG` - App config, only used when user applies changes locally

---

## Dependencies
As with most web projects, Dashy relies on several [dependencies](https://github.com/Lissy93/dashy/blob/master/docs/credits.md#dependencies-). For links to each, and a breakdown of their licenses, please see [Legal](https://github.com/Lissy93/dashy/blob/master/.github/LEGAL.md).

Dependencies can introduce security vulnerabilities, but since all these packages are open source any issues are usually very quickly spotted. Dashy is using Snyk for dependency security monitoring, and you can see [the latest report here](https://snyk.io/test/github/lissy93/dashy).
Dependencies can introduce security vulnerabilities, but since all these packages are open source any issues are usually very quickly spotted. Dashy is using Snyk for dependency security monitoring, and you can see [the latest report here](https://snyk.io/test/github/lissy93/dashy). If any issue is detected by Snyk, a note about it will appear at the top of the Reamde, and will usually be fixed within 48 hours.

Note that packages listed under `deDependencies` section are only used for building the project, and are not included in the production environment.

---

Expand Down
6 changes: 4 additions & 2 deletions docs/readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
![Dashy Docs](https://i.ibb.co/4mdNf7M/heading-docs.png)

### Running Dashy
- [Deployment](/docs/deployment.md) - Getting Dashy up and running
- [Quick Start](/docs/quick-start.md) - TDLR guide on getting Dashy up and running
- [Deployment](/docs/deployment.md) - Full guide on deploying Dashy either locally or online
- [Configuring](/docs/configuring.md) - Complete list of all available options in the config file
- [App Management](/docs/management.md) - Managing your app, updating, security, web server configuration, etc
- [Troubleshooting](/docs/troubleshooting.md) - Common errors and problems, and how to fix them
Expand All @@ -15,9 +16,10 @@

### Feature Docs
- [Authentication](/docs/authentication.md) - Guide to setting up authentication to protect your dashboard
- [Alternate Views](/docs/alternate-views.md) - Outline of available pages / views and item opening methods
- [Backup & Restore](/docs/backup-restore.md) - Guide to Dashy's cloud sync feature
- [Icons](/docs/icons.md) - Outline of all available icon types for sections and items
- [Language Switching](/docs/multi-language-support.md)
- [Language Switching](/docs/multi-language-support.md) - Details on how to switch language, or add a new locale
- [Status Indicators](/docs/status-indicators.md) - Using Dashy to monitor uptime and status of your apps
- [Theming](/docs/theming.md) - Complete guide to applying, writing and modifying themes and styles

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
:rows="displayData.rows"
:color="displayData.color"
:customStyles="displayData.customStyles"
v-if="isSectionVisibleToUser()"
>
<div v-if="!items || items.length < 1" class="no-items">
No Items to Show Yet
Expand Down Expand Up @@ -52,10 +51,9 @@
import Item from '@/components/LinkItems/Item.vue';
import Collapsable from '@/components/LinkItems/Collapsable.vue';
import IframeModal from '@/components/LinkItems/IframeModal.vue';
import { getCurrentUser, isLoggedInAsGuest } from '@/utils/Auth';
export default {
name: 'ItemGroup',
name: 'Section',
inject: ['config'],
props: {
groupId: String,
Expand Down Expand Up @@ -87,9 +85,6 @@ export default {
? `grid-template-rows: repeat(${this.displayData.itemCountY}, 1fr);` : '';
return styles;
},
currentUser() {
return getCurrentUser();
},
},
methods: {
/* Returns a unique lowercase string, based on name, for section ID */
Expand All @@ -116,35 +111,6 @@ export default {
if (interval < 1) interval = 0;
return interval;
},
/* Returns false if this section should not be rendered for the current user/ guest */
isSectionVisibleToUser() {
const determineVisibility = (visibilityList, currentUser) => {
let isFound = false;
visibilityList.forEach((userInList) => {
if (userInList.toLowerCase() === currentUser) isFound = true;
});
return isFound;
};
const checkVisiblity = () => {
if (!this.currentUser) return true;
const hideFor = this.displayData.hideForUsers || [];
const currentUser = this.currentUser.user.toLowerCase();
return !determineVisibility(hideFor, currentUser);
};
const checkHiddenability = () => {
if (!this.currentUser) return true;
const currentUser = this.currentUser.user.toLowerCase();
const showForUsers = this.displayData.showForUsers || [];
if (showForUsers.length < 1) return true;
return determineVisibility(showForUsers, currentUser);
};
const checkIfHideForGuest = () => {
const hideForGuest = this.displayData.hideForGuests;
const isGuest = isLoggedInAsGuest();
return !(hideForGuest && isGuest);
};
return checkVisiblity() && checkHiddenability() && checkIfHideForGuest();
},
},
};
</script>
Expand Down
54 changes: 54 additions & 0 deletions src/utils/CheckSectionVisibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* A helper function that filters all the sections based on current users permissions
* Checks each sections displayData for hideForUsers, showForUsers and hideForGuests
* Returns an array of sections that the current logged in user has permissions for
*/

// Import helper functions from auth, to get current user, and check if guest
import { getCurrentUser, isLoggedInAsGuest } from '@/utils/Auth';

/* Helper function, checks if a given username appears in a user array */
const determineVisibility = (visibilityList, cUsername) => {
let isFound = false;
visibilityList.forEach((userInList) => {
if (userInList.toLowerCase() === cUsername) isFound = true;
});
return isFound;
};

/* Returns false if this section should not be rendered for the current user/ guest */
const isSectionVisibleToUser = (displayData, currentUser, isGuest) => {
// Checks if user explicitly has access to a certain section
const checkVisiblity = () => {
if (!currentUser) return true;
const hideFor = displayData.hideForUsers || [];
const cUsername = currentUser.user.toLowerCase();
return !determineVisibility(hideFor, cUsername);
};
// Checks if user is explicitly prevented from viewing a certain section
const checkHiddenability = () => {
if (!currentUser) return true;
const cUsername = currentUser.user.toLowerCase();
const showForUsers = displayData.showForUsers || [];
if (showForUsers.length < 1) return true;
return determineVisibility(showForUsers, cUsername);
};
// Checks if the current user is a guest, and if section allows for guests
const checkIfHideForGuest = () => {
const hideForGuest = displayData.hideForGuests;
return !(hideForGuest && isGuest);
};
return checkVisiblity() && checkHiddenability() && checkIfHideForGuest();
};

/* Putting it all together, the function to export */
const checkSectionVisibility = (sections) => {
const currentUser = getCurrentUser(); // Get current user object
const isGuest = isLoggedInAsGuest(); // Check if current user is a guest
return sections.filter((currentSection) => {
const displayData = currentSection.displayData || {};
return isSectionVisibleToUser(displayData, currentUser, isGuest);
});
};

export default checkSectionVisibility;
7 changes: 6 additions & 1 deletion src/utils/ConfigHelpers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ConfigAccumulator from '@/utils/ConfigAccumalator';
import filterUserSections from '@/utils/CheckSectionVisibility';
import { languages } from '@/utils/languages';
import {
visibleComponents,
Expand All @@ -13,7 +14,11 @@ import {
*/
export const config = (() => {
const Accumulator = new ConfigAccumulator();
return Accumulator.config();
return {
appConfig: Accumulator.appConfig(),
pageInfo: Accumulator.pageInfo(),
sections: filterUserSections(Accumulator.sections()),
};
})();

/**
Expand Down
10 changes: 5 additions & 5 deletions src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<!-- Main content, section for each group of items -->
<div v-if="checkTheresData(sections)"
:class="`item-group-container orientation-${layout} item-size-${itemSizeBound}`">
<ItemGroup
<Section
v-for="(section, index) in getSections(sections)"
:key="index"
:title="section.name"
Expand All @@ -42,7 +42,7 @@
<script>
import SettingsContainer from '@/components/Settings/SettingsContainer.vue';
import ItemGroup from '@/components/LinkItems/ItemGroup.vue';
import Section from '@/components/LinkItems/Section.vue';
import Defaults, { localStorageKeys, iconCdns } from '@/utils/defaults';
export default {
Expand All @@ -54,7 +54,7 @@ export default {
},
components: {
SettingsContainer,
ItemGroup,
Section,
},
data: () => ({
searchValue: '',
Expand Down Expand Up @@ -130,11 +130,11 @@ export default {
getDisplayData(section) {
return !section.displayData ? {} : section.displayData;
},
/* Sets layout attribute, which is used by ItemGroup */
/* Sets layout attribute, which is used by Section */
setLayoutOrientation(layout) {
this.layoutOrientation = layout;
},
/* Sets item size attribute, which is used by ItemGroup */
/* Sets item size attribute, which is used by Section */
setItemSize(itemSize) {
this.iconSize = itemSize;
},
Expand Down

0 comments on commit 053c55c

Please sign in to comment.