]> source.dussan.org Git - nextcloud-server.git/commitdiff
Add dashboard app
authorJulius Härtl <jus@bitgrid.net>
Mon, 8 Jun 2020 13:39:26 +0000 (15:39 +0200)
committerJulius Härtl <jus@bitgrid.net>
Wed, 15 Jul 2020 07:24:51 +0000 (09:24 +0200)
Signed-off-by: Julius Härtl <jus@bitgrid.net>
.gitignore
apps/dashboard/appinfo/app.php [new file with mode: 0644]
apps/dashboard/appinfo/info.xml [new file with mode: 0644]
apps/dashboard/appinfo/routes.php [new file with mode: 0644]
apps/dashboard/img/dashboard.svg [new file with mode: 0644]
apps/dashboard/lib/Controller/DashboardController.php [new file with mode: 0644]
apps/dashboard/src/App.vue [new file with mode: 0644]
apps/dashboard/src/main.js [new file with mode: 0644]
apps/dashboard/templates/index.php [new file with mode: 0644]
apps/dashboard/webpack.js [new file with mode: 0644]
webpack.common.js

index 680bd19c8eefa1e36aa7f29f56da958bca3462d4..0b6eeaec463feddca77daca1bf151b345612cbf7 100644 (file)
@@ -17,6 +17,7 @@
 !/apps/cloud_federation_api
 !/apps/comments
 !/apps/contactsinteraction
+!/apps/dashboard
 !/apps/dav
 !/apps/files
 !/apps/federation
diff --git a/apps/dashboard/appinfo/app.php b/apps/dashboard/appinfo/app.php
new file mode 100644 (file)
index 0000000..3edd537
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+$app = new \OCP\AppFramework\App('dashboard');
diff --git a/apps/dashboard/appinfo/info.xml b/apps/dashboard/appinfo/info.xml
new file mode 100644 (file)
index 0000000..9d3b7fc
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
+       <id>dashboard</id>
+       <name>Dashboard</name>
+       <summary>Dashboard app</summary>
+       <description><![CDATA[Show something]]></description>
+       <version>7.0.0</version>
+       <licence>agpl</licence>
+       <author>Julius Härtl</author>
+       <namespace>Dashboard</namespace>
+       <default_enable/>
+
+       <category>customization</category>
+
+       <bugs>https://github.com/nextcloud/server/issues</bugs>
+
+       <dependencies>
+               <nextcloud min-version="20" max-version="20"/>
+       </dependencies>
+
+       <navigations>
+               <navigation>
+                       <name>Dashboard</name>
+                       <route>dashboard.dashboard.index</route>
+                       <icon>dashboard.svg</icon>
+                       <order>-1</order>
+               </navigation>
+       </navigations>
+</info>
diff --git a/apps/dashboard/appinfo/routes.php b/apps/dashboard/appinfo/routes.php
new file mode 100644 (file)
index 0000000..5ad8e90
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+return [
+       'routes' => [
+               ['name' => 'dashboard#index', 'url' => '/', 'verb' => 'GET'],
+       ]
+];
diff --git a/apps/dashboard/img/dashboard.svg b/apps/dashboard/img/dashboard.svg
new file mode 100644 (file)
index 0000000..a942dfc
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><g stroke-width=".905" fill="#fff" paint-order="stroke fill markers"><path d="M2.096 2.902h3.26v10.046h-3.26zM6.49 2.85h3.26v10.046H6.49zM11.052 2.824h3.26V12.87h-3.26z"/></g></svg>
\ No newline at end of file
diff --git a/apps/dashboard/lib/Controller/DashboardController.php b/apps/dashboard/lib/Controller/DashboardController.php
new file mode 100644 (file)
index 0000000..75a3472
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/**
+ * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Dashboard\Controller;
+
+use OCA\Viewer\Event\LoadViewer;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\Dashboard\IManager;
+use OCP\Dashboard\IPanel;
+use OCP\Dashboard\IRegisterPanelEvent;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IInitialStateService;
+use OCP\IRequest;
+
+class DashboardController extends Controller {
+
+       /** @var IInitialStateService */
+       private $inititalStateService;
+       /** @var IEventDispatcher */
+       private $eventDispatcher;
+       /** @var IManager */
+       private $dashboardManager;
+
+       public function __construct($appName, IRequest $request, IInitialStateService $initialStateService, IEventDispatcher $eventDispatcher, IManager $dashboardManager) {
+               parent::__construct($appName, $request);
+
+               $this->inititalStateService = $initialStateService;
+               $this->eventDispatcher = $eventDispatcher;
+               $this->dashboardManager = $dashboardManager;
+       }
+
+       /**
+        * @NoCSRFRequired
+        * @NoAdminRequired
+        * @return TemplateResponse
+        */
+       public function index(): TemplateResponse {
+               $this->eventDispatcher->dispatchTyped(new IRegisterPanelEvent($this->dashboardManager));
+
+               $dashboardManager = $this->dashboardManager;
+               $this->inititalStateService->provideLazyInitialState('dashboard', 'panels', function () use ($dashboardManager) {
+                       return array_map(function (IPanel $panel) {
+                               return [
+                                       'id' => $panel->getId(),
+                                       'title' => $panel->getTitle(),
+                                       'iconClass' => $panel->getIconClass(),
+                                       'url' => $panel->getUrl()
+                               ];
+                       }, $dashboardManager->getPanels());
+               });
+
+               if (class_exists(LoadViewer::class)) {
+                       $this->eventDispatcher->dispatchTyped(new LoadViewer());
+               }
+
+               return new TemplateResponse('dashboard', 'index');
+       }
+}
diff --git a/apps/dashboard/src/App.vue b/apps/dashboard/src/App.vue
new file mode 100644 (file)
index 0000000..9f8598d
--- /dev/null
@@ -0,0 +1,119 @@
+<template>
+       <div id="app-dashboard">
+               <h2>{{ greeting }}, {{ name }}</h2>
+
+               <div class="panels">
+                       <div v-for="panel in panels" :key="panel.id" class="panel">
+                               <a :href="panel.url">
+                                       <h3 :class="panel.iconClass">
+                                               {{ panel.title }}
+                                       </h3>
+                               </a>
+                               <div :ref="panel.id" :data-id="panel.id" />
+                       </div>
+               </div>
+       </div>
+</template>
+
+<script>
+import Vue from 'vue'
+import { loadState } from '@nextcloud/initial-state'
+import { getCurrentUser } from '@nextcloud/auth'
+
+const panels = loadState('dashboard', 'panels')
+
+console.debug('Loading dashboard panels', panels)
+
+export default {
+       name: 'App',
+       data() {
+               return {
+                       timer: new Date(),
+                       callbacks: {},
+                       panels,
+                       name: getCurrentUser()?.displayName,
+               }
+       },
+       computed: {
+               greeting() {
+                       const time = this.timer.getHours()
+
+                       if (time > 18) {
+                               return t('dashboard', '🌙 Good evening')
+                       }
+                       if (time > 12) {
+                               return t('dashboard', '☀ Good afternoon')
+                       }
+                       if (time === 12) {
+                               return t('dashboard', '🍽 Time for lunch')
+                       }
+                       return t('dashboard', '🌄 Good morning')
+               },
+       },
+       watch: {
+               callbacks() {
+                       for (const app in this.callbacks) {
+                               const element = this.$refs[app]
+                               if (this.panels[app].mounted) {
+                                       return
+                               }
+
+                               if (element) {
+                                       this.callbacks[app](element[0])
+                                       Vue.set(this.panels[app], 'mounted', true)
+                               } else {
+                                       console.error('Failed to register panel in the frontend as no backend data was provided for ' + app)
+                               }
+                       }
+               },
+       },
+       mounted() {
+               setInterval(() => {
+                       this.timer = new Date()
+               }, 30000)
+       },
+       methods: {
+               register(app, callback) {
+                       Vue.set(this.callbacks, app, callback)
+               },
+       },
+}
+</script>
+
+<style lang="scss" scoped>
+       #app-dashboard {
+               width: 100%;
+               padding-left: 50px;
+               padding-right: 50px;
+       }
+       h2 {
+               text-align: center;
+               padding: 40px;
+       }
+
+       .panels {
+               width: 100%;
+               display: flex;
+               justify-content: center;
+       }
+
+       .panel {
+               width: 250px;
+               margin: 0 30px 30px 0;
+
+               &:first-child {
+                       margin-left: 30px;
+               }
+
+               h3 {
+                       position: sticky;
+                       top: 50px;
+                       margin-top: 0;
+                       background-position: 10px 32px;
+                       padding: 30px 12px 12px 35px;
+                       background-color: var(--color-main-background);
+               }
+
+       }
+
+</style>
diff --git a/apps/dashboard/src/main.js b/apps/dashboard/src/main.js
new file mode 100644 (file)
index 0000000..998f538
--- /dev/null
@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import App from './App.vue'
+
+const Dashboard = Vue.extend(App)
+const Instance = new Dashboard({}).$mount('#app')
+
+window.OCA.Dashboard = {
+       register: (app, callback) => Instance.register(app, callback),
+}
diff --git a/apps/dashboard/templates/index.php b/apps/dashboard/templates/index.php
new file mode 100644 (file)
index 0000000..b9e614b
--- /dev/null
@@ -0,0 +1,4 @@
+<?php
+       \OC_Util::addScript('dashboard', 'dashboard');
+?>
+<div id="app"></div>
diff --git a/apps/dashboard/webpack.js b/apps/dashboard/webpack.js
new file mode 100644 (file)
index 0000000..ac45549
--- /dev/null
@@ -0,0 +1,11 @@
+const path = require('path')
+
+module.exports = {
+       entry: path.join(__dirname, 'src', 'main.js'),
+       output: {
+               path: path.resolve(__dirname, './js'),
+               publicPath: '/js/',
+               filename: 'dashboard.js',
+               jsonpFunction: 'webpackJsonpDashboard'
+       }
+}
index ee8db43e72894468190889ab50326ad84852557e..249647c31460d952ff8e32470e4bcf88bb47dbf8 100644 (file)
@@ -6,6 +6,7 @@ const { VueLoaderPlugin } = require('vue-loader')
 const accessibility = require('./apps/accessibility/webpack')
 const comments = require('./apps/comments/webpack')
 const core = require('./core/webpack')
+const dashboard = require('./apps/dashboard/webpack')
 const files = require('./apps/files/webpack')
 const files_sharing = require('./apps/files_sharing/webpack')
 const files_trashbin = require('./apps/files_trashbin/webpack')
@@ -21,6 +22,7 @@ const modules = {
        accessibility,
        comments,
        core,
+       dashboard,
        files,
        files_sharing,
        files_trashbin,