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
Select Git revision

Target

Select target project
  • 987FCF504483CBC8/toolkit
1 result
Select Git revision
Show changes
Commits on Source (18)
Showing
with 242 additions and 43 deletions
......@@ -4,15 +4,22 @@ cache:
key: ${CI_PROJECT_PATH}
paths:
- _yarn_cache
- _cypress_cache
before_script:
- yarn config set cache-folder "$CI_PROJECT_DIR/_yarn_cache"
- export CYPRESS_CACHE_FOLDER="$CI_PROJECT_DIR/_cypress_cache"
stages:
- test
- deploy
- e2e-stage1
- e2e-stage2
- e2e-stage3
test:
stage: test
script:
- yarn config set cache-folder "$CI_PROJECT_DIR/_yarn_cache"
- yarn install
- yarn run test
......@@ -20,7 +27,6 @@ linting:
stage: test
allow_failure: true
script:
- yarn config set cache-folder "$CI_PROJECT_DIR/_yarn_cache"
- yarn install
- yarn run lint
......@@ -35,7 +41,6 @@ publish:
# NPM_TOKEN needs to be a "Publish" token with 2FA disabled!
# "Automation" tokens don't work with lerna (even if they would work with "npm publish")
- echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > ~/.npmrc
- yarn config set cache-folder "$CI_PROJECT_DIR/_yarn_cache"
- yarn install
- yarn run build
- yarn run publish
......@@ -81,3 +86,46 @@ deploy_development:
DEPLOY_KEY: "$DEPLOY_SSH_KEY"
<<: *deploy_defaults
# For docker images see:
# https://github.com/cypress-io/cypress-docker-images/tree/master/browsers#cypressbrowsers
.e2e_test_defaults: &e2e_test_defaults
only:
refs:
- master
# We don't need artifacts from previous runs
dependencies: []
script:
- git clone https://gitlab.tugraz.at/dbp/middleware/e2e-tests.git --depth 1
- cd e2e-tests
- yarn install
# - ./node_modules/.bin/cypress run --browser "$BROWSER" --spec "cypress/integration/toolkit-showcase/*.spec.js"
- xvfb-run ./node_modules/.bin/cypress run --browser "$BROWSER" --headed --spec "cypress/integration/toolkit-showcase/*.spec.js"
artifacts:
paths:
- e2e-tests/cypress/videos/*
- e2e-tests/cypress/screenshots/*
expire_in: 4 week
when: always
ff75:
image: cypress/browsers:node13.8.0-chrome81-ff75
stage: e2e-stage1
variables:
MOZ_FORCE_DISABLE_E10S: 'true'
BROWSER: 'firefox'
<<: *e2e_test_defaults
chrome84:
image: cypress/browsers:node14.7.0-chrome84
stage: e2e-stage2
variables:
BROWSER: 'chrome'
<<: *e2e_test_defaults
edge88:
image: cypress/browsers:node14.10.1-edge88
stage: e2e-stage3
variables:
BROWSER: 'edge'
<<: *e2e_test_defaults
......@@ -8,5 +8,8 @@
"rangeStrategy": "update-lockfile",
"composer": {
"enabled": false
},
"poetry": {
"enabled": false
}
}
\ No newline at end of file
......@@ -21,6 +21,6 @@
"author": "",
"license": "LGPL-2.1-or-later",
"devDependencies": {
"lerna": "^3.22.1"
"lerna": "^4.0.0"
}
}
......@@ -20,7 +20,7 @@
"babel-eslint": "^10.1.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"glob": "^7.1.6",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
......
......@@ -50,6 +50,7 @@ export class AppShell extends ScopedElementsMixin(AdapterLitElement) {
this.subtitle = '';
this.description = '';
this.routes = [];
this.visibleRoutes = [];
this.metadata = {};
this.topic = {};
this.basePath = '/';
......@@ -236,6 +237,7 @@ export class AppShell extends ScopedElementsMixin(AdapterLitElement) {
entryPointUrl: { type: String, attribute: 'entry-point-url' },
keycloakConfig: { type: Object, attribute: 'keycloak-config' },
metadata: { type: Object, attribute: false },
visibleRoutes: { type: Array, attribute: false },
topic: { type: Object, attribute: false },
subtitle: { type: String, attribute: false },
description: { type: String, attribute: false },
......@@ -291,6 +293,11 @@ export class AppShell extends ScopedElementsMixin(AdapterLitElement) {
this.subtitle = this.activeMetaDataText("short_name");
this.description = this.activeMetaDataText("description");
break;
case 'metadata':
{
this._updateVisibleRoutes();
}
break;
case 'auth':
{
if (this.auth.person) {
......@@ -298,6 +305,7 @@ export class AppShell extends ScopedElementsMixin(AdapterLitElement) {
} else {
this._roles = [];
}
this._updateVisibleRoutes();
const loginStatus = this.auth['login-status'];
if (loginStatus !== this._loginStatus) {
......@@ -770,6 +778,36 @@ export class AppShell extends ScopedElementsMixin(AdapterLitElement) {
return elm;
}
_updateVisibleRoutes() {
let visibleRoutes = [];
for (let routingName of this.routes) {
const data = this.metadata[routingName];
const requiredRoles = data['required_roles'];
let visible = data['visible'];
// Hide them until the user is logged in and we know the roles of the user
for (let role of requiredRoles) {
if (!this._roles.includes(role)) {
visible = false;
break;
}
}
if (visible) {
visibleRoutes.push(routingName);
}
}
this.visibleRoutes = visibleRoutes;
const event = new CustomEvent("visibility-changed", {
bubbles: false,
cancelable: true,
});
this.dispatchEvent(event);
}
render() {
const getSelectClasses = (name => {
return classMap({selected: this.activeView === name});
......@@ -797,22 +835,8 @@ export class AppShell extends ScopedElementsMixin(AdapterLitElement) {
// build the menu
let menuTemplates = [];
for (let routingName of this.routes) {
const data = this.metadata[routingName];
const requiredRoles = data['required_roles'];
let visible = data['visible'];
// Hide them until the user is logged in and we knwo the roles of the user
for (let role of requiredRoles) {
if (!this._roles.includes(role)) {
visible = false;
break;
}
}
if (visible) {
menuTemplates.push(html`<li><a @click="${(e) => this.onMenuItemClick(e)}" href="${this.router.getPathname({component: routingName})}" data-nav class="${getSelectClasses(routingName)}" title="${this.metaDataText(routingName, "description")}">${this.metaDataText(routingName, "short_name")}</a></li>`);
}
for (let routingName of this.visibleRoutes) {
menuTemplates.push(html`<li><a @click="${(e) => this.onMenuItemClick(e)}" href="${this.router.getPathname({component: routingName})}" data-nav class="${getSelectClasses(routingName)}" title="${this.metaDataText(routingName, "description")}">${this.metaDataText(routingName, "short_name")}</a></li>`);
}
const imprintUrl = this.lang === "en" ?
......
......@@ -11,6 +11,8 @@ class AppShellWelcome extends ScopedElementsMixin(LitElement) {
constructor() {
super();
this.lang = i18n.language;
this._onVisibilityChanged = this._onVisibilityChanged.bind(this);
}
static get properties() {
......@@ -54,9 +56,26 @@ class AppShellWelcome extends ScopedElementsMixin(LitElement) {
`;
}
_onVisibilityChanged() {
this.requestUpdate();
}
connectedCallback() {
super.connectedCallback();
const app = AppShellWelcome._app;
app.addEventListener('visibility-changed', this._onVisibilityChanged);
}
disconnectedCallback() {
const app = AppShellWelcome._app;
app.removeEventListener('visibility-changed', this._onVisibilityChanged);
super.disconnectedCallback();
}
render() {
const app = AppShellWelcome._app;
let metadata = app.metadata;
let itemTemplates = [];
const switchActivity = (e, data) => {
......@@ -64,9 +83,10 @@ class AppShellWelcome extends ScopedElementsMixin(LitElement) {
app.switchComponent(data.routing_name);
};
for (let [key, data] of Object.entries(metadata)) {
for (let routeName of app.visibleRoutes) {
let data = app.metadata[routeName];
if (data['visible'] && (key !== "welcome")) {
if (routeName !== "welcome") {
itemTemplates.push(html`
<div class="item">
<h2><a href="#" @click=${(e) => {switchActivity(e, data);}}>${data.name[this.lang]}</a></h2>
......
......@@ -72,6 +72,29 @@ The component emits a `dbp-set-property` event for the attribute `auth`:
The component emits a `dbp-set-property` event for the attribute `requested-login-status` (possible values `logged-in`, `logged-out`).
## Alternative to &lt;dbp-auth&gt;
If embedded in an external page (without `<dbp-provider>`) components can also work together with a different source for the auth token:
```html
<dbp-person-select id="ps-1"></dbp-person-select>
<script>
function onAuthHasChanged(auth) {
/* fully featured auth object */
const ps = document.getElementById('ps-1');
ps.setProperty('auth', auth);
}
/* or */
function onTokenHasChanged(token) {
/* only token available */
const auth = { token: token };
onAuthHasChanged(auth);
}
</script>
```
Note: Some components need information about the logged-in person too!
## Local development
```bash
......
......@@ -22,7 +22,7 @@
"babel-eslint": "^10.1.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"glob": "^7.1.6",
"i18next-scanner": "^3.0.0",
"karma": "^6.0.0",
......
......@@ -21,7 +21,7 @@
"@rollup/plugin-url": "^6.0.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"i18next-scanner": "^3.0.0",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
......
......@@ -16,7 +16,7 @@
"babel-eslint": "^10.1.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
"karma-firefox-launcher": "^2.1.0",
......
......@@ -20,7 +20,7 @@
"@rollup/plugin-url": "^6.0.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"glob": "^7.1.6",
"i18next-scanner": "^3.0.0",
"karma": "^6.0.0",
......
......@@ -19,7 +19,7 @@
"@rollup/plugin-node-resolve": "^11.0.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"i18next-scanner": "^3.0.0",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
......
......@@ -1022,7 +1022,7 @@ export class NextcloudFilePicker extends ScopedElementsMixin(DBPLitElement) {
display: inline-grid;
width: 100%;
grid-template-columns: auto auto;
}
} <table id="directory-content-table" class="force-no-select"></table>
.nextcloud-header div button{
justify-self: start;
......
......@@ -11,6 +11,8 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
super();
this.lang = 'de';
this.url = '';
this.selectedFiles = [];
this.selectedFilesCount = 0;
}
static get scopedElements() {
......@@ -24,6 +26,8 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
return {
lang: { type: String },
url: { type: String },
selectedFiles: { type: Array, attribute: false },
selectedFilesCount: { type: Number, attribute: false },
};
}
......@@ -31,9 +35,10 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
super.connectedCallback();
this.updateComplete.then(() => {
this.shadowRoot.querySelectorAll('dbp-file-source')
this.shadowRoot.querySelectorAll('.file-source')
.forEach(element => {
element.addEventListener('dbp-file-source-file-finished', this.addLogEntry.bind(this));
// TODO: remove orphaned event listeners
element.addEventListener('dbp-file-source-file-selected', this.addLogEntry.bind(this));
});
});
}
......@@ -48,12 +53,27 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
super.update(changedProperties);
}
addLogEntry(ev) {
const ul = this.shadowRoot.querySelector('#log');
const li = document.createElement('li');
li.innerHTML = `<li><b>${ev.detail.status}</b> <tt>${ev.detail.filename}</tt>`;
getSelectedFilesHtml() {
if (this.selectedFilesCount === 0) {
return `No files were selected`;
}
let results = [];
this.selectedFiles.forEach((file) => {
results.push(html`
<div class="file-block">
<strong>${file.name}</strong> (${file.type})</span>
</div>
`);
});
ul.appendChild(li);
return results;
}
addLogEntry(ev) {
this.selectedFiles.push(ev.detail.file);
this.selectedFilesCount = this.selectedFiles.length;
}
render() {
......@@ -77,9 +97,12 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
<section class="section">
<div class="content">
<h1 class="title">${i18n.t('demo-title')}</h1>
<p>${unsafeHTML(i18n.t('required-server', { url: this.url}))}</p>
<!-- <p>${unsafeHTML(i18n.t('required-server', { url: this.url}))}</p> -->
</div>
<div class="content">
<h2 class="subtitle">Selected files</h2>
${this.getSelectedFilesHtml()}
<h2 class="subtitle">Send files via event</h2>
<p>There is no restriction for a specific file type:</p>
......@@ -88,6 +111,7 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
Open dialog
</button>
<dbp-file-source id="file-source1"
class="file-source"
allowed-mime-types="*/*"
subscribe="nextcloud-auth-url:nextcloud-auth-url,nextcloud-web-dav-url:nextcloud-web-dav-url,nextcloud-name:nextcloud-name,nextcloud-file-url:nextcloud-file-url"
lang="en"
......@@ -99,6 +123,7 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
Open dialog
</button>
<dbp-file-source id="file-source2" lang="en" url="${this.url}"
class="file-source"
allowed-mime-types="image/*"
subscribe="nextcloud-auth-url:nextcloud-auth-url,nextcloud-web-dav-url:nextcloud-web-dav-url,nextcloud-name:nextcloud-name,nextcloud-file-url:nextcloud-file-url"
enabled-targets="local,nextcloud"
......@@ -110,6 +135,7 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
Open dialog
</button>
<dbp-file-source id="file-source3" lang="en" url="${this.url}"
class="file-source"
allowed-mime-types="application/pdf"
subscribe="nextcloud-auth-url:nextcloud-auth-url,nextcloud-web-dav-url:nextcloud-web-dav-url,nextcloud-name:nextcloud-name,nextcloud-file-url:nextcloud-file-url"
enabled-targets="local,nextcloud"
......@@ -121,6 +147,7 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
Open dialog
</button>
<dbp-file-source id="file-source4" lang="en" url="${this.url}"
class="file-source"
allowed-mime-types="text/plain,image/*"
subscribe="nextcloud-auth-url:nextcloud-auth-url,nextcloud-web-dav-url:nextcloud-web-dav-url,nextcloud-name:nextcloud-name,nextcloud-file-url:nextcloud-file-url"
enabled-targets="local,nextcloud"
......@@ -132,6 +159,7 @@ export class FileSourceDemo extends ScopedElementsMixin(LitElement) {
Open dialog
</button>
<dbp-file-source id="file-source5" lang="en" url="${this.url}"
class="file-source"
allowed-mime-types="application/pdf"
decompress-zip
subscribe="nextcloud-auth-url:nextcloud-auth-url,nextcloud-web-dav-url:nextcloud-web-dav-url,nextcloud-name:nextcloud-name,nextcloud-file-url:nextcloud-file-url"
......
......@@ -214,6 +214,11 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
}
}
saveFilesToClipboard()
{
//save it
}
closeDialog(e) {
this.sendDestination();
MicroModal.close(this._('#modal-picker'));
......@@ -262,6 +267,12 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
<dbp-icon class="nav-icon" name="cloud"></dbp-icon>
<p> ${this.nextcloudName} </p>
</div>
<div title="Clipboard"
@click="${() => { this.activeTarget = "clipboard"; }}"
class="${classMap({"active": this.activeTarget === "clipboard", hidden: !this.hasEnabledDestination("clipboard") })}">
<dbp-icon class="nav-icon" name="clipboard"></dbp-icon>
<p>Clipboard</p>
</div>
</nav>
<div class="modal-header">
<button title="${i18n.t('file-sink.modal-close')}" class="modal-close" aria-label="Close modal" @click="${() => { this.closeDialog();}}">
......@@ -304,6 +315,17 @@ export class FileSink extends ScopedElementsMixin(DBPLitElement) {
this.finishedFileUpload(event);
}}"></dbp-nextcloud-file-picker>
</div>
<div class="source-main ${classMap({"hidden": this.activeTarget !== "clipboard"})}">
<div class="block clipboard-container">
<h2>In Zwischenablage speicher</h2>
<p>Hier können Sie Dateien temporär ablegen..<br><br></p>
<button class="button is-primary"
?disabled="${this.disabled}"
@click="${() => { this.saveFilesToClipboard(); }}">
${this.buttonLabel || "Ablegen"}
</button>
</div>
</div>
</main>
</div>
</div>
......
......@@ -398,6 +398,13 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
MicroModal.close(this._('#modal-picker'));
}
getClipboardFiles() {
let htmlpath = [];
htmlpath[0] = html`ein file und danach select button`;
return htmlpath;
}
static get styles() {
// language=css
return css`
......@@ -477,6 +484,12 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
<dbp-icon class="nav-icon" name="cloud"></dbp-icon>
<p> ${this.nextcloudName} </p>
</div>
<div title="Clipboard"
@click="${() => { this.activeTarget = "clipboard"; }}"
class="${classMap({"active": this.activeTarget === "clipboard", hidden: !this.hasEnabledSource("clipboard") })}">
<dbp-icon class="nav-icon" name="clipboard"></dbp-icon>
<p>Clipboard</p>
</div>
</nav>
<div class="modal-header">
......@@ -518,6 +531,13 @@ export class FileSource extends ScopedElementsMixin(DBPLitElement) {
this.sendFileEvent(event.detail.file);
}}"></dbp-nextcloud-file-picker>
</div>
<div class="source-main ${classMap({"hidden": this.activeTarget !== "clipboard"})}">
<div class="block clipboard-container">
<h2>Von der Zwischenablage auswählen</h2>
<p>Hier können Sie aus der zuvor temporär abgelegte Dateien auswählen.<br><br></p>
<p>${this.getClipboardFiles()}</p>
</div>
</div>
</main>
</div>
</div>
......
......@@ -70,6 +70,15 @@ export function getFileHandlingCss() {
}
.clipboard-container{
height: 100%;
display: flex;
align-items: center;
flex-direction: column;
width: 100%;
justify-content: center;
}
/**************************\\
Picker Styles
\\**************************/
......
......@@ -8,7 +8,7 @@ python-versions = "*"
[[package]]
name = "fonttools"
version = "4.18.2"
version = "4.21.1"
description = "Tools to manipulate font files"
category = "main"
optional = false
......@@ -63,9 +63,11 @@ brotli = [
{file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"},
{file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"},
{file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"},
{file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"},
{file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
]
fonttools = [
{file = "fonttools-4.18.2-py3-none-any.whl", hash = "sha256:645f22e8abd6e2c85d167df98333f5d2f33f9853e59210c93e2dd1574a44e6cf"},
{file = "fonttools-4.18.2.zip", hash = "sha256:5c50af6fb9b4de4609c0e5558f3444c20f8632aa319319a7ef14fd5ba677c9f8"},
{file = "fonttools-4.21.1-py3-none-any.whl", hash = "sha256:3e990e6de10c2d708fa79f1d0d93022d7e5fae1ff8cb94f69ee8d916e2512df8"},
{file = "fonttools-4.21.1.zip", hash = "sha256:d9cf618ab76afb42a79dcc0b4b5e5ee7ec1534f7ad9da3809bb15ddfcedc073d"},
]
......@@ -20,7 +20,7 @@
"@rollup/plugin-url": "^6.0.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
"karma-firefox-launcher": "^2.1.0",
......
......@@ -19,7 +19,7 @@
"@rollup/plugin-node-resolve": "^11.0.0",
"chai": "^4.2.0",
"eslint": "^7.3.1",
"eslint-plugin-jsdoc": "^31.0.0",
"eslint-plugin-jsdoc": "^32.0.0",
"i18next-scanner": "^3.0.0",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
......