!/apps/cloud_federation_api
!/apps/comments
!/apps/contactsinteraction
+!/apps/dashboard
!/apps/dav
!/apps/files
!/apps/federation
--- /dev/null
+<?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');
--- /dev/null
+<?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>
--- /dev/null
+<?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'],
+ ]
+];
--- /dev/null
+<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
--- /dev/null
+<?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');
+ }
+}
--- /dev/null
+<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>
--- /dev/null
+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),
+}
--- /dev/null
+<?php
+ \OC_Util::addScript('dashboard', 'dashboard');
+?>
+<div id="app"></div>
--- /dev/null
+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'
+ }
+}
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')
accessibility,
comments,
core,
+ dashboard,
files,
files_sharing,
files_trashbin,