@ -0,0 +1,5 @@ | |||
module.exports = { | |||
rules: { | |||
'no-console': 'off', | |||
}, | |||
}; |
@ -0,0 +1,22 @@ | |||
<?php | |||
namespace OCA\SideMenu\Appinfo; | |||
use OC\Security\CSP\ContentSecurityPolicy; | |||
use OCP\Util; | |||
$config = \OC::$server->getConfig(); | |||
$cspnm = \OC::$server->getContentSecurityPolicyNonceManager(); | |||
Util::addScript('side_menu', 'main'); | |||
Util::addScript('side_menu', 'sideMenu'); | |||
Util::addStyle('side_menu', 'sideMenu'); | |||
// whitelist the URL to allow loading JS from this external domain | |||
// $CSPManager = \OC::$server->getContentSecurityPolicyManager(); | |||
// $policy = new ContentSecurityPolicy(); | |||
// $policy->addAllowedScriptDomain($url); | |||
// $policy->addAllowedImageDomain($url); | |||
// $policy->addAllowedConnectDomain($url); | |||
// $CSPManager->addDefaultPolicy($policy); |
@ -0,0 +1,22 @@ | |||
<?xml version="1.0"?> | |||
<info> | |||
<id>side_menu</id> | |||
<name>Side menu</name> | |||
<summary>Create a side menu</summary> | |||
<description></description> | |||
<licence>agpl</licence> | |||
<author mail="dev+sidemenu@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author> | |||
<version>0.0.1</version> | |||
<namespace>SideMenu</namespace> | |||
<category>customization</category> | |||
<website>https://www.deblan.io</website> | |||
<bugs>https://github.com/ONLYOFFICE/onlyoffice-nextcloud/issues</bugs> | |||
<repository type="git">https://gitnet.fr</repository> | |||
<dependencies> | |||
<nextcloud min-version="18" max-version="18"/> | |||
</dependencies> | |||
<settings> | |||
<admin>OCA\Onlyoffice\AdminSettings</admin> | |||
<admin-section>OCA\Onlyoffice\AdminSection</admin-section> | |||
</settings> | |||
</info> |
@ -0,0 +1,16 @@ | |||
<?php | |||
return [ | |||
'routes' => [ | |||
[ | |||
'name' => 'Javascript#sideMenu', | |||
'url' => '/side_menu/javascript/side_menu', | |||
'verb' => 'GET', | |||
], | |||
[ | |||
'name' => 'Api#apps', | |||
'url' => '/side_menu/api/apps', | |||
'verb' => 'GET', | |||
] | |||
], | |||
]; |
@ -0,0 +1,60 @@ | |||
#appmenu { | |||
display: none; | |||
} | |||
#side-menu { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
height: 100vh; | |||
overflow: hidden; | |||
width: 100%; | |||
max-width: 250px; | |||
background: #333; | |||
z-index: 3000; | |||
color: #fff; | |||
box-shadow: rgba(0, 0, 0, 0.22) 0px 25.6px 57.6px 0px, rgba(0, 0, 0, 0.18) 0px 4.8px 14.4px 0px; | |||
padding: 2px 0 0 0; | |||
display: none; | |||
} | |||
#side-menu.is-active { | |||
display: block; | |||
} | |||
.side-menu-opener { | |||
background: url('../img/side-menu-opener.svg'); | |||
height: 40px; | |||
width: 40px; | |||
border-radius: 0; | |||
border: 0; | |||
} | |||
.side-menu-apps-list { | |||
padding: 30px 0; | |||
} | |||
.side-menu-app-icon { | |||
width: 20px; | |||
vertical-align: middle; | |||
margin-right: 10px; | |||
} | |||
.side-menu-app a { | |||
line-height: 30px; | |||
color: #fff; | |||
display: block; | |||
padding: 7px 15px; | |||
} | |||
.side-menu-app a:hover { | |||
background: #444; | |||
} | |||
.side-menu-logo { | |||
text-align: center; | |||
} | |||
.side-menu-logo img { | |||
width: 60%; | |||
} |
@ -0,0 +1,159 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="40" | |||
height="40" | |||
viewBox="0 0 10.583333 10.583333" | |||
version="1.1" | |||
id="svg8" | |||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)" | |||
sodipodi:docname="side-menu-opener.svg"> | |||
<defs | |||
id="defs2" /> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="9.7123697" | |||
inkscape:cx="61.421304" | |||
inkscape:cy="18.194306" | |||
inkscape:document-units="mm" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
units="px" | |||
inkscape:window-width="1918" | |||
inkscape:window-height="1027" | |||
inkscape:window-x="0" | |||
inkscape:window-y="21" | |||
inkscape:window-maximized="0" /> | |||
<metadata | |||
id="metadata5"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title /> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Calque 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(0,-286.41667)"> | |||
<g | |||
id="g1014" | |||
transform="translate(0.30030892,0.3423999)" | |||
style="fill:#ffffff"> | |||
<rect | |||
ry="0" | |||
rx="0" | |||
y="288.47351" | |||
x="4.2660251" | |||
height="1.4506654" | |||
width="1.4506654" | |||
id="rect817-7" | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" /> | |||
<g | |||
transform="translate(-0.12939894)" | |||
id="g952" | |||
style="fill:#ffffff"> | |||
<rect | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" | |||
id="rect817-7-5" | |||
width="1.4506654" | |||
height="1.4506654" | |||
x="6.4453764" | |||
y="288.47351" | |||
rx="0" | |||
ry="0" /> | |||
<rect | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" | |||
id="rect817-7-3" | |||
width="1.4506654" | |||
height="1.4506654" | |||
x="2.3454716" | |||
y="288.47351" | |||
rx="0" | |||
ry="0" /> | |||
</g> | |||
<rect | |||
ry="0" | |||
rx="0" | |||
y="290.63095" | |||
x="4.2660251" | |||
height="1.4506654" | |||
width="1.4506654" | |||
id="rect817-7-56" | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" /> | |||
<g | |||
transform="translate(-0.12939893,2.157457)" | |||
id="g952-2" | |||
style="fill:#ffffff"> | |||
<rect | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" | |||
id="rect817-7-5-9" | |||
width="1.4506654" | |||
height="1.4506654" | |||
x="6.4453764" | |||
y="288.47351" | |||
rx="0" | |||
ry="0" /> | |||
<rect | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" | |||
id="rect817-7-3-1" | |||
width="1.4506654" | |||
height="1.4506654" | |||
x="2.3454716" | |||
y="288.47351" | |||
rx="0" | |||
ry="0" /> | |||
</g> | |||
<rect | |||
ry="0" | |||
rx="0" | |||
y="292.80765" | |||
x="4.2660251" | |||
height="1.4506654" | |||
width="1.4506654" | |||
id="rect817-7-56-2" | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" /> | |||
<g | |||
transform="translate(-0.12939893,4.3341867)" | |||
id="g952-2-7" | |||
style="fill:#ffffff"> | |||
<rect | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" | |||
id="rect817-7-5-9-0" | |||
width="1.4506654" | |||
height="1.4506654" | |||
x="6.4453764" | |||
y="288.47351" | |||
rx="0" | |||
ry="0" /> | |||
<rect | |||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.85507244;fill-rule:nonzero;stroke:#ffffff;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke markers fill;enable-background:accumulate" | |||
id="rect817-7-3-1-9" | |||
width="1.4506654" | |||
height="1.4506654" | |||
x="2.3454716" | |||
y="288.47351" | |||
rx="0" | |||
ry="0" /> | |||
</g> | |||
</g> | |||
</g> | |||
</svg> |
@ -0,0 +1,47 @@ | |||
<?php | |||
namespace OCA\SideMenu\Controller; | |||
use OCP\AppFramework\Controller; | |||
use OCP\IRequest; | |||
use OCP\INavigationManager; | |||
use OCP\AppFramework\Http\DataDownloadResponse; | |||
use OCP\AppFramework\Http\TemplateResponse; | |||
use OCP\AppFramework\Http\JSONResponse; | |||
class ApiController extends Controller | |||
{ | |||
/** | |||
* @var INavigationManager | |||
*/ | |||
protected $navigationManager; | |||
/** | |||
* @param string $appName | |||
* @param IRequest $request | |||
* @param INavigationManager $navigationManager | |||
*/ | |||
public function __construct($appName, IRequest $request, INavigationManager $navigationManager) | |||
{ | |||
parent::__construct($appName, $request); | |||
$this->navigationManager = $navigationManager; | |||
} | |||
/** | |||
* @NoAdminRequired | |||
* @NoCSRFRequired | |||
* | |||
* @return JSONResponse | |||
*/ | |||
public function apps(): JSONResponse | |||
{ | |||
$apps = $this->navigationManager->getAll(); | |||
$data = [ | |||
'apps' => $apps, | |||
]; | |||
return new JSONResponse($apps); | |||
} | |||
} |
@ -0,0 +1,58 @@ | |||
{ | |||
"license": "agpl", | |||
"private": true, | |||
"scripts": { | |||
"build": "NODE_ENV=production webpack --progress --hide-modules --config webpack.js", | |||
"dev": "NODE_ENV=development webpack --progress --config webpack.js", | |||
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.js", | |||
"lint": "eslint --ext .js,.vue src", | |||
"lint:fix": "eslint --ext .js,.vue src --fix", | |||
"stylelint": "stylelint src", | |||
"stylelint:fix": "stylelint src --fix" | |||
}, | |||
"dependencies": { | |||
"@nextcloud/axios": "^1.3.2", | |||
"@nextcloud/vue": "^1.4.0", | |||
"vue": "^2.6.11" | |||
}, | |||
"browserslist": [ | |||
"extends @nextcloud/browserslist-config" | |||
], | |||
"engines": { | |||
"node": ">=10.0.0" | |||
}, | |||
"devDependencies": { | |||
"@babel/core": "^7.9.0", | |||
"@babel/plugin-syntax-dynamic-import": "^7.8.3", | |||
"@babel/preset-env": "^7.9.0", | |||
"@nextcloud/browserslist-config": "^1.0.0", | |||
"babel-eslint": "^10.1.0", | |||
"babel-loader": "^8.1.0", | |||
"css-loader": "^3.4.2", | |||
"eslint": "^5.16.0", | |||
"eslint-config-nextcloud": "0.1.1", | |||
"eslint-config-standard": "^12.0.0", | |||
"eslint-import-resolver-webpack": "^0.12.1", | |||
"eslint-loader": "^3.0.3", | |||
"eslint-plugin-import": "^2.20.0", | |||
"eslint-plugin-nextcloud": "^0.3.0", | |||
"eslint-plugin-node": "^10.0.0", | |||
"eslint-plugin-promise": "^4.2.1", | |||
"eslint-plugin-standard": "^4.0.1", | |||
"eslint-plugin-vue": "^5.2.3", | |||
"file-loader": "^6.0.0", | |||
"node-sass": "^4.13.1", | |||
"sass-loader": "^8.0.2", | |||
"stylelint": "^8.4.0", | |||
"stylelint-config-recommended-scss": "^3.3.0", | |||
"stylelint-scss": "^3.16.0", | |||
"stylelint-webpack-plugin": "^0.10.5", | |||
"url-loader": "^4.0.0", | |||
"vue-loader": "^15.9.1", | |||
"vue-template-compiler": "^2.6.11", | |||
"webpack": "^4.42.0", | |||
"webpack-cli": "^3.3.11", | |||
"webpack-merge": "^4.2.2", | |||
"webpack-node-externals": "^1.7.2" | |||
} | |||
} |
@ -0,0 +1,21 @@ | |||
import Vue from 'vue' | |||
import SideMenu from './SideMenu.vue' | |||
Vue.prototype.t = t | |||
Vue.prototype.OC = OC | |||
const View = Vue.extend(SideMenu) | |||
const sideMenu = new View({ | |||
}) | |||
const mountSideMenuComponent = () => { | |||
const sideMenuContainer = document.querySelector('#side-menu') | |||
if (sideMenuContainer) { | |||
sideMenu.$mount('#side-menu') | |||
} else { | |||
window.setTimeout(mountSideMenuComponent, 50) | |||
} | |||
} | |||
mountSideMenuComponent() |
@ -0,0 +1,49 @@ | |||
<template> | |||
<div id="side-menu"> | |||
<button class="side-menu-opener"></button> | |||
<div v-if="logo" class="side-menu-logo"> | |||
<img v-bind:src="logo"> | |||
</div> | |||
<ul class="side-menu-apps-list"> | |||
<li v-for="app in apps" class="side-menu-app"> | |||
<a v-bind:href="app.href"> | |||
<img v-bind:src="app.icon" v-bind:alt="app.name" class="side-menu-app-icon"> | |||
{{ app.name }} | |||
</a> | |||
</li> | |||
</ul> | |||
</div> | |||
</template> | |||
<script> | |||
import axios from '@nextcloud/axios' | |||
import { generateUrl } from '@nextcloud/router' | |||
export default { | |||
name: 'SideMenu', | |||
data() { | |||
return { | |||
apps: [], | |||
logo: null | |||
} | |||
}, | |||
created() { | |||
let that = this | |||
axios.get(generateUrl('apps/side_menu/side_menu/api/apps')) | |||
.then(response => { | |||
this.apps = response.data | |||
}) | |||
this.logo = window.getComputedStyle(document.querySelector('#nextcloud .logo'), null) | |||
.getPropertyValue('background-image') | |||
.replace('url("', '') | |||
.replace('")', '') | |||
console.log(this.logo) | |||
} | |||
} | |||
</script> |
@ -0,0 +1,10 @@ | |||
const sideMenuContainer = $('<div id="side-menu">') | |||
const sideMenuOpener = $('<button class="side-menu-opener"></button>') | |||
$('body').append(sideMenuContainer); | |||
sideMenuOpener.insertBefore('#nextcloud'); | |||
$('body').on('click', '.side-menu-opener', () => { | |||
$('#side-menu').toggleClass('is-active'); | |||
}) |
@ -0,0 +1,53 @@ | |||
const path = require('path') | |||
const { VueLoaderPlugin } = require('vue-loader') | |||
const StyleLintPlugin = require('stylelint-webpack-plugin') | |||
module.exports = { | |||
entry: { | |||
'main': path.join(__dirname, 'src', 'main.js'), | |||
'sideMenu': path.join(__dirname, 'src', 'SideMenu.js'), | |||
}, | |||
output: { | |||
path: path.resolve(__dirname, './js'), | |||
publicPath: '/js', | |||
filename: '[name].js?v=[hash]', | |||
chunkFilename: 'chunks/[name]-[hash].js', | |||
}, | |||
module: { | |||
rules: [ | |||
{ | |||
test: /\.css$/, | |||
use: ['vue-style-loader', 'css-loader'], | |||
}, | |||
{ | |||
test: /\.scss$/, | |||
use: ['vue-style-loader', 'css-loader', 'sass-loader'], | |||
}, | |||
{ | |||
test: /\.vue$/, | |||
loader: 'vue-loader', | |||
}, | |||
{ | |||
test: /\.js$/, | |||
loader: 'babel-loader', | |||
exclude: /node_modules/, | |||
}, | |||
{ | |||
test: /\.(png|jpg|gif|svg)$/, | |||
loader: 'url-loader', | |||
options: { | |||
name: '[name].[ext]?[hash]', | |||
limit: 8192, | |||
}, | |||
}, | |||
], | |||
}, | |||
plugins: [ | |||
new VueLoaderPlugin(), | |||
new StyleLintPlugin(), | |||
], | |||
resolve: { | |||
extensions: ['*', '.js', '.vue'], | |||
symlinks: false, | |||
}, | |||
} |