Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • applications/etuc/hubl
  • applications/hubl
  • decentral1se/hubl
  • rngadam/hubl
  • jvtrudel/hubl
  • 3wc/hubl
6 results
Show changes
Commits on Source (1313)
Showing
with 1426 additions and 720 deletions
{
}
**/node_modules
config.json
.DS_Store
*.iml
*.swp
dist
.npm
# Test cache
cache
cypress/screenshots
cypress/videos
cache
.npm
.DS_Store
# Built files
.cache
dist
*-dist
dist-*
# Config specific files
config.json
config.*.json
!config.sample.json
# Client specific files
public/clients/*.css
!public/clients/sample.css
......@@ -2,14 +2,11 @@
stages:
- build
- test
- integration
- acceptance
- release
- deployment
- deploy
# default image for jobs
default:
image: node:11
image: node:22
# cache modules between jobs
cache:
......@@ -31,6 +28,8 @@ build:
expire_in: 1 day
paths:
- dist/
except:
- tags
tags:
- test
......@@ -38,229 +37,33 @@ build:
test:e2e:
stage: test
image: cypress/included:4.5.0
image:
name: cypress/included:13.15.2
entrypoint: [""]
services:
- name: ${CI_REGISTRY_IMAGE}/server:0.1
- name: ${CI_REGISTRY_IMAGE}/djangoldp:latest
before_script:
# install missing dependencies
- npm install -g sirv-cli
- npm install cypress-localstorage-commands
# making sure the process is orphan
- sirv dist --port 3000 > /dev/null 2>&1 &
- npm run serve -- -l silent &
script:
- cypress run -e CYPRESS_baseUrl=http://localhost:3000
- cypress run -e CYPRESS_baseUrl=http://localhost:4173
except:
- tags
tags:
- test
## VALIDATION ##
test1:
stage: integration
environment:
name: test1
url: https://test1.startinblox.com
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_TEST1" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* test1@astral.startinblox.com:~/front/
only:
- /^feature\/.*/
when: manual
tags:
- deploy
test2:
stage: integration
environment:
name: test2
url: https://test2.startinblox.com
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_TEST2" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* test2@astral.startinblox.com:~/front/
only:
- /^feature\/.*/
when: manual
tags:
- deploy
test3:
stage: integration
environment:
name: test3
url: https://test3.startinblox.com
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_TEST3" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* test3@astral.startinblox.com:~/front/
only:
- /^feature\/.*/
when: manual
tags:
- deploy
stg1:
stage: acceptance
environment:
name: stg1
url: https://stg1.startinblox.com
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_STG1" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* stg1@astral.startinblox.com:~/front/
only:
- /^release\/.*/
when: manual
tags:
- deploy
stg2:
stage: acceptance
environment:
name: stg2
url: https://stg2.startinblox.com
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_STG2" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* stg2@astral.startinblox.com:~/front/
only:
- /^release\/.*/
when: manual
tags:
- deploy
## RELEASE TAGGING ##
publish:
stage: release
script:
- npm install -g semantic-release@v17 @semantic-release/gitlab
- semantic-release
only:
- master
tags:
- deploy
## LIVE DEPLOYMENTS ##
community:
stage: deployment
environment:
name: community
url: https://community.startinblox.com
stage: deploy
image: node:22
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_COMMUNITY" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* community@astral.startinblox.com:~/front/
only:
- master
when: manual
tags:
- deploy
etuc:
stage: deployment
environment:
name: etuc
url: https://app.digitalplatformobservatory.org
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_DIGITALPLATFORMOBSERVATORY" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* etuc@ssh-etuc.happy-dev.fr:~/sib/www/
only:
- master
when: manual
tags:
- deploy
nantes:
stage: deployment
environment:
name: nantes
url: https://app.nantes.happy-dev.fr
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_NANTESHD" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* nantes@ssh-nantes.happy-dev.fr:~/sib/www/
only:
- master
when: manual
tags:
- deploy
paris:
stage: deployment
environment:
name: paris
url: https://app.paris.happy-dev.fr
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_PARIS" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* paris@ssh-paris.happy-dev.fr:~/sib/www/
only:
- master
when: manual
tags:
- deploy
toulouse:
stage: deployment
environment:
name: toulouse
url: https://smart-toulouse.happy-dev.fr
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_TOULOUSE" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* smart-toulouse@ssh-smart-toulouse.happy-dev.fr:~/www/
only:
- master
when: manual
tags:
- deploy
volumes:
stage: deployment
environment:
name: volumes
url: https://volumes.hubl.world
before_script:
- npm ci --cache .npm --prefer-offline --only=production
- npm install -g semantic-release@24 @semantic-release/gitlab@13
script:
- echo "$APP_CONFIG_VOLUMES" > config.json
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* volumes-sib@astral.startinblox.com:~/front/
- semantic-release
only:
- master
when: manual
tags:
- deploy
/label ~BUG
### What's happening?
*Describe in a few words what's happening*
### Steps to reproduce
*How one can reproduce the issue - this is very important*
1. Step 1
2. Step 2
3. Step 3
### Relevant logs and/or screenshots
*If possible, please add a screenshot.*
### Your Environment
(Include relevant details about the environment you experienced the bug in)
* Browser name and version:
* Operating System and version (desktop or mobile):
## What needs to be done?
/label ~Feature request
## Technical details
*Are there any technical details worth mentioning?*
## Test cases
*Describe here the tests needed in order to validate this feature*
1. Step 1
2. Step 2
3. Step 3
## Links & Mockups
1. Link to user story in wiki
2. other related Gitlab issues
## Checklist
<!---
Please, make sure you have changed the topic and also
described briefly what have you done. Thanks!
-->
I have done ...
- [ ] This commits targets only one specific issue
- [ ] The commit message follows our guidelines
- [ ] Tests for the changes have been added
- [ ] Docs have been added or updated
- [ ] I have assigned my architect to review this merge request
- [ ] I have checked how to create a [merge request]()
## Issues
<!---
Which issue this PR closes? It can close only one issue.
Which issues this PR references?
Please, specify all issues.
Format is: Closes #X or Refs #Y
Docs: https://docs.gitlab.com/ee/user/project/issues/closing_issues.html#via-merge-request
-->
## Time spent
<!---
This information is only required for statistics and analysis.
But we need to know exactly how much time you have spent,
please try to be as accurate as possible.
Format is: /spend 1h
Docs: https://docs.gitlab.com/ee/workflow/time_tracking.html
-->
/spend Xh
## Assignee
<!---
You need to assign your architect to review your merge request.
Format: /assign @username
Docs: https://docs.gitlab.com/ee/user/project/quick_actions.html
-->
/assign
## Feedback
<!---
Did you encounter any other problems you want to share with us?
Optional. Feel free to remove this section if you don't have any feedback.
-->
<!--- Thank you for you contribution! -->
{
"branches": [
"master",
{
"name": "beta",
"prerelease": true
}
],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{
"type": "major",
"release": "major"
},
{
"type": "minor",
"release": "minor"
},
{
"type": "*",
"release": "patch"
}
]
}
],
"@semantic-release/release-notes-generator",
"@semantic-release/gitlab"
]
}
\ No newline at end of file
{
"includePaths": ["node_modules"]
}
\ No newline at end of file
MIT License
Copyright (c) 2018 Startin blox
Copyright (c) 2024-PRESENT Startin blox
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
# Hubl
<h1 align="center">
<br>
<a href="https://startinblox.com"><img src="https://cdn.startinblox.com/logos/startinblox.png" alt="Orbit" width="200"></a>
<br>
</h1>
Hubl is the magic tool that allows the Freelance Network to thrive in a decentralized way.
<h4 align="center">A magic orchestrator that allows any Blox to thrive in a decentralized way, built on top of <a href="https://startinblox.com/" target="_blank">Startin'blox</a>.</h4>
<hr>
<p align="center">
<a href="https://git.startinblox.com/management/product-owners-sync/issues/"><img alt="create a feature request" src="https://img.shields.io/badge/%2B-feature%20request-blue" /></a>
<a href="https://git.startinblox.com/applications/orbit/issues/"><img alt="create an issue" src="https://img.shields.io/badge/%2B-issue-orange" /></a>
<a href="https://git.startinblox.com/applications/orbit/commits/master"><img alt="pipeline status" src="https://git.startinblox.com/applications/hubl/badges/master/pipeline.svg" /></a>
</p>
## Getting Started
......@@ -8,48 +20,38 @@ These instructions will get you a copy of the project up and running on your loc
### Prerequisites
To install Hubl, you'll need:
To install Orbit, you'll need:
* A Hubl Server (djangoldp>=0.6.42) with the appropriate modules
* A Prosody Server (with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/)
* A [DjangoLDP Server](https://git.startinblox.com/djangoldp-packages/server-manager/) (djangoldp>2)
* A [Prosody Server](https://prosody.im/) (with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/)) (optional)
* A SMTP Server (optional)
* NodeJS on your machine
* NodeJS (>=20) on your machine
Before diving in you have to check your Hubl Server supports the following LDP packages:
Before diving in you have to check your DjangoLDP Server supports the following LDP packages:
* djangoldp_account
* djangoldp_notification
* djangoldp_profile
* djangoldp_skill
* djangoldp_uploader
* oidc_provider: 'git+https://github.com/jblemee/django-oidc-provider.git@develop'
* an oidc_provider (eg: [django-webidoidc-provider](https://git.startinblox.com/djangoldp-packages/django-webidoidc-provider))
Those packages are given with the last stable version tested.
Refer to the [documentation to install a Hubl Server](https://git.startinblox.com/documentation/doc/wikis/devops/install_sib_server) with this configuration.
Refer to the [documentation to install a DjangoLDP Server](https://docs.startinblox.com/import_documentation/install-djangoldp-server.html) with this configuration.
## Build the application
In order to find your server(s) the client application needs to be assembled with the proper configuration.
Get the code of the Hubl on your machine:
Get the code of the Orbit on your machine:
```bash
git clone ...
cd hubl
cd orbit
npm install
```
Then create a `config.json` based on your needs, see Mandatory and Optional Modules on this page. For convienence a `config.sample.json` exists in the source.
Federated Hubl needs to use `config.sample.federated.json` example.
You can quickly update your API URI from the samples:
```
$ sed 's/http:\/\/localhost:8000/https:\/\/api.your-server.startinblox.com/' config.sample.json > config.json
```
Then build your new Hubl:
Then build your new Orbit:
```bash
npm run build
......@@ -65,82 +67,311 @@ Serve, watch files & rebuild on change with this command:
npm run watch
```
## Mandatory modules
Notice that you may have to restart the watcher for the config.json and for locales files.
### Multiple config.json
You can have as many `config.*.json` as you need.
Watch on a custom config file:
```bash
CONFIG_PATH='config.customName.json' npm run watch
```
Build with a custom config file:
By default, a Hubl includes only individual chat modules.
```bash
CONFIG_PATH='config.customName.json' npm run build
```
## Component, extensions, integrations?
The core concept of Orbit is to allow any webcomponent to integrate with Orbit **and** with any other Orbit-loaded component.
A **component** is a webcomponent, named by its `type` key.
It can have as many `parameters` as it needs. Orbit will translate them to `attributes` if needed (e.g., `dataSrc` => `data-src`), and will try to load any object directly on the component.
A `parameters` can point to `server://` or `federation://`, and Orbit will create a proper local resource for this attribute.
A component can be duplicated as many times as needed; Orbit will try to deduplicate any `route` using the `type` and the `uniq` key.
The `uniq` key is handled by Orbit; you can suggest one, and Orbit will do its best to use it.
An **extension** is a sub-component, it is also a web component, and the parent component will have to handle its integration within its scope.
E.g., an Event component within a Circle component allows plugging Events into one Circle.
An extension can have its own parameters and will not be related if also declared on Orbit's component list.
An **integration** is a link to another component; one component expects another component to behave in its presence.
E.g., a Directory component requires a `routing` integration to be properly handled by the `routing` component.
E.g., an Event component could mention a `dashboard` integration to declare a widget-like Event component, user-manageable, on the Dashboard (not implemented).
A component without an integration to another component is **not** expected to have any interaction with it.
On Server: `djangoldp_account`, `djangoldp_profile`, `djangoldp_notification`, `djangoldp_skill`, `djangoldp_upload`, `oidc_provider` packages
## Mandatory components
On `config.json`:
By default does not include any component.
On Server: `djangoldp_account`, `djangoldp_upload`, `django-webidoidc-provider` packages
On `config.json`:
```json
{
"xmpp": "https://jabber.happy-dev.fr/http-bind/",
"authority": "http://localhost:8000/",
"endpoints": {
"skills": "http://server.url/skills/",
"users": "http://server.url/users/",
"uploads": "http://server.url/upload/"
}
"client": {
"name": "Localhost",
"logo": "/images/logo.webp",
"server": "http://server"
},
"components": []
}
```
Where:
* `xmpp` is your Prosody with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/) configured on.
* `authority` is the OpenID Provider. Usually, if you use `djangoldp-account` it's the same as your djangoldp server.
* `endpoints.users` is the API endpoints for Users on your djangoldp server.
* `client.name` is the name of your Orbit
* `client.logo` is an URL to an image file
* `client.server` is the main data server of the client
* `components` is your modules declaration registry
### Optional personalisation
On `config.json`:
```json
"clientName": "Hubl",
"clientFavicon": "/images/favicon.png",
"clientLogo": "/images/logo.png",
"clientLogoHeight": "32px",
"clientCSS": "/path/to/custom.css",
"authorityName": "djangoldp-server-name"
{
"client": {
"favicon": "/images/favicon.webp",
"css": "/path/to/custom.css",
"defaultAvatar": "/path/to/image.webp",
"i18n": {
"lang": "fr",
"force": "false"
}
}
}
```
Where:
* `clientName` is the name of your Hubl.
* `clientFavicon` is an URL to a distant favicon
* `clientLogo` is an URL to a distant logo for your client
* `clientLogoHeight` allow a quick fix to manage different height logos
* `clientCSS` is an URL to a distant CSS that'll be the last one loaded by the Hubl
* `authorityName` is a visual name of your OpenID Provider
* `client.favicon` is an URL to a distant favicon
* `client.css` is an URL to a distant CSS that'll be the last one loaded by the Orbit. You can use an array of css files
* `client.defaultAvatar` is an URL to a distant image that'll be used instead of the default alien
* `client.i18n.lang` is the fallback langage in case the visitor's browser one does not contain the string
* `client.i18n.force` allows to ignore the visitor's browser langage and force the `client.i18n.lang` one
## Optional modules
### Allow to login to your application
### Analytics
Most of other components will need to have an user logged in, if you want to use communities, then scroll back to the User Registration module, else you'll need to activate the Auto Login module:
```json
{
"type": "autoLogin",
"parameters": {
"authority": "http://server.url/"
},
"route": false
}
```
Where:
* `authority` is the OpenID Provider. Usually, if you use `djangoldp_account` it's the same as your djangoldp server.
## Optional components
Hubl support Google or Matomo as analytics trackers. To use them, add to your `config.json`:
### Adding components
You can append any module listed bellow to your `components` entry on your `config.json`
Eg. to add the `notification` module:
```json
"analytics": [
{
"components": [
{
"type": "matomo", //Or "google"
"url": "https://my-personal.matomo.cloud/",
"id": "1"
"type": "notification",
"route": false
}
]
}
```
### About
About is a short page about the technology behind Orbit.
To activate about on Orbit, add this module declaration your `config.json`:
```json
{
"type": "about"
}
```
### Administration
Administration is a minimal modulable admin module for all other ones.
To activate administration on Orbit, add this module declaration your `config.json`:
```json
{
"type": "admin",
"route": false
}
```
### Analytics
Orbit support Google or Matomo as analytics trackers. To use them, add to your `config.json`:
```json
{
"type": "analytics",
"parameters": {
"type": "matomo",
"url": "https://my-personal.matomo.cloud/",
"id": "1"
},
"route": false
}
```
### Circles
Circles are a public group chat. To activate them, you need:
Circles define group of users that can chat & share documents togethers.
On Server: `djangoldp_circle` packages
Community module is mandatory.
On `config.json`:
To activate them, you need:
On Server: `djangoldp_circle`, `djangoldp_communities`, `djangoldp_notifications` packages
Module declaration, on `config.json`:
```json
"endpoints": {
"circle": "http://server.url/circles/"
}
{
"type": "circles",
"parameters": {
"dataSrc": "federation://circles/",
"dataSrcJoinable": "federation://circles/joinable/",
"owners": "federation://users/",
"post": "server://circles/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket",
"twoStepCreation": true,
"customMembersDisplayWidget": "tzcld-circle-members-display",
"notRequiredSubtitle": true,
"nextListItem": false
},
"federation": [
"..."
],
"route": "circles"
}
```
Where:
* `dataSrc`: is a container listing every circles
* `dataSrcJoinable`: is a container listing every public & not joined circles for the user
* `post`: is where you create new circles
* `noRender`: will load datas only when it'll be on screen, remove it if you encounter any issue
* `owners`: is your users container which contains valid owners
* `users`: is your users container
* `xmpp` is your [Prosody](https://prosody.im/) with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/)
* `twoStepCreation` 2 step creat circle with add memebers on second step
* `customMembersDisplayWidget` Use your own widget for display memebers on list
* `notRequiredSubtitle` By default subtitle is required. Can disable it with true value
* `nextListItem` By default false. If true value, enable link to information circle for all item in join or leave lists
#### Circles extensions
You can extend circles with other components, the same way you would add them to your components.
Extensions always inherit from its parent federation.
Actually it support: Events, Resources & Polls.
Can enable option `twoStepCreation` with `true` to directly display an add user form on circle after the firts creation form.
Can define custom widget to display memeber of cicrcle on circle profile. Set parameter `customMembersDisplayWidget` with the name of your custom widget
Eg.:
```json
{
"type": "circles",
"parameters": {
"dataSrc": "federation://circles/",
"dataSrcJoinable": "federation://circles/joinable/",
"owners": "federation://users/",
"post": "server://circles/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket",
"twoStepCreation": false,
"customMembersDisplayWidget": "name-of-widget"
},
"federation": [
"..."
],
"route": "circles",
"extensions": [
{
"type": "events",
"parameters": {
"dataSrc": "federation://events/",
"postUri": "server://events/",
"circles": "federation://circles/",
"typeevents": "federation://typeevents/",
"postTypeUri": "server://typeevents/",
"uploads": "server://upload/"
},
"federation": [
"..."
]
}
]
}
```
### Communities
Communities are an optional layer to add on an Orbit. They add a SOLID representation of one to many group of users on your data server.
If you're upgrading an existing Orbit, you can assign all your local users to a community this way:
```bash
./manage.py create_community --name="My community"
```
Don't forget to set some users as admin of the community from the Django Admin if you want to allow them to create new users from the app.
To activate community on Orbit, add this module declaration your `config.json`:
```json
{
"type": "communities",
"route": false
}
```
#### Activate the community directory
When you work with a federated application, you may want a directory for communities.
You can activate it by changing the route to anything else than false. Some endpoints will also get needed:
```json
{
"type": "communities",
"parameters": {
"addresses": "federation://community-addresses/",
"dataSrc": "federation://communities/",
"uploads": "server://upload/"
},
"route": "communities",
"integration": ["routing"]
}
```
### Dashboard
......@@ -149,110 +380,759 @@ Dashboard includes card generation from HTML. To activate them, you need:
On Server: `djangoldp_dashboard` packages
On `config.json`:
Module declaration, on `config.json`:
```json
"endpoints": {
"dashboard": "http://server.url/dashboard/"
}
{
"type": "dashboard",
"parameters": {
"dataSrc": "server://dashboards/",
"noRender": "",
"target": false
},
"route": "dashboard",
"integration": [
"routing"
]
}
```
A [sample fixture](https://git.startinblox.com/djangoldp-packages/djangoldp-dashboard/blob/master/djangoldp_dashboard/fixtures/sample.json) can be loaded with `./manage.py loaddata path/to/djangoldp_dashboard/fixtures/sample.json`.
A [sample fixture](https://git.startinblox.com/djangoldp-packages/djangoldp-dashboard/blob/master/djangoldp_dashboard/fixtures/sample.json) can be loaded with `./manage.py loaddata sample`.
You can have multiple dashboard module, see the [related documentation](https://git.startinblox.com/components/solid-dashboard#having-multiple-dashboard).
You need the routing integration enabled to have a Dashboard.
### Events
Events allow to create and manage instance-level evenement. To activate them, you need:
The events module includes a listing of upcoming events and the capability to create new ones.
To activate it, you need:
On Server: `djangoldp_event`, `djangoldp_upload` packages
On Server: `djangoldp_event` packages
On `config.json`:
Module declaration, on `config.json`:
```json
"endpoints": {
"events": "http://server.url/events/",
"typeevents": "http://server.url/typeevents/",
"uploads": "http://server.url/upload/"
}
{
"type": "events",
"parameters": {
"dataSrc": "federation://events/",
"postUri": "server://events/",
"circles": "federation://circles/",
"typeevents": "federation://typeevents/",
"postTypeUri": "server://typeevents/",
"pastevents": "visible",
"visiblecheckbox": "visible",
"visibilityregions": "visible",
"uploads": "server://upload/"
},
"federation": [
"..."
]
}
```
### Project
Where:
(Experimental) Project are a private group chat including Customer and Business Provider management. To activate them, you need:
* `parameters.pastevents`: is blank or hidden to display past events
* `parameters.visiblecheckbox`: is blank or hidden to add a checkbox that allow to show events from another website
* `parameters.visibilityregions`: is blank or hidden to have a Region field on events
On Server: `djangoldp_project` packages
You can get only future events by using:
```json
"get": "http://server.url/events/future/",
```
### Internationalization
Each client can overwrite langs files with their own or even create custom langs.
#### Overwrite langs
On `config.json`:
```json
"endpoints": {
"projects": "http://server.url/projects/",
"customers": "http://server.url/customers/",
"businessproviders": "http://server.url/businessproviders/",
"skills": "http://server.url/skills/"
}
{
"type": "lang",
"parameters": {
"name": "fr",
"file": "/path/to/fr.json"
},
"route": false
}
```
### Users Directory
Where:
* `parameters.name` allows to use custom client lang file.
* `parameters.file` is the path the the json lang file
(In development) Directory includes a listing of each users of your app and editable individual profile. To activate them, you need:
#### Custom langs
On Server: `djangoldp_skill`, `djangoldp_upload` packages
Needs `client.i18n.force` to `true` and `client.i18n.lang` to the custom lang name.
Your custom JSON file **must** contain every keys, from the template and from every bloxes, prefixed by the blox namespace. See example on `src/locales/fr.json`.
On `config.json`:
```json
"publicDirectory": true,
"endpoints": {
"groups": "http://server.url/groups/",
"skills": "http://server.url/skills/",
"uploads": "http://server.url/upload/"
}
{
"client": {
"i18n": {
"lang": "pirate",
"force": "true"
}
},
"components": [
{
"type": "i18n",
"parameters": {
"name": "pirate",
"file": "/path/to/yarr.json"
},
"route": false
}
]
}
```
Where:
* `parameters.name` allows to use custom client lang file.
* `parameters.file` is the path the the json lang file
Setting custom langs will not allow user to choose their own lang.
### Invoices
Invoices allow your Projects to manage invoices
Project is mandatory.
You'll need:
On Server: `djangoldp_invoice` packages
Module declaration, on `config.json`:
```json
{
"type": "projects",
"extensions": [
{
"type": "invoices",
"parameters": {
"uploads": "server://upload/"
}
}
]
}
```
### Job Offers
(In development) Job Offers includes a job board with conversation. To activate them, you need:
Job Offers includes a job board with conversation. To activate them
Community module is mandatory.
You'll need:
On Server: `djangoldp_joboffer`, `djangoldp_skill`, `djangoldp_upload`, `djangoldp_conversation` packages
Module declaration, on `config.json`:
```json
{
"type": "job-board",
"parameters": {
"dataSrc": "federation://job-offers/current/",
"dataSrcExpired": "federation://job-offers/expired/",
"postDataSrc": "server://job-offers/",
"noRender": "",
"rangeSkills": "federation://skills/",
"fields": "earnBusinessProviding"
},
"route": "job-offers",
"integration": [
"routing"
]
}
```
Where:
* `parameters.fields`: Optional set of custom fields. Notice that only `earnBusinessProviding` is already handled on djangoldp-joboffer.
You need the routing integration enabled to have a Job Board.
### Notifications
The notification module adds a bell with user's notification list and a badge on each menus with how much notifications are related to this resource. You'll need:
On Server: `djangoldp_notifications` packages
On `config.json`:
```json
{
"type": "notification",
"route": false
}
```
### One-to-one chat
One-to-one chat allow your users to chat together on a private channel.
Community & User Directory components are mandatory.
You'll need:
Module declaration, on `config.json`:
```json
{
"type": "chat",
"parameters": {
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
}
}
```
Where:
* `xmpp` is your [Prosody](https://prosody.im/) with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/) configured on.
### Polls
The polls module allows user to create polls. To activate it, you need:
On Server: `djangoldp_polls`, `djangoldp_conversation` packages
On `config.json`:
```json
"endpoints": {
"joboffers": "http://server.url/joboffers/",
"skills": "http://server.url/skills/",
"uploads": "http://server.url/upload/"
{
"type": "polls",
"parameters": {
"dataSrc": "http://server.url/polls/",
"post": "http://server.url/polls/",
"pollRangeTags": "http://server.url/tags/",
"pollRangeCircles": "http://server.url/circles/",
"uploads": "http://server.url/upload/",
"displayStartEndDates": false
}
}
```
Where:
* `parameters.displayStartEndDates` display or not the start and the end date of polls
### Project
Project are a private group chat including Customer and Business Provider management.
Community module is mandatory.
To activate them, you need:
On Server: `djangoldp_project` packages
Module declaration, on `config.json`:
```json
{
"type": "projects",
"parameters": {
"captains": "federation://users/",
"circles": "federation://circles/",
"dataSrc": "federation://projects/",
"dataSrcJoinable": "federation://projects/joinable/",
"post": "server://projects/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
},
"route": "projects"
}
```
Where:
* `captains`: is your users container which contains valid captains
* `xmpp` is your [Prosody](https://prosody.im/) with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/) configured on.
### Resources
The resources module includes a listing of indexed resources and the capability to index new ones.
To activate it, you need:
On Server: `djangoldp_resource`, `djangoldp_conversation` packages
Module declaration, on `config.json`:
```json
{
"type": "resources",
"parameters": {
"dataSrc": "http://server.url/resources/",
"post": "http://server.url/resources/",
"types": "http://server.url/types/",
"keywords": "http://server.url/keywords/",
"circles": "http://server.url/circles",
"postTypes": "http://server.url/types/",
"postKeywords": "http://server.url/keywords/",
"uploads": "http://server.url/upload/"
}
}
```
### User registration
The user registration module allows users to self-register.
Community module is mandatory.
If you set `allow_self_registration` on a community, it'll disable the auto-login feature of Orbit and allow your users to self register on your application.
To activate it, you need:
Module declaration, on `config.json`:
```json
{
"type": "registering",
"parameters": {
"dataSrc": "server://open-communities/",
"authority": "http://server.url/",
"authorityName": "your-authority-indentifier"
}
}
```
Where:
* `authority` is the OpenID Provider. Usually, if you use `djangoldp_account` it's the same as your djangoldp server.
* `authorityName` is a visual name of your OpenID Provider
If you add the `allowAnonymous` parameter, it will disable the automatic redirection to login page, and allow your users to navigate the app without being logged in. Activate it like this:
Module declaration, on `config.json`:
```json
{
"type": "registering",
"parameters": {
"authority": "http://server.url/",
"authorityName": "your-authority-indentifier",
"allowAnonymous": true
}
}
```
### Users Directory
Directory includes a listing of each users of your app and editable individual profile. To activate them, you need:
On Server: `djangoldp_skill` packages
Community module is mandatory.
Module declaration, on `config.json`:
```json
{
"type": "directory",
"parameters": {
"dataSrc": "federation://users/",
"rangeSkills": "federation://skills/",
"noRender": "",
"paginateBy": "30",
"uploads": "server://upload/"
},
"federation": [
"..."
],
"route": "members",
"integration": [
"routing"
]
}
```
You need the routing integration enabled to have a Profile Directory.
## Further configuration
### Add link to left menu
```json
{
"type": "link",
"parameters": {
"icon": "icon-docs",
"name": "some.path",
"target": "https://startinblox.com"
},
"route": true
},
```
Where:
* `parameters.icon` is a valid simple line icon
* `parameters.name` is a path to a translation. You can create your own with the lang component
* `parameters.target` is the target URL
### Enfore NPM package version
To enforce a version to get used from NPM, add this to your `config.json`:
```json
"npm": [
{
"package": "@startinblox/core",
"version": "0"
}
],
```
Where:
* `npm[].package` is the package name to include
* `npm[].version` if the version of the package you want to use
* `npm[].attributes` (optional) allows to add attributes to the script element
Package will get required from Skypack CDNs.
#### Use local packages
Use the `npm` parameter on your `config.json`
```json
{
"package": "@startinblox/oidc",
"version": "0",
"path": "./path/to/sib-auth/sib-auth.js"
}
```
### Route generation
Orbit will, by default, generate an unique route for every of your module. You can customize this route by setting a `route` attribute on your module declaration.
Eg. for the Users Directory:
```json
{
"type": "directory",
"parameters": {
"dataSrc": "server://users/",
"skills": "server://skills/",
"uploads": "server://upload/"
},
"route": "profiles"
}
```
Will lead to `http://client.url/profiles` as URL to reach the module instead of the default `http://client.url/directory`.
If you provide no `route`, then Orbit will use the `type` as route view name. If two components share the same `route`, they'll get suffixed with a random unique id.
Some module don't need any route to be active, set `route` to `false` so.
Components can get the route of a module with `window.orbit.getRoute('componentName')`.
#### Routing integration
This setting allow to create a view containing a `solid-*` with every parameters provided without any code.
```json
{
"type": "display",
"parameters": {
"dataSrc": "server://users/",
"fields": "name"
},
"integration": [
"routing"
]
}
```
will provide a view with:
```html
<solid-display data-src="http://server/users/" fields="name"></solid-display>
```
#### Set the default route
By default, Orbit will take a Dashboard as a default route.
You can enforce a component to be the default one by adding `defaultRoute` to its parameters.
Eg.:
```json
{
"type": "directory",
"parameters": {
"dataSrc": "server://users/",
"skills": "server://skills/",
"uploads": "server://upload/"
},
"route": "directory",
"defaultRoute": true
}
```
If there is more than one component with this parameter, it'll be ignored.
### Left Menu
#### Customize left menu
You can enable/disable the left menu.
Add a `"menu"` component to your config
```json
{
"type": "menu",
"parameters": {
"disabled": true
}
}
```
#### Left menu integration
It is possible to automatically add a menu entry on the left. For this:
Add a `"menu"` element to the `integration` config
```json
{
"type": "mycomponent",
"parameters": {
"dataSrc": "server://users/",
"fields": "name"
},
"integration": [
"menu"
]
}
```
Then create a component `solid-mycomponent-menu`. This component will be rendered in the left sidebar.
### Top Menu
#### Customize top menu
You can enable/disable the top menu, switch from text to logo or even change the users' displayed fields.
Add a `"menu-top"` component to your config
```json
{
"type": "menu-top",
"parameters": {
"disabled": false,
"displayLogo": true,
"userCustomField": "first_name",
"classUserCustomField": "text-semibold text-color-heading"
}
}
```
Notice that if you use the code above you may want to fix the CSS in one of your client.css files:
```css
header details[open] summary .labelled-avatar>div {
line-height: 16px;
}
```
#### Top menu integration
It is possible to automatically add a menu entry to the top. For this:
Add a `"menu"` element to the `integration` config
```json
{
"type": "mycomponent",
"parameters": {
"dataSrc": "server://users/",
"fields": "name"
},
"integration": [
"menu-top"
]
}
```
Then create a component `solid-mycomponent-menu-top`. This component will be rendered in the top menu dropdown or the top simple menu.
#### Top simple menu
You can disable the top menu dropdown in favor of a simple list generated from menu-top components only.
```json
{
"client": {
"simpleMenu": true
}
}
```
## Use with docker
### Client-side federations
### Multi services
Any parameter of your config.json can take benefits from the source generation:
Run with a local binding on localhost:
`server://` will be replaced by the value of `client.server`:
```bash
docker-compose build
docker-compose up -d client server
```json
{
"type": "awesome",
"parameters": {
"dataSrc": "server://users/"
}
}
```
Use in CI context:
`federation://` will generate a virtual federated container linking:
```bash
docker-compose -f docker-compose.yml build
docker-compose -f docker-compose.yml up -d client server
docker-compose -f docker-compose.yml run --rm e2e
* `client.server`
* `client.servers`
* `component.federation`
* `extension.federation`, if on an extension
```json
{
"client": {
"server": "http://serverA",
"servers": [
"http://serverB"
]
},
"components": [
{
"type": "awesome",
"parameters": {
"dataSrc": "federation://users/"
},
"federation": [
"http://serverC"
]
}
]
}
```
Build and push the server to registry:
### PWA / Mobile Application
```bash
docker build -f docker/djangoldp.docker --build-arg serve="http://localhost:8000" -t registry.startinblox.com/applications/hubl/server:0.1 .
docker push registry.startinblox.com/applications/hubl/server:0.1
In addition of the manifest configuration, the PWA will use:
* client.i18n.lang
* client.name
* client.shortName
For a better experience, please ensure to provide a complete manifest.
Default minimal manifest configuration:
```json
{
"client": {
"pwa": {
"dir": "ltr",
"icons": [
{
"src": "/pwa/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/pwa-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/pwa/pwa-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#fff",
"theme_color": "#FFFFFF"
}
}
}
```
Note: within a Kubernetes pod all services are bound to `localhost`.
[Recommended manifest generator](https://favicon.inbrowser.app/tools/favicon-generator)
## Enable/Disable a component
You can enable/disable any component by adding the `disabled` parameter.
```json
{
"type": "xxx",
"parameters": {
"disabled": true
}
}
```
## Replace a component
You can replace any component with the `replacement` integration.
```json
{
"type": "xxx",
"parameters": {
"replacement": "my-custom-component"
},
"integration": ["replacement"]
}
```
Please notice that `my-custom-component` should be loaded from any package within your `npm` object.
## Troubleshooting
### Circles or Projects are missing the @user list
Did you properly created subscriptions on your DjangoLDP's server? You can quickly create them with `./manage.py create_subscriptions`
### I see some technical strings instead of my fallback language strings
By default, the template fallback to `fr` lang, which is the most complete. If you're using your own fallback lang within your configuration, please ensure that this lang is completly translated.
### I see some technical strings instead of my custom language
Using a custom language file does not allow to use the default fallback language.
## Built With
* [Sib-Core](https://git.startinblox.com/framework/sib-core/) - An awesome new framework!
* [Sib-Core](https://git.startinblox.com/framework/sib-core/) - A SOLID-Compliant framework
# Documentation
## Define your own colors
We use css variables to apply colors in the application.
The file can be found in src/styles/etc/
Example of use:
```css
:root {
--color-primary: #FFD759;
--color-secondary: #FFB700;
--color-complementary: #3C3F57;
--color-complementary-darken: #27293A;
}
```
Users can find example files at the root of the application. Use
- client.sample.happy-dev.css : for a more advanced example of customization.
- client.sample.css : to set five basic colors and let the default setup apply the colors.
:root {
--color-primary: #FF6765;
--color-secondary: #46271B;
--color-complementary: #5BB4CE;
--color-complementary-darken: #35A0C0;
--color-black-s: 45%;
--color-black-h: 16;
--color-black-l: 6%;
--color-text: #636363;
--color-primary: #FF0055;
--color-secondary: #0068FF;
--color-third: #00E3B4;
--color-heading: #2E3F58;
}
:root {
--color-primary: #FFB700;
--color-secondary: #3C3F57;
--color-complementary: #6259E5;
--color-complementary-darken: #36383B;
--color-white: #FFFFFF;
--color-black-h: 216;
--color-black-s: 4%;
--color-black-l: 22%;
--color-main-background: var(--color-grey-13);
--color-main-text: #7A7F85;
--color-highlight-primary: var(--color-primary);
--color-user-panel: var(--color-black);
--color-bell: var(--color-secondary);
--color-avatar-background: #E4E8ED;
--color-title: #36383B;
--color-h1: var(--color-title);
--color-h2: var(--color-title);
/* Header's elements */
--color-header-background: var(--color-white);
--color-bell: var(--color-complementary-darken);
--color-user-panel-header-text: var(--color-complementary-darken);
--color-user-panel-header-background: var(--color-white);
--color-user-panel-header-text-open: var(--color-white);
--color-user-panel-header-background-open: var(--color-secondary);
--color-user-panel-list-background: var(--color-white);
--color-user-panel-list-text-hover: var(--color-complementary);
--color-user-panel-list-border: #E4E8ED;
/* Left menu */
--color-menu-highlight-primary: #FFD759;
--color-menu-background: var(--color-secondary);
--color-menu-text: var(--color-white);
--color-menu-text-active: var(--color-secondary);
--color-menu-background-active: var(--color-menu-highlight-primary);
--color-menu-badge-background: var(--color-menu-highlight-primary);
--color-menu-badge-text-active: var(--color-menu-highlight-primary);
--color-menu-badge-background-active: var(--color-secondary);
--color-menu-icon-background-active: var(--color-secondary);
/* Right menu */
--color-right-menu-background: #DAE2F3;
--color-right-menu-text: var(--color-secondary);
--color-right-menu-link-border: #BDC2D7;
--color-right-menu-active-text: var(--color-primary);
--color-right-menu-active-background: var(--color-secondary);
--color-right-menu-active-icon: var(--color-primary);
/* Scrollbar */
--color-scrollbar-right-background: var(--color-white);
--color-scrollbar-right-track: var(--color-grey-6);
--color-scrollbar-left-background: var(--color-secondary);
--color-scrollbar-left-track: var(--color-grey-11);
/* Tags */
--color-tag-admin-text: var(--color-complementary);
--color-tag-admin-border: var(--color-complementary);
/* Form elements */
--color-button-white: var(--color-white);
--color-button-primary: var(--color-primary);
--color-button-secondary: var(--color-complementary);
--color-button-complementary: var(--color-secondary);
--color-input-background: #EDF1FA;
--color-input-text: var(--color-secondary);
--color-input-icon: var(--color-complementary);
--color-input-active: var(--color-complementary);
--color-fieldset: var(--color-title);
--color-fieldset-border: #DAE2F3;
--color-button-modal: var(--color-title);
--color-select-list: var(--color-secondary);
--color-select-add-button: var(--color-button-secondary);
--color-select-add-button-background: var(--color-button-white);
--color-label-dark: var(--color-complementary);
--color-label-light: var(--color-grey-6);
/* Skill */
--color-skill-background: var(--color-primary);
--color-skill-text: var(--color-white);
/* Icon */
--color-icon: var(--color-primary);
/* Table */
--color-table-header-background: #BDC2D7;
--color-table-header-text: var(--color-white);
--color-table-border: #BDC2D7;
/* User thumb */
--color-user-thumb-name: #7A7F85;
--color-backlink: var(--color-secondary);
--color-content-header: #DAE2F3;
/* Chat */
--color-chat-white: var(--color-white);
--color-chat-primary: var(--color-primary);
--color-chat-complementary: var(--color-complementary);
--color-chat-secondary: var(--color-secondary);
--color-chat-complementary-darken: var(--color-complementary-darken);
--color-chat-grey-1: var(--color-grey-4);
--color-chat-grey-2: var(--color-grey-6);
--color-chat-grey-3: var(--color-grey-10);
/* Directory */
--color-directory-grey-4: #7A7F85;
--color-directory-grey-5: #F0F3F6;
--color-directory-avatar-background: #E4E8ED;
--color-directory-back-link: #36383B;
--color-directory-border: #DAE2F3;
--color-directory-content-header-border: #DAE2F3;
--color-directory-h1: var(--color-complementary-darken);
--color-directory-list-icon: var(--color-primary);
--color-directory-text: var(--color-directory-grey-4);
--color-directory-form-input: #EDF1FA;
--color-directory-form-input-text: var(--color-directory-grey-4);
--color-directory-form-input-active: var(--color-complementary);
--color-directory-form-select-icon: var(--color-complementary);
}
/* Button to edit a channel or a project (in project-profile) */
#project sib-link[next="project-edit"],
#circle sib-link[next="circle-edit"] {
background: var(--color-complementary);
border: 1px solid var(--color-complementary);
}
#project sib-link[next="project-edit"]:hover,
#circle sib-link[next="circle-edit"]:hover {
background: var(--color-white);
border: 1px solid var(--color-complementary);
color: var(--color-complementary);
}
/* Button to delete a channel */
#circle-profile>div>div.box-button>sib-ac-checker>sib-delete {
background: var(--color-white);
border: 1px solid var(--color-complementary);
color: var(--color-complementary);
}
#circle-profile>div>div.box-button>sib-ac-checker>sib-delete>button {
color: var(--color-complementary);
}
#circle-profile>div>div.box-button>sib-ac-checker>sib-delete:hover {
background: var(--color-complementary);
border: 1px solid var(--color-complementary);
color: var(--color-white);
}
#circle-profile>div>div.box-button>sib-ac-checker>sib-delete:hover>button {
color: var(--color-white);
}
/* Button with a pen to edit a user */
#admin-users-list>div>div.table>sib-display>div>sib-display>div>sib-action-hd-custom>sib-ac-checker>sib-link {
background: var(--color-complementary);
border: 1px solid var(--color-complementary);
color: var(--color-white);
}
#admin-users-list>div>div.table>sib-display>div>sib-display>div>sib-action-hd-custom>sib-ac-checker>sib-link:hover {
background: var(--color-white);
border: 1px solid var(--color-complementary);
color: var(--color-complementary);
}
/* Button to join a channel */
#admin-circle-list>div>div.table>sib-display:nth-child(5)>div>sib-display>div>admin-circle-join-button>sib-form {
background: var(--color-complementary);
border: 1px solid var(--color-complementary);
color: var(--color-white);
}
#admin-circle-list>div>div.table>sib-display:nth-child(5)>div>sib-display>div>admin-circle-join-button>sib-form:hover {
background: var(--color-white);
border: 1px solid var(--color-complementary);
color: var(--color-complementary);
}
#admin-circle-list>div>div.table>sib-display:nth-child(5)>div>sib-display>div>admin-circle-join-button>sib-form:hover input {
color: var(--color-complementary);
}
/* Directory - my profile*/
/* Button to update the avatar */
#sib-picture-browse,
#sib-picture-remove {
background-color: var(--color-white);
border: 1px solid var(--color-complementary);
color: var(--color-complementary);
}
#sib-picture-browse:hover,
#sib-picture-remove:hover {
background-color: var(--color-complementary);
border: 1px solid var(--color-complementary);
color: var(--color-white);
}
{
"xmpp": "https://jabber.happy-dev.fr/http-bind/",
"authority": "http://localhost:8000/",
"endpoints": {
"get": {
"skills": "http://localhost:8000/skills/",
"users": "http://localhost:8000/users/",
"groups": "http://localhost:8000/groups/"
},
"post": {
"skills": "http://localhost:8000/skills/",
"users": "http://localhost:8000/users/",
"groups": "http://localhost:8000/groups/"
}
}
}
{
"xmpp": "https://jabber.happy-dev.fr/http-bind/",
"authority": "http://localhost:8000/",
"authorityName": "djangoldp-server-name",
"endpoints": {
"groups": "http://localhost:8000/groups/",
"skills": "http://localhost:8000/skills/",
"users": "http://localhost:8000/users/"
}
}
"client": {
"name": "Sample of a functional Orbit",
"logo": "https://cdn.startinblox.com/logos/webp/startinblox.webp",
"server": "http://localhost:8000",
"css": "server://clients/sample.css"
},
"components": [{
"type": "registering",
"parameters": {
"dataSrc": "server://open-communities/",
"authority": "http://localhost:8000/",
"authorityName": "dataserver"
},
"route": false
},
{
"type": "notification",
"route": false
},
{
"type": "admin",
"route": false
},
{
"type": "about"
},
{
"type": "communities",
"route": false
},
{
"type": "dashboard",
"parameters": {
"dataSrc": "server://dashboards/"
},
"experimental": [
"routing"
]
},
{
"type": "directory",
"parameters": {
"dataSrc": "federation://users/",
"rangeSkills": "federation://skills/",
"paginateBy": "30",
"uploads": "server://upload/"
},
"route": "members",
"experimental": [
"routing"
]
},
{
"type": "job-board",
"parameters": {
"dataSrc": "federation://job-offers/current/",
"dataSrcExpired": "federation://job-offers/expired/",
"postDataSrc": "server://job-offers/",
"rangeSkills": "federation://skills/"
},
"route": "job-offers",
"experimental": [
"routing"
]
},
{
"type": "projects",
"parameters": {
"captains": "federation://users/",
"circles": "federation://circles/",
"dataSrc": "federation://projects/",
"dataSrcJoinable": "federation://projects/joinable/",
"post": "server://projects/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
}
},
{
"type": "circles",
"parameters": {
"dataSrc": "federation://circles/",
"dataSrcJoinable": "federation://circles/joinable/",
"owners": "federation://users/",
"post": "server://circles/",
"users": "federation://users/",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
}
},
{
"type": "chat",
"parameters": {
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
},
"route": "messages"
},
{
"type": "analytics",
"parameters": {
"type": "matomo",
"url": "https://matomo.startinblox.com/",
"id": "2"
},
"route": false
}
]
}
\ No newline at end of file
const { defineConfig } = require('cypress')
module.exports = defineConfig({
defaultCommandTimeout: 2000,
chromeWebSecurity: false,
viewportWidth: 1920,
viewportHeight: 1080,
video: false,
screenshotOnRunFailure: false,
e2e: {
baseUrl: 'http://localhost:4173',
},
})
{
"baseUrl": "http://127.0.0.1:3000",
"defaultCommandTimeout": 60000,
"chromeWebSecurity": false,
"viewportWidth": 1920,
"viewportHeight": 1080
}
## Users:
```
- signin
- signup
- create
- listing
- edit
// TO-FIX: Uncomment workaround (blocked by: nested routing bug)
```
## Job Offers:
```
- create
- edit
- listing
// TO-FIX: Search by title and description (blocked by: no search fields available)
- post
// TO-DO: Entire flow (blocked by: no published / unpublished flag)
```
## Channels:
```
- create
- edit
- leave
- retire
- delete
- join
// TO-DO: Join other user created project (blocked by: can't create new user to create a channel)
- invite
// TO-DO
```
## Projects:
```
- create
- edit
- leave
- retire
- delete
// TO-DO: Delete a project (blocked by: no option to delete a project)
- join
// TO-DO: Join other user created project (blocked by: can't create new user to create a project)
- invite
// TO-DO
```
## Breakdown:
```
DONE: 15
TO-FIX: 2
BLOCKED: 4
NOT DONE: 2
```
/// <reference types="Cypress" />
/* globals cy, expect */
context('Create Channel Browser Testing', () => {
before(() => {
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage({ domain: null });
cy.clearCookies({ domain: null });
});
beforeEach(() => cy.restoreLocalStorage());
afterEach(() => cy.saveLocalStorage());
it('should visit user login screen', () => cy.userLogin());
it('should login', () => cy.login());
describe('Channel Creation process #1', () => {
let channelName = 'Test Channel ',
description = 'Test Description ',
subtitle = 'Test Subtitle ';
it('should visit the channel creation screen', () => {
cy.visit('/admin-circles-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles-create');
});
});
it('should enter correct channel data', () => {
cy.randomNum().then(num => {
channelName += num;
description += num;
subtitle += num;
cy.get('#admin-circles-create input[name="name"]').clear().type(channelName);
cy.get('#admin-circles-create input[name="name"]').should('have.value', channelName);
cy.get('#admin-circles-create input[name="subtitle"]').clear().type(subtitle);
cy.get('#admin-circles-create input[name="subtitle"]').should('have.value', subtitle);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create channel button', () => {
cy.get('#admin-circles-create input[type="submit"]').click();
});
it('should land on channels list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should land newly created channel on channels list screen', () => {
cy.contains('solid-display-value[name="circle.name"]', channelName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
describe('Channel Creation process #2', () => {
let channelName = 'Test Channel ',
description = 'Test Description ',
subtitle = 'Test Subtitle ';
it('should visit the channel creation screen', () => {
cy.visit('/admin-circles-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles-create');
});
});
it('should enter correct channel data', () => {
cy.randomNum().then(num => {
channelName += num;
description += num;
subtitle += num;
cy.get('#admin-circles-create input[name="name"]').clear().type(channelName);
cy.get('#admin-circles-create input[name="name"]').should('have.value', channelName);
cy.get('#admin-circles-create input[name="subtitle"]').clear().type(subtitle);
cy.get('#admin-circles-create input[name="subtitle"]').should('have.value', subtitle);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create channel button', () => {
cy.get('#admin-circles-create input[type="submit"]').click();
});
it('should land on channels list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should land newly created channel on channels list screen', () => {
cy.contains('solid-display-value[name="circle.name"]', channelName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
describe('Channel Creation process #3', () => {
let channelName = 'Test Channel ',
description = 'Test Description ',
subtitle = 'Test Subtitle ';
it('should visit the channel creation screen', () => {
cy.visit('/admin-circles-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles-create');
});
});
it('should enter correct channel data', () => {
cy.randomNum().then(num => {
channelName += num;
description += num;
subtitle += num;
cy.get('#admin-circles-create input[name="name"]').clear().type(channelName);
cy.get('#admin-circles-create input[name="name"]').should('have.value', channelName);
cy.get('#admin-circles-create input[name="subtitle"]').clear().type(subtitle);
cy.get('#admin-circles-create input[name="subtitle"]').should('have.value', subtitle);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create channel button', () => {
cy.get('#admin-circles-create input[type="submit"]').click();
});
it('should land on channels list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should land newly created channel on channels list screen', () => {
cy.contains('solid-display-value[name="circle.name"]', channelName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
});
/// <reference types="Cypress" />
/* globals cy, expect */
context('Create Job Offer Browser Testing', () => {
let jobDate = '',
jobTitle = 'Test Job Offer ',
description = 'Test Description ';
before(() => {
cy.nextYear(1).then(year => {
jobDate = year + '-12-31';
});
cy.randomNum().then(num => {
jobTitle += num;
description += num;
});
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage({ domain: null });
cy.clearCookies({ domain: null });
});
beforeEach(() => cy.restoreLocalStorage());
afterEach(() => cy.saveLocalStorage());
it('should visit user login screen', () => cy.userLogin());
it('should login', () => cy.login());
describe('Job Offer Creation process', () => {
it('should visit the job offer creation screen', () => {
cy.visit('/job-offers/job-offers-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/job-offers/job-offers-create');
});
});
it('should enter correct job offer data', () => {
cy.get('#job-offers-create input[name="closingDate"]').clear().type(jobDate);
cy.get('#job-offers-create input[name="closingDate"]').should('have.value', jobDate);
cy.get('#job-offers-create input[name="title"]').clear().type(jobTitle);
cy.get('#job-offers-create input[name="title"]').should('have.value', jobTitle);
cy.get('#job-offers-create textarea[name="description"]').clear().type(description);
cy.get('#job-offers-create textarea[name="description"]').should('have.value', description);
});
it('should click on create job offer button', () => {
cy.get('#job-offers-create input[type="submit"]').click();
});
it('should land on job offers list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/job-offers');
});
});
it('should land newly created job offer on job offers list screen', () => {
cy.contains('solid-display-value[name="title"]', jobTitle).should("exist");
cy.contains('solid-display-value[name="description"]', description).should("exist");
});
});
});