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
  • maspect/amiv-admintool
  • emustafa/amiv-admintool
  • dvruette/amiv-admintool
  • amiv/amiv-admintool
4 results
Show changes
Commits on Source (505)
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
{
"name": "amiv-admintool",
"image": "mcr.microsoft.com/vscode/devcontainers/javascript-node:14",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [9000],
// Uncomment the next line to run commands after the container is created.
"postCreateCommand": "npm install"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
//"remoteUser": "node"
}
module.exports = {
"extends": "airbnb-base",
"plugins": [
"import"
],
"env": {
"browser": true,
},
"rules": {
"no-console": 1,
"class-methods-use-this": 0,
"prefer-destructuring": 1,
"no-underscore-dangle": 0,
"linebreak-style": 0,
"import/no-unresolved": [ "error", { "ignore": [ 'networkConfig' ] } ], // hack until resolving import properly
"object-curly-newline": [ "error", {
ObjectExpression: { multiline: true, consistent: true },
ObjectPattern: { multiline: true, consistent: true },
ImportDeclaration: { minProperties: 7, consistent: true },
ExportDeclaration: { minProperties: 7, consistent: true },
}],
"max-len": [ "error", { "code": 100, ignorePattern: ".*<svg.+>" }],
}
};
.ftpconfig .ftpconfig
node_modules/
.idea/
dist/
.DS_Store
stages: stages:
- test
- build
- deploy - deploy
dev_deploy: eslint:
stage: deploy stage: test
image: alpine:latest image: node:latest
when: manual before_script:
- npm install
script:
- npm run lint
build_master:
stage: build
image: docker:stable
services:
- docker:dind
before_script: before_script:
- 'which ssh-agent || ( apk update -y && apk add openssh-client -y )' - echo "$CI_DOCKER_REGISTRY_TOKEN" | docker login -u "$CI_DOCKER_REGISTRY_USER" --password-stdin
- mkdir -p ~/.ssh
- eval $(ssh-agent -s)
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- echo "$DEPLOY_PRIVATE_KEY" | ssh-add -
script: script:
- ssh -p22 amivadmin@amiv-zoidberg.ethz.ch "docker build -t admintools ./amiv-containers/admintools/" - docker build --build-arg NPM_BUILD_COMMAND=build --pull -t "$CI_REGISTRY_IMAGE:prod" ./
- ssh -p22 amivadmin@amiv-zoidberg.ethz.ch "cd ./amiv-containers/ && docker-compose up -d" - docker build --build-arg NPM_BUILD_COMMAND=build-staging --pull -t "$CI_REGISTRY_IMAGE:staging" ./
- docker build --build-arg NPM_BUILD_COMMAND=build-local --pull -t "$CI_REGISTRY_IMAGE:local" ./
- docker push "$CI_REGISTRY_IMAGE:prod"
- docker push "$CI_REGISTRY_IMAGE:staging"
- docker push "$CI_REGISTRY_IMAGE:local"
environment:
name: production
url: https://admin.amiv.ethz.ch
only: only:
- master - master
build_dev:
stage: build
image: docker:stable
services:
- docker:dind
before_script:
- echo "$CI_DOCKER_REGISTRY_TOKEN_DEV" | docker login -u "$CI_DOCKER_REGISTRY_USER_DEV" --password-stdin
script:
- docker build --build-arg NPM_BUILD_COMMAND=build-dev --pull -t "$CI_REGISTRY_IMAGE_DEV" ./
- docker push "$CI_REGISTRY_IMAGE_DEV"
environment: environment:
name: development name: development
url: https://amiv-admin.amiv.ethz.ch url: https://admin-dev.amiv.ethz.ch
deploy:
stage: deploy
image: amiveth/ansible-ci-helper
script:
- python /main.py
File moved
# Summary
> Hi! Thanks for contributing to the admintools by reporting a new Bug!
> In order to fix the bug soon, please help us to figure out what causes the
> malfunction!
> describe the issue here
# Steps to Reproduce
> What is the main menu point (left menu bar) where this issue occurs?
> Copy and Paste the url from the window where the issue occured
> Describe in your own words what steps you did before the issue occured
# Additional Debug Information
> Please add information on what browser and operating system you are using
> If possible, can you make a screenshot of the bug?
> Please open the "developer tools" of your browser and copy and paste any
> printouts in the "console"
/label ~bug
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/relationlistcontroller.js"
}
]
}
\ No newline at end of file
# First stage: Build project
FROM node:14 as build
ARG NPM_BUILD_COMMAND=build
# Copy files and install dependencies
COPY ./ /
RUN npm install
# Build project
RUN npm run $NPM_BUILD_COMMAND
# Second stage: Server to deliver files
FROM nginx:1.15-alpine
# Copy files from first stage
COPY --from=build /index.html /var/www/
COPY --from=build /dist /var/www/dist
# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Admintool # Admintool
### Software:
* ```ubuntu /14.04.1```
* ```nginx /1.4.6```
### Dependecies: # Developer Installation
* ```jQuery /2.2.2``` ## w/ Docker
* ```bootstrap /3.3.6``` Start the dev Container in VS Code
* ```amivaccess /1.0```
### File Structure: run ```npm run start``` inside the container.
* admin (Admintool)
* lib (Libraries)
* bootstrap
* jquery
* amiv (amivcore)
* cust (custom files)
* main.js (our js file)
* main.css (our css file)
* res (Resources)
* bg (big pictures and backgrounds)
* favicon
* fonts
* logo
* tools (tools)
* main.tool
* users.tool
* ...
* public (Website)
## Library ```tools```:
The JS library ```tools``` is the backbone of the single tools. It enables the tool itself to take actions, such as store data, customize the menu, spawn alert boxes, load new tools and more.
### log(msg, type, timeout)
###### Displays an alert box to the user.
* ```msg /text,HTML``` The message or html to be displayed in the alert box
* ```type /('s', 'i', 'w', 'e')``` Specifies the type of message. Displays different colors for each type.
* s: success
* i: information
* w: warning
* e: error
* ```timeout /int (optional)``` Number of milliseconds that the message will be displayed. If not specified the default time is 5s, or 5000ms.
##### Example: The development server is available under localhost:9000. It refreshes automatically as soon as you save changes in any `.js` file.
* ``` tools.log('User updated!', 's'); ``` Creates a green alert box with the message specified that will disappear after 5000ms.
* ``` tools.log('Error!', 'e', 10000); ``` Creates a gred alert box with the message specified that will disappear after 10s.
### modal(data) ## w/o Docker
###### Spawns a BS modal. To close a modal without a button just call ```tools.modalClose()```
* ```data /js object``` Object containning the infos
* ```head /text, HTML (optional)``` Sets the modal title.
* ```body /text, HTML (optional)``` Sets the modal body.
* ```button /object (optional)``` Buttons in the footer. (Multiple allowed!! :D)
* ```type /string (optional)``` Type of boostrap button
* primary
* success
* info
* warning
* danger
* link
* ```close /bool (optional)``` Close modal on click
* ```callback /function (optional)``` Callback for the button
* ```cancel /function (optional)``` Function called on cancel or modal is closed.
##### Example:
```javascript
tools.modal();
//Creates an empty modal.
tools.modal({
head: 'Download Flash Player!!',
body: 'Your browser needs this super important plugin',
button: {
'DOWNLOAD!':{
type: 'success',
close: true,
callback: function(){
some.nasty.virusdownload.now();
}
},
},
cancel: function(){
console.log('No Virus for you -.-');
}
});
// Makes a modal to download stuff
``` ```
### getTool(tool) npm install
###### Loads the specified tool. If no tool is specified the tool in the navigaton bar (hashtag) will be chosen.
* ```tool /text (optional)``` Specifies the tool.
##### Example:
* ``` tools.getTool('home'); ``` Will get the /res/tools/```home```.tool and loads it into the site.
### ui
###### The ```ui``` element allows you to access ui components (menu) and take actions (login, logout, toggleSideMenu).
#### toggleSideMenu()
##### Example:
* ```tools.ui.toggleSideMenu();``` Toggles the sidebar.
#### menu(object)
###### Allows a tool to access the top menu and have custom links and callbacks.
* ```object /js object``` Menu structured element from which the menu is generated.
* ```link /link (optional)``` HTTP link or hash. If left empty the link is disabled.
* ```callback /function (optional)``` The function that is called if the link is pressed.
##### Example:
```javascript
tools.ui.menu({
'Foo':{}
});
// Creates a single link element named 'Foo' without a href or callback
tools.ui.menu({
'Foo':{
link:'google.com'
}
});
// Creates a link named 'Foo' without a callback, but is linked to google.com
tools.ui.menu({
'Foo':{
callback: function (){
console.log('I was pressed!!');
}
}
});
// Creates a link 'Foo' and calles the function once the link is called
tools.ui.menu({
'Foo':{
link: '#trololo',
callback: function (){
console.log('I was pressed!!');
}
}
});
// Creates a link 'Foo' and calles the function once the link is called and the user gets redirected to #trololo.
``` ```
And now, start developing:
### mem.local & mem.session ```
###### The mem element can store data for the tools, used for multiple cases. There are 2 tyoes of storage: ```local``` hast no expiration and ```session``` is stored until you close the window or tab. Every tool has separated storage, so you don't need to worry about conflicting with other tools. The subfunctions are the same, a so only ```session``` wil be demonstraded here. local works identically. npm run start
```
#### set(name, value)
###### Sets and stores a value. If the value already exists it will be overwritten!
* ```name /text``` Name of the 'variable'.
* ```value /any``` The data to be stored. Can be any valid JS data, object, etc.
##### Example:
* ```tools.mem.session.set('currentUser', 'Sir Anon');``` Stores 'Sir Anon' in 'currentUser'.
* ```tools.mem.session.set('someData', {'car':'tesla'});``` Stores the object in 'someData'.
#### get(name)
###### Returnes the assosiated value. If there is no data it will return ```null```.
* ```name /text``` Name of the 'variable'.
##### Example: *Warning*: For installation on Ubuntu 16.04 (and possibly similar), you need to install nodejs from the repos source.
* ```tools.mem.session.get('currentUser'); > 'Sir Anon'``` Retrieves 'currentUser'. 1. Remove nodejs if you already have it (ONLY IF YOU REALLY WANT!):
* ```tools.mem.session.set('someData'); > {'car':'tesla'}``` Retrieves 'someData'. ```
* ```tools.mem.session.set('nopeFoo'); > null``` No data stored unter 'nopeFoo', so returns ```null```. sudo apt remove nodejs
```
2. Add nodejs10 from repo (download): `curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -`
3. Install: `sudo apt install -y nodejs`
4. Clean-up and install the packages for amiv-admintools
```
rm -rf node_modules/
npm install
npm run start
```
This will open up a local server outputting the current version of the admintools. It refreshes automatically as soon as you save changes in any `.js` file.
# Website ### File Structure:
\ No newline at end of file * res (Resources)
- favicon
- logo
* src
- views (reusable view components, etc for Tables and selection lists)
- `index.js` main file
- `*Tool.js` main file per API resource, e.g. for all user-related UIs.
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <title>Admintools</title>
<title>Admintool</title>
<link rel="apple-touch-icon" sizes="57x57" href="res/favicon/apple-icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="res/favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="res/favicon/apple-icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="res/favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="res/favicon/apple-icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="res/favicon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="res/favicon/apple-icon-76x76.png"> <link rel="apple-touch-icon" sizes="76x76" href="res/favicon/apple-icon-76x76.png">
...@@ -20,120 +19,24 @@ ...@@ -20,120 +19,24 @@
<link rel="icon" type="image/png" sizes="32x32" href="res/favicon/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="res/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="res/favicon/favicon-96x96.png"> <link rel="icon" type="image/png" sizes="96x96" href="res/favicon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="res/favicon/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="res/favicon/favicon-16x16.png">
<link rel="manifest" href="res/favicon/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="res/favicon/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<link href="lib/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!--link href="lib/cust/main.css" rel="stylesheet"-->
<link href="lib/cust/main.css" rel="stylesheet"> <style>
@keyframes spin {
<script src="lib/jquery/jquery-2.2.2.min.js"></script> from { transform:rotate(0deg); }
<script src="lib/bootstrap/js/bootstrap.min.js"></script> to { transform:rotate(360deg); }
<script> }
// set the api url for the amivcore js library
var api_url_config = "https://amiv-api.ethz.ch"; @keyframes popup {
var spec_url_config = "lib/amiv/spec.json"; from { opacity: 0; }
</script> 90% { opacity: 0; }
<script src="lib/amiv/amivcore.js"></script> to { opacity: 100%; }
<script src="lib/cust/main.js"></script> }
} </style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment-with-locales.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.14.30/js/bootstrap-datetimepicker.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.14.30/css/bootstrap-datetimepicker.css">
</head> </head>
<body> <body>
<!-- Login Overlay--> <script src="/dist/bundle.js"></script>
<div class="loginPanel smooth">
<div class="col-sm-4"></div>
<div class="col-sm-4 well">
<img class="login-logo" src="res/logo/main.svg" style="margin-left: 15%;" alt="AMIV Logo">
<div class="input-group">
<input type="text" class="form-control" id="loginUsername" name="user" placeholder="user">
<span class="input-group-addon">@student.ethz.ch</span>
</div>
<br>
<input type="password" class="form-control" id="loginPassword" name="password" placeholder="password">
<br>
<button type="submit" class="btn btn-default loginAction">Submit</button>
</div>
<div class="col-sm-4"></div>
</div>
<!-- Modal -->
<div class="modal fade modalCont" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title"></h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
<!-- Log Container -->
<div class="alertCont"></div>
<!-- Main Container -->
<div class="wrapper-main smooth">
<!-- Sidebar Container -->
<div class="wrapper-sidebar smooth">
<div class="container-fluid">
<a href="#home"><img class="sidebar-logo" src="res/logo/main.svg" alt="AMIV Logo"></a>
<ul class="nav nav-pills nav-stacked nav-sidebar" role="navigation">
<li>
<div class="input-group">
<input type="text" class="form-control" placeholder="Find Me!">
<span class="input-group-btn">
<button class="btn btn-default" type="button"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></button>
</span>
</div>
</li>
<li role="separator" class="divider"></li>
<li><a href="#users"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> Users</a></li>
<li><a href="#events"><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span> Events</a></li>
<li><a href="#groups"><span class="glyphicon glyphicon-blackboard" aria-hidden="true"></span> Groups</a></li>
<li><a href="#annouce"><span class="glyphicon glyphicon-bullhorn" aria-hidden="true"></span> Announce</a></li>
<li><a href="#studydocuments"><span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span> Studydocs</a></li>
<li><a href="#beerncoffee"><span class="glyphicon glyphicon-cutlery" aria-hidden="true"></span> Beer & Coffee</a></li>
</li>
</ul>
</div>
</div>
<!-- Top Navbar -->
<nav class="navbar navbar-default navbar-main">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed toggleSidebarBtn" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<img height="40" style="margin: 5px;" src="res/logo/wheel.svg" id="wheel-logo" class="smooth" alt="Loading Wheel">
</div>
<ul class="nav navbar-nav navbar-left cust-menu pull-left">
</ul>
<ul class="nav navbar-nav navbar-right pull-right">
<li><a href="#" class="logoutAction"><span class="glyphicon glyphicon-off" aria-hidden="true"></span></a></li>
</ul>
</div>
</nav>
<!-- Main Content / Tool -->
<div class="wrapper-content" id="main-content">
</div>
</div>
</body> </body>
</html> </html>
(function(window) {
'use strict';
// Library NameSpace
var lns = 'amivcore'
function libgen() {
// Lib to returned
var lib = {};
// Core
var core = {
// Important vars n' stuff
lib: {
api_url: api_url_config,
spec_url: spec_url_config,
authenticated: false,
ready: false,
req_time_out: 5000,
on_interval: 100,
auth_interval: 5000,
auth_allowed_fails: 5,
auth_fails: 0,
show_errors: false,
},
// Header Setup
header: {
req: {
'get': ['Content-Type', 'Authorization'],
'post': ['Content-Type', 'Authorization'],
'patch': ['Content-Type', 'Authorization', 'If-Match'],
'delete': ['Content-Type', 'Authorization', 'If-Match'],
},
make: {
'Content-Type': function() {
return 'application/json'
},
'Authorization': function() {
var token = get('cur_token');
if (token != null)
return token;
return '';
},
'If-Match': function() {
return null;
}
}
},
adapter: {
'none': function(ret) {
return ret;
},
'string': function(strg) {
return String(strg);
},
'integer': function(int) {
return parseInt(int);
},
'boolean': function(bool) {
return (String(bool).trim().toLowerCase() == 'true' || bool === true || bool === 1)
},
'datetime': function(dt) {
var tmp = new Date(dt);
// send an iso string without the milis, thats what the api expects
return new Date(dt).toISOString().split('.')[0]+"Z";
}
}
}
/**
* Utility empty function for no callback
* @constructor
*/
function dummy() {};
/**
* Save and get into localStorage
* @constructor
* @param {string} cname
* @param {string} cvalue
*/
function set(cname, cvalue) {
if (lib.shortSession) {
window.sessionStorage.setItem('glob-' + cname, cvalue);
}
else
window.localStorage.setItem('glob-' + cname, cvalue);
}
/**
* Get from LocalStorage
* @constructor
* @param {string} cname
*/
function get(cname) {
if (lib.shortSession)
return window.sessionStorage.getItem('glob-' + cname);
else
return window.localStorage.getItem('glob-' + cname);
}
/**
* Remove variable in localStorage
* @param {string} cname
*/
function remove(cname) {
if (lib.shortSession) {
if (window.sessionStorage.getItem('glob-' + cname) !== null)
window.sessionStorage.removeItem('glob-' + cname);
}
else {
if (window.localStorage.getItem('glob-' + cname) !== null)
window.localStorage.removeItem('glob-' + cname);
}
}
/**
* Make JSON request with all request parameters in attr
* @constructor
* @param {} attr - all request parameters (attr.path, attr.data, attr.method ...)
* @param {} callback
*/
function req(attr, callback) {
callback = callback || function(msg) {
console.log(msg);
};
$.ajax({
url: core.lib.api_url + attr.path,
data: JSON.stringify(attr.data),
method: attr.method,
dataType: "json",
timeout: core.lib.req_time_out,
headers: attr.headers,
error: function(res) {
if (core.lib.show_errors) console.log(res);
callback(res);
},
}).done(function(res) {
callback(res);
});
}
/**
* Make FormData request with all request parameters in attr
* @constructor
* @param {} attr - all request parameters (attr.path, attr.data, attr.method ...)
* @param {} callback
*/
function reqFormData(attr, callback) {
callback = callback || function(msg) {
console.log(msg);
};
// put the json object into form-data
var form = new FormData();
for (var key in attr['data'])
form.append(key, attr['data'][key]);
$.ajax({
url: core.lib.api_url + attr.path,
data: form,
method: attr.method,
dataType: "json",
contentType: false,
processData: false,
timeout: core.lib.req_time_out,
headers: attr.headers,
error: function(res) {
if (core.lib.show_errors) console.log(res);
callback(res);
},
}).done(function(res) {
callback(res);
});
}
/**
* Make Function
* @constructor
* @param {string} domain
* @param {string} m - method
*/
function makeFunc(domain, m) {
return function(attr, callback) {
attr = attr || {}; // if attr does not exist use empty object
var curLib = {}
for (var curAttr in attr['data']) {
var curAttrType = lib.getParamType(domain, curAttr);
if (core.adapter.hasOwnProperty(curAttrType))
curLib[curAttr] = core.adapter[lib.getParamType(domain, curAttr)](attr['data'][curAttr]);
else
curLib[curAttr] = attr['data'][curAttr];
}
//curLib[curAttr] = attr['data'][curAttr];
var hdr = {};
for (var curHdr in attr['header'])
hdr[curHdr] = attr['header'][curHdr];
var curPath = '/' + domain;
var curLink = curPath;
if (attr['id'] != undefined) {
curPath += '/' + attr['id'];
curLink += '/{_id}';
}
// handle where, sort, projection, embedded
var urlParams = "";
var urlTypes = ['where', 'sort', 'projection', 'embedded'];
if (m === 'GET') {
for (var curUrlType of urlTypes) {
if (attr[curUrlType] != undefined) {
urlParams += ((urlParams != "") ? "&" + curUrlType + "=": curUrlType + "=");
if (typeof attr[curUrlType] === 'object')
urlParams += JSON.stringify(attr[curUrlType]);
else
urlParams += attr[curUrlType];
}
}
}
// append urlParams
curPath += "?" + urlParams;
if (get('cur_token') != null)
hdr['Authorization'] = 'Basic ' + btoa(get('cur_token') + ':');
if (m != 'GET') {
if (m == 'POST' || m == 'PUT')
for (var param in lib[domain]['methods'][m][curLink]['params'])
if (lib[domain]['methods'][m][curLink]['params'][param]['required'] == true)
if (curLib[lib[domain]['methods'][m][curLink]['params'][param]['name']] == undefined)
return 'Error: Missing ' + lib[domain]['methods'][m][curLink]['params'][param]['name'];
// hdr['Content-Type'] = 'application/json';
// curLib = JSON.stringify(curLib);
}
if (m != 'POST' && m != 'PATCH') {
req({
path: curPath,
method: m,
data: curLib,
headers: hdr,
}, callback);
}
else {
reqFormData({
path: curPath,
method: m,
data: curLib,
headers: hdr,
}, callback);
}
return true;
};
}
/**
* Read spec.json and set all needed parameters
* @constructor
*/
$.ajax({
url: core.lib.spec_url,
dataType: 'json',
timeout: core.lib.req_time_out,
success: function(d) {
var data = d['domains'];
for (var domain in data) {
lib[domain] = {};
lib[domain].methods = [];
for (var p in data[domain]['paths']) {
for (var m in data[domain]['paths'][p]) {
if (lib[domain].methods[m] == undefined) lib[domain].methods[m] = {};
lib[domain].methods[m][p] = data[domain]['paths'][p][m];
}
}
for (var m in lib[domain]['methods']) {
lib[domain][m] = makeFunc(domain, m);
}
}
checkAuth();
},
error: function(d) {
console.log('Cannot reach initialization spec: ' + core.lib.spec_url);
console.error(d);
}
});
/**
* Check Authentication
* @constructor
* @param {} exec_once
*/
function checkAuth(exec_once) {
exec_once = exec_once || false;
if (get('cur_token') != null) {
lib.sessions.GET({
data: {
where: 'token==["' + get('cur_token') + '"]'
}
}, function(res) {
if (res !== undefined && res.hasOwnProperty('_items') && res['_items'].length > 0) {
core.lib.authenticated = true;
core.lib.auth_fails = 0;
} else {
core.lib.auth_allowed_fails++;
if (core.lib.auth_fails > core.lib.auth_allowed_fails)
core.lib.authenticated = false;
}
core.lib.ready = true;
if (!exec_once)
setTimeout(checkAuth, core.lib.auth_interval);
});
} else {
core.lib.authenticated = false;
core.lib.ready = true;
if (!exec_once)
setTimeout(checkAuth, core.lib.auth_interval);
}
}
/**
* Get parameter type
* @constructor
* @param {string} dom
* @param {string} param
* @example
* // returns type of field "_id" of resource "users"
* amivcore.getParamType("users", "_id")
*/
lib.getParamType = function(dom, param) {
var tmp = 'none';
try {
if (Array.isArray(lib[dom].methods.POST['/' + dom].params))
lib[dom].methods.POST['/' + dom].params.forEach(function(cur) {
if (cur.name == param) {
tmp = cur.type;
}
});
} catch (e) {}
return tmp;
}
/**
* Get the time converted to the format the api understands
* @param {Date} d - date. If none is given then the NOW is used
* @example
* amivcore.getTime() // "2016-12-20T14:12:55Z"
* amivcore.getTime(new Date(2011, 0, 1, 2, 3, 4, 567)) // "2011-01-01T01:03:04Z"
*/
lib.getTime = function(d) {
d = d || new Date();
return core.adapter['datetime'](d.toISOString());
}
/**
* Get the etag
* @constructor
* @param {} curDomain
* @param {} curId
* @param {} callback
* @example
* amivcore.getEtag("users", amivcore.cur_user, function(res) {
* console.log(res);
* });
*/
lib.getEtag = function(curDomain, curId, callback) {
return lib[curDomain].GET({
id: curId
}, function(res) {
callback(res['_etag']);
});
}
/**
* Returns whether user is logged in
* @constructor
*/
lib.authenticated = function() {
return core.lib.authenticated;
}
/**
* Login function
* @constructor
* @param {String} curUser
* @param {String} curPass
* @param {function} callback
* @param {boolean} shortSession - if user is on a public computer
*/
lib.login = function(curUser, curPass, callback, shortSession = false) {
lib.shortSession = shortSession || false;
callback = callback || dummy;
req({
path: '/sessions/',
method: 'POST',
data: {
username: curUser.toLowerCase(),
password: curPass
},
headers: {
'Content-Type': 'application/json',
},
}, function(msg) {
var reqVar = ['token', 'user', '_id'];
for (var i in reqVar) {
lib['cur_' + reqVar[i]] = msg[reqVar[i]];
}
if (msg['_status'] == 'OK') {
set('cur_token_id', msg['_id']);
set('cur_token', msg['token']);
set('cur_user_id', msg['user']);
set('cur_session_etag', msg['_etag']);
callback(true);
} else {
remove('cur_token_id');
remove('cur_token');
remove('cur_user_id');
remove('cur_session_etag');
callback(false);
}
});
}
/**
* Logout
* @constructor
*/
lib.logout = function() {
// Deleting token from api and unsetting the vars
lib.sessions.DELETE({
id: get('cur_token_id'),
header: {"if-match": get('cur_session_etag')}
}, function(res) {
remove('cur_token');
remove('cur_token_id');
remove('cur_user_id');
remove('cur_session_etag');
});
}
/**
* Get info about the current user
* @constructor
* @param {} attr
* @param {} callback
*/
lib.user = function(attr, callback) {
callback = callback || dummy;
lib.users.GET({
id: get('cur_user_id')
}, function(res) {
if (typeof attr === 'object') {
var ret = {};
for (var key in attr)
ret[attr[key]] = res[attr[key]];
callback(ret);
} else {
callback(res[attr]);
}
});
}
/**
* Get the necessary field for specific requests
* @constructor
* @param {} domain - resource eg. "/users"
* @param {} type - HTTP request type eg. "PATCH"
* @param {boolean} wId - with id eg. "/users/$id"
* @example
* amivcore.getRequiredFields("users", "POST", false)
*/
lib.getRequiredFields = function(domain, type, wId) {
var curTree;
var resAttr = {};
if (wId)
curTree = lib[domain]['methods'][type]['/' + domain + '/{_id}']['params'];
else
curTree = lib[domain]['methods'][type]['/' + domain]['params'];
if (curTree.length == 0) return false;
else {
for (var i = 0; i < curTree.length; i++)
if (curTree[i].required == true)
resAttr[curTree[i].name] = curTree[i];
}
return resAttr;
}
/**
* On function
* @constructor
* @param {} trigger
* @param {} callback
*/
lib.on = function(trigger, callback) {
if (callback) {
lib.on_mem[trigger].callback = callback;
lib.on_mem[trigger].func();
}
}
lib.on_mem = {
ready: {
func: function() {
if (core.lib.ready)
lib.on_mem.ready.callback();
else setTimeout(function() {
lib.on_mem.ready.func();
}, core.lib.on_interval);
}
},
login: {
func: function() {
if (core.lib.authenticated && !lib.on_mem.login.prev)
lib.on_mem.login.callback();
lib.on_mem.login.prev = core.lib.authenticated;
setTimeout(lib.on_mem.login.func, core.lib.on_interval);
},
prev: false,
},
logout: {
func: function() {
if (!core.lib.authenticated && lib.on_mem.logout.prev)
lib.on_mem.logout.callback();
lib.on_mem.logout.prev = core.lib.authenticated;
setTimeout(lib.on_mem.logout.func, core.lib.on_interval);
},
prev: false,
},
}
return lib;
}
if (typeof(window[lns]) === 'undefined') {
window[lns] = libgen();
} else {
console.log(lns + ' already defined, please solve conflict');
}
})(window);
{
"domains": {
"purchases": {
"paths": {
"/purchases": {
"POST": {
"params": [
{
"unique": false,
"name": "timestamp",
"nullable": false,
"required": true,
"type": "datetime"
},
{
"not_patchable_unless_admin": true,
"name": "product",
"nullable": false,
"required": true,
"allowed": [
"beer",
"coffee"
],
"unique": false,
"type": "string",
"maxlength": 6
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"name": "user",
"nullable": false,
"required": true,
"data_relation": {
"field": "_id",
"resource": "users",
"embeddable": true
},
"unique": false,
"type": "objectid"
}
],
"label": "Create a Purchase"
},
"GET": {
"params": [],
"label": "Retrieve all purchases"
}
},
"/purchases/{_id}": {
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Purchase"
}
}
},
"description": {
"fields": {},
"general": "A beer machine or kaffi machine transaction. Users should be able to get beer or kaffi, if their last timestamp is older than one day and they are AMIV members. This resource is used to log their purchases."
}
},
"users": {
"paths": {
"/users/{_id}": {
"PATCH": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
},
{
"not_patchable_unless_admin": true,
"name": "legi",
"nullable": true,
"required": false,
"maxlength": 8,
"unique": true,
"type": "string"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"not_patchable_unless_admin": true,
"name": "firstname",
"nullable": false,
"required": true,
"maxlength": 50,
"type": "string",
"empty": false
},
{
"not_patchable_unless_admin": true,
"name": "lastname",
"nullable": false,
"required": true,
"maxlength": 50,
"type": "string",
"empty": false
},
{
"name": "rfid",
"nullable": true,
"required": false,
"maxlength": 6,
"unique": true,
"type": "string",
"empty": false
},
{
"nullable": true,
"name": "phone",
"maxlength": 20,
"required": false,
"type": "string",
"empty": false
},
{
"not_patchable_unless_admin": true,
"name": "membership",
"required": true,
"allowed": [
"none",
"regular",
"extraordinary",
"honorary"
],
"unique": false,
"type": "string",
"maxlength": 13
},
{
"not_patchable_unless_admin": true,
"name": "nethz",
"nullable": true,
"default": null,
"required": false,
"maxlength": 30,
"unique": true,
"type": "string",
"empty": false
},
{
"not_patchable_unless_admin": true,
"name": "gender",
"required": true,
"allowed": [
"male",
"female"
],
"unique": false,
"type": "string",
"maxlength": 6
},
{
"nullable": true,
"not_patchable_unless_admin": true,
"name": "department",
"allowed": [
"itet",
"mavt"
],
"required": false,
"type": "string"
},
{
"name": "password",
"nullable": true,
"default": null,
"required": false,
"maxlength": 100,
"type": "string",
"empty": false
},
{
"regex": "^.+@.+$",
"name": "email",
"required": true,
"maxlength": 100,
"unique": true,
"type": "string"
},
{
"required": false,
"type": "boolean",
"name": "send_newsletter",
"nullable": true
}
],
"label": "Update a User"
},
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a User"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a User"
}
},
"/users/{nethz}": {
"GET": {
"params": [
{
"not_patchable_unless_admin": true,
"name": "nethz",
"nullable": true,
"default": null,
"required": false,
"maxlength": 30,
"unique": true,
"type": "string",
"empty": false
}
],
"label": "Retrieve a User"
}
},
"/users": {
"POST": {
"params": [
{
"not_patchable_unless_admin": true,
"name": "legi",
"nullable": true,
"required": false,
"maxlength": 8,
"unique": true,
"type": "string"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"not_patchable_unless_admin": true,
"name": "firstname",
"nullable": false,
"required": true,
"maxlength": 50,
"type": "string",
"empty": false
},
{
"not_patchable_unless_admin": true,
"name": "lastname",
"nullable": false,
"required": true,
"maxlength": 50,
"type": "string",
"empty": false
},
{
"name": "rfid",
"nullable": true,
"required": false,
"maxlength": 6,
"unique": true,
"type": "string",
"empty": false
},
{
"nullable": true,
"name": "phone",
"maxlength": 20,
"required": false,
"type": "string",
"empty": false
},
{
"not_patchable_unless_admin": true,
"name": "membership",
"required": true,
"allowed": [
"none",
"regular",
"extraordinary",
"honorary"
],
"unique": false,
"type": "string",
"maxlength": 13
},
{
"not_patchable_unless_admin": true,
"name": "nethz",
"nullable": true,
"default": null,
"required": false,
"maxlength": 30,
"unique": true,
"type": "string",
"empty": false
},
{
"not_patchable_unless_admin": true,
"name": "gender",
"required": true,
"allowed": [
"male",
"female"
],
"unique": false,
"type": "string",
"maxlength": 6
},
{
"nullable": true,
"not_patchable_unless_admin": true,
"name": "department",
"allowed": [
"itet",
"mavt"
],
"required": false,
"type": "string"
},
{
"name": "password",
"nullable": true,
"default": null,
"required": false,
"maxlength": 100,
"type": "string",
"empty": false
},
{
"regex": "^.+@.+$",
"name": "email",
"required": true,
"maxlength": 100,
"unique": true,
"type": "string"
},
{
"required": false,
"type": "boolean",
"name": "send_newsletter",
"nullable": true
}
],
"label": "Create a User"
},
"GET": {
"params": [],
"label": "Retrieve all users"
}
}
},
"description": {
"methods": {
"GET": "Authorization is required for most of the fields"
},
"general": "In general, the user data will be generated from LDAP-Data. However, one might change the RFID-Number or the membership-status. Extraordinary members may not have a LDAP-Account and can therefore access all given fields."
}
},
"sessions": {
"paths": {
"/sessions/{_id}": {
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a Session"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Session"
}
},
"/sessions": {
"POST": {
"params": [
{
"name": "username",
"nullable": false,
"required": true,
"type": "string",
"empty": false
},
{
"readonly": true,
"required": false,
"type": "string",
"name": "token"
},
{
"name": "password",
"nullable": false,
"required": true,
"type": "string",
"empty": false
},
{
"readonly": true,
"name": "user",
"data_relation": {
"field": "_id",
"cascade_delete": true,
"resource": "users",
"embeddable": true
},
"required": false,
"type": "objectid"
},
{
"required": false,
"type": "objectid",
"name": "_id"
}
],
"label": "Create a Session"
},
"GET": {
"params": [],
"label": "Retrieve all sessions"
}
}
},
"description": {
"methods": {
"POST": "Login and aquire a login token. Post the fields 'username' and 'password', the response will contain the token. 'username' can be either nethz, mail, or user_id",
"GET": "Check token(s)."
},
"general": "A session is used to authenticate a user after he provided login data. A POST to /session will return a token you can use in an Authorization header: token <yourtoken>"
}
},
"groupmemberships": {
"paths": {
"/groupmemberships": {
"POST": {
"params": [
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"self_enrollment_required": true,
"name": "group",
"data_relation": {
"field": "_id",
"cascade_delete": true,
"resource": "groups",
"embeddable": true
},
"required": true,
"type": "objectid"
},
{
"name": "user",
"unique_combination": [
"group"
],
"required": true,
"only_self_or_moderator": true,
"data_relation": {
"field": "_id",
"cascade_delete": true,
"resource": "users",
"embeddable": true
},
"type": "objectid"
},
{
"required": false,
"type": "datetime",
"name": "expiry"
}
],
"label": "Create a Groupmembership"
},
"GET": {
"params": [],
"label": "Retrieve all groupmemberships"
}
},
"/groupmemberships/{_id}": {
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a Groupmembership"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Groupmembership"
}
}
},
"description": {
"general": "Assignment of registered users to groups."
}
},
"joboffers": {
"paths": {
"/joboffers": {
"POST": {
"params": [
{
"dependencies": "description_de",
"name": "title_de",
"required_if_not": "title_en",
"required": false,
"type": "string"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"required": false,
"type": "string",
"name": "company",
"maxlength": 30
},
{
"dependencies": "description_en",
"name": "title_en",
"required_if_not": "title_de",
"required": false,
"type": "string"
},
{
"required": false,
"type": "datetime",
"name": "time_end"
},
{
"required": false,
"type": "string",
"name": "description_en"
},
{
"name": "logo",
"filetype": [
"png",
"jpeg"
],
"required": true,
"type": "media"
},
{
"default": false,
"required": false,
"type": "boolean",
"name": "show_website"
},
{
"required": false,
"type": "string",
"name": "description_de"
},
{
"name": "pdf",
"filetype": [
"pdf"
],
"required": true,
"type": "media"
}
],
"label": "Create a Joboffer"
},
"GET": {
"params": [],
"label": "Retrieve all joboffers"
}
},
"/joboffers/{_id}": {
"PATCH": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
},
{
"dependencies": "description_de",
"name": "title_de",
"required_if_not": "title_en",
"required": false,
"type": "string"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"required": false,
"type": "string",
"name": "company",
"maxlength": 30
},
{
"dependencies": "description_en",
"name": "title_en",
"required_if_not": "title_de",
"required": false,
"type": "string"
},
{
"required": false,
"type": "datetime",
"name": "time_end"
},
{
"required": false,
"type": "string",
"name": "description_en"
},
{
"name": "logo",
"filetype": [
"png",
"jpeg"
],
"required": true,
"type": "media"
},
{
"default": false,
"required": false,
"type": "boolean",
"name": "show_website"
},
{
"required": false,
"type": "string",
"name": "description_de"
},
{
"name": "pdf",
"filetype": [
"pdf"
],
"required": true,
"type": "media"
}
],
"label": "Update a Joboffer"
},
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a Joboffer"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Joboffer"
}
}
},
"description": {
"fields": {},
"general": "A Job Offer posts repositoryUsers can post a job offer with the necessarycontent to fill out a job offer advertisement"
}
},
"eventsignups": {
"paths": {
"/eventsignups/{_id}": {
"PATCH": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
},
{
"required": false,
"type": "json_event_field",
"name": "additional_fields",
"nullable": true
},
{
"readonly": true,
"name": "confirmed",
"nullable": true,
"required": false,
"type": "boolean"
},
{
"signup_requirements": true,
"name": "event",
"unique_combination": [
"user",
"email"
],
"required": true,
"not_patchable": true,
"data_relation": {
"field": "_id",
"resource": "events",
"embeddable": true
},
"type": "objectid"
},
{
"name": "user",
"nullable": false,
"required": false,
"not_patchable": true,
"data_relation": {
"field": "_id",
"resource": "users",
"embeddable": true
},
"only_self_enrollment_for_event": true,
"type": "objectid"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"regex": "^.+@.+$",
"name": "email",
"email_signup_must_be_allowed": true,
"nullable": false,
"required": false,
"not_patchable": true,
"maxlength": 100,
"type": "string"
}
],
"label": "Update a Eventsignup"
},
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a Eventsignup"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Eventsignup"
}
},
"/eventsignups": {
"POST": {
"params": [
{
"required": false,
"type": "json_event_field",
"name": "additional_fields",
"nullable": true
},
{
"readonly": true,
"name": "confirmed",
"nullable": true,
"required": false,
"type": "boolean"
},
{
"signup_requirements": true,
"name": "event",
"unique_combination": [
"user",
"email"
],
"required": true,
"not_patchable": true,
"data_relation": {
"field": "_id",
"resource": "events",
"embeddable": true
},
"type": "objectid"
},
{
"name": "user",
"nullable": false,
"required": false,
"not_patchable": true,
"data_relation": {
"field": "_id",
"resource": "users",
"embeddable": true
},
"only_self_enrollment_for_event": true,
"type": "objectid"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"regex": "^.+@.+$",
"name": "email",
"email_signup_must_be_allowed": true,
"nullable": false,
"required": false,
"not_patchable": true,
"maxlength": 100,
"type": "string"
}
],
"label": "Create a Eventsignup"
},
"GET": {
"params": [],
"label": "Retrieve all eventsignups"
}
}
},
"description": {
"fields": {
"additional_fields": "Data-schema depends on 'additional_fields' from the mapped event. Please provide in json-format.",
"email": "For registered users, this is just a projection of your general email-address. External users need to provide their email here.",
"user": "Provide either user or email."
},
"methods": {
"PATCH": "Only additional_fields can be changed"
},
"general": "You can signup here for an existing event inside of the registration-window. External Users can only sign up to public events."
}
},
"groups": {
"paths": {
"/groups": {
"POST": {
"params": [
{
"name": "name",
"required": true,
"maxlength": 100,
"unique": true,
"type": "string",
"empty": false
},
{
"default": false,
"required": false,
"type": "boolean",
"name": "has_zoidberg_share"
},
{
"default": false,
"required": false,
"type": "boolean",
"name": "allow_self_enrollment"
},
{
"name": "moderator",
"data_relation": {
"field": "_id",
"resource": "users"
},
"nullable": true,
"required": false,
"type": "objectid"
},
{
"name": "forward_to",
"unique_elements": true,
"required": false,
"type": "list",
"schema": {
"regex": "^.+@.+$",
"type": "string",
"maxlength": 100
}
},
{
"name": "receive_from",
"unique_elements": true,
"unique_elements_for_resource": true,
"required": false,
"type": "list",
"schema": {
"regex": "[a-z0-9_\\.-]+",
"type": "string",
"maxlength": 100
}
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"propertyschema": {
"api_resources": true,
"type": "string"
},
"name": "permissions",
"nullable": true,
"required": false,
"type": "dict",
"valueschema": {
"type": "string",
"allowed": [
"read",
"readwrite"
]
}
}
],
"label": "Create a Group"
},
"GET": {
"params": [],
"label": "Retrieve all groups"
}
},
"/groups/{_id}": {
"PATCH": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
},
{
"name": "name",
"required": true,
"maxlength": 100,
"unique": true,
"type": "string",
"empty": false
},
{
"default": false,
"required": false,
"type": "boolean",
"name": "has_zoidberg_share"
},
{
"default": false,
"required": false,
"type": "boolean",
"name": "allow_self_enrollment"
},
{
"name": "moderator",
"data_relation": {
"field": "_id",
"resource": "users"
},
"nullable": true,
"required": false,
"type": "objectid"
},
{
"name": "forward_to",
"unique_elements": true,
"required": false,
"type": "list",
"schema": {
"regex": "^.+@.+$",
"type": "string",
"maxlength": 100
}
},
{
"name": "receive_from",
"unique_elements": true,
"unique_elements_for_resource": true,
"required": false,
"type": "list",
"schema": {
"regex": "[a-z0-9_\\.-]+",
"type": "string",
"maxlength": 100
}
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"propertyschema": {
"api_resources": true,
"type": "string"
},
"name": "permissions",
"nullable": true,
"required": false,
"type": "dict",
"valueschema": {
"type": "string",
"allowed": [
"read",
"readwrite"
]
}
}
],
"label": "Update a Group"
},
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a Group"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Group"
}
},
"/groups/{name}": {
"GET": {
"params": [
{
"name": "name",
"required": true,
"maxlength": 100,
"unique": true,
"type": "string",
"empty": false
}
],
"label": "Retrieve a Group"
}
}
},
"description": {
"fields": {
"has_zoidberg_share": "If the group has a share in the amiv storage",
"allow_self_enrollment": "If true, the group can be seen by all users and they can subscribe themselves",
"permissions": "permissions the group grants. has to be according to the jsonschema available at /notyetavailable"
},
"general": "This resource describes the different teams in AMIV.A group can grant API permissions and can be reached with several addresses. To see the addresses of this group, see /groupaddressesTo see the members, have a look at '/groupmembers'. To see the addresses messages are forwarded to, see /groupforwards"
}
},
"studydocuments": {
"paths": {
"/studydocuments/{_id}": {
"PATCH": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
},
{
"name": "files",
"required": true,
"type": "list",
"schema": {
"type": "media"
}
},
{
"nullable": true,
"name": "author",
"maxlength": 100,
"required": false,
"type": "string"
},
{
"nullable": true,
"name": "professor",
"maxlength": 100,
"required": false,
"type": "string"
},
{
"name": "title",
"maxlength": 100,
"required": false,
"type": "string",
"empty": false
},
{
"allowed": [
"1",
"2",
"3",
"4",
"5+"
],
"name": "semester",
"nullable": true,
"required": false,
"type": "string"
},
{
"readonly": true,
"name": "uploader",
"data_relation": {
"field": "_id",
"resource": "users"
},
"nullable": true,
"required": false,
"type": "objectid"
},
{
"allowed": [
"itet",
"mavt",
"arch",
"baug",
"bsse",
"infk",
"matl",
"biol",
"chab",
"math",
"phys",
"erdw",
"usys",
"hest",
"mtec",
"gess"
],
"name": "department",
"nullable": true,
"required": false,
"type": "string"
},
{
"nullable": true,
"name": "lecture",
"maxlength": 100,
"required": false,
"type": "string"
},
{
"required": false,
"type": "integer",
"name": "course_year"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"required": false,
"type": "string",
"name": "type",
"allowed": [
"exams",
"cheat sheets",
"lecture documents",
"exercises"
]
}
],
"label": "Update a Studydocument"
},
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a Studydocument"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Studydocument"
}
},
"/studydocuments": {
"POST": {
"params": [
{
"name": "files",
"required": true,
"type": "list",
"schema": {
"type": "media"
}
},
{
"nullable": true,
"name": "author",
"maxlength": 100,
"required": false,
"type": "string"
},
{
"nullable": true,
"name": "professor",
"maxlength": 100,
"required": false,
"type": "string"
},
{
"name": "title",
"maxlength": 100,
"required": false,
"type": "string",
"empty": false
},
{
"allowed": [
"1",
"2",
"3",
"4",
"5+"
],
"name": "semester",
"nullable": true,
"required": false,
"type": "string"
},
{
"readonly": true,
"name": "uploader",
"data_relation": {
"field": "_id",
"resource": "users"
},
"nullable": true,
"required": false,
"type": "objectid"
},
{
"allowed": [
"itet",
"mavt",
"arch",
"baug",
"bsse",
"infk",
"matl",
"biol",
"chab",
"math",
"phys",
"erdw",
"usys",
"hest",
"mtec",
"gess"
],
"name": "department",
"nullable": true,
"required": false,
"type": "string"
},
{
"nullable": true,
"name": "lecture",
"maxlength": 100,
"required": false,
"type": "string"
},
{
"required": false,
"type": "integer",
"name": "course_year"
},
{
"required": false,
"type": "objectid",
"name": "_id"
},
{
"required": false,
"type": "string",
"name": "type",
"allowed": [
"exams",
"cheat sheets",
"lecture documents",
"exercises"
]
}
],
"label": "Create a Studydocument"
},
"DELETE": {
"params": [],
"label": "Delete all studydocuments"
},
"GET": {
"params": [],
"label": "Retrieve all studydocuments"
}
}
},
"description": {
"fields": {
"semester": "Study-Semester as an Integer starting with first semester Bachelor.",
"course_year": "Course Year",
"uploader": "Read-only field describing which AMIV member uploaded the files",
"author": "Original author of the uploaded files(Prof, Assistant, copyright owner)"
},
"general": "Study-documents are basically all documents that are connected to a course. All metadata is optional and intended to help finding the file. There are no strict categories, as those do not work well for courses available to many departements and aiming at all levels of experience."
}
},
"events": {
"paths": {
"/events": {
"POST": {
"params": [
{
"name": "title_de",
"required_if_not": "title_en",
"nullable": true,
"required": false,
"dependencies": [
"catchphrase_de",
"description_de"
],
"maxlength": 100,
"type": "string"
},
{
"name": "time_register_start",
"nullable": true,
"only_if_not_null": "spots",
"required": false,
"dependencies": [
"time_register_end"
],
"type": "datetime",
"earlier_than": "time_register_end"
},
{
"readonly": true,
"required": false,
"type": "integer",
"name": "signup_count"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_thumbnail"
},
{
"dependencies": [
"time_start"
],
"name": "time_end",
"nullable": true,
"later_than": "time_start",
"required": false,
"type": "datetime"
},
{
"name": "allow_email_signup",
"nullable": false,
"default": false,
"only_if_not_null": "spots",
"required": false,
"type": "boolean"
},
{
"nullable": true,
"dependencies": [
"time_register_start",
"time_register_end"
],
"name": "spots",
"min": 0,
"required": false,
"type": "integer"
},
{
"name": "description_de",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 10000
},
{
"name": "time_register_end",
"nullable": true,
"later_than": "time_register_start",
"required": false,
"dependencies": [
"time_register_start"
],
"type": "datetime",
"only_if_not_null": "spots"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_banner"
},
{
"dependencies": [
"time_end"
],
"name": "time_start",
"nullable": true,
"required": false,
"type": "datetime",
"earlier_than": "time_end"
},
{
"name": "catchphrase_en",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 500
},
{
"name": "priority",
"min": 0,
"default": 5,
"max": 10,
"required": true,
"type": "integer"
},
{
"nullable": true,
"name": "location",
"maxlength": 50,
"required": false,
"type": "string"
},
{
"name": "show_infoscreen",
"nullable": false,
"default": false,
"required": false,
"type": "boolean"
},
{
"name": "additional_fields",
"nullable": true,
"only_if_not_null": "spots",
"required": false,
"type": "json_schema"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_infoscreen"
},
{
"nullable": true,
"name": "price",
"min": 0,
"required": false,
"type": "integer"
},
{
"name": "title_en",
"required_if_not": "title_de",
"nullable": true,
"required": false,
"dependencies": [
"catchphrase_en",
"description_en"
],
"maxlength": 100,
"type": "string"
},
{
"name": "description_en",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 10000
},
{
"name": "time_advertising_end",
"nullable": false,
"later_than": "time_advertising_start",
"required": true,
"type": "datetime"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_poster"
},
{
"name": "show_announce",
"nullable": false,
"default": false,
"required": false,
"type": "boolean"
},
{
"name": "time_advertising_start",
"nullable": false,
"required": true,
"type": "datetime",
"earlier_than": "time_advertising_end"
},
{
"name": "show_website",
"nullable": false,
"default": false,
"required": false,
"type": "boolean"
},
{
"name": "catchphrase_de",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 500
},
{
"required": false,
"type": "objectid",
"name": "_id"
}
],
"label": "Create a Event"
},
"GET": {
"params": [],
"label": "Retrieve all events"
}
},
"/events/{_id}": {
"PATCH": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
},
{
"name": "title_de",
"required_if_not": "title_en",
"nullable": true,
"required": false,
"dependencies": [
"catchphrase_de",
"description_de"
],
"maxlength": 100,
"type": "string"
},
{
"name": "time_register_start",
"nullable": true,
"only_if_not_null": "spots",
"required": false,
"dependencies": [
"time_register_end"
],
"type": "datetime",
"earlier_than": "time_register_end"
},
{
"readonly": true,
"required": false,
"type": "integer",
"name": "signup_count"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_thumbnail"
},
{
"dependencies": [
"time_start"
],
"name": "time_end",
"nullable": true,
"later_than": "time_start",
"required": false,
"type": "datetime"
},
{
"name": "allow_email_signup",
"nullable": false,
"default": false,
"only_if_not_null": "spots",
"required": false,
"type": "boolean"
},
{
"nullable": true,
"dependencies": [
"time_register_start",
"time_register_end"
],
"name": "spots",
"min": 0,
"required": false,
"type": "integer"
},
{
"name": "description_de",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 10000
},
{
"name": "time_register_end",
"nullable": true,
"later_than": "time_register_start",
"required": false,
"dependencies": [
"time_register_start"
],
"type": "datetime",
"only_if_not_null": "spots"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_banner"
},
{
"dependencies": [
"time_end"
],
"name": "time_start",
"nullable": true,
"required": false,
"type": "datetime",
"earlier_than": "time_end"
},
{
"name": "catchphrase_en",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 500
},
{
"name": "priority",
"min": 0,
"default": 5,
"max": 10,
"required": true,
"type": "integer"
},
{
"nullable": true,
"name": "location",
"maxlength": 50,
"required": false,
"type": "string"
},
{
"name": "show_infoscreen",
"nullable": false,
"default": false,
"required": false,
"type": "boolean"
},
{
"name": "additional_fields",
"nullable": true,
"only_if_not_null": "spots",
"required": false,
"type": "json_schema"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_infoscreen"
},
{
"nullable": true,
"name": "price",
"min": 0,
"required": false,
"type": "integer"
},
{
"name": "title_en",
"required_if_not": "title_de",
"nullable": true,
"required": false,
"dependencies": [
"catchphrase_en",
"description_en"
],
"maxlength": 100,
"type": "string"
},
{
"name": "description_en",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 10000
},
{
"name": "time_advertising_end",
"nullable": false,
"later_than": "time_advertising_start",
"required": true,
"type": "datetime"
},
{
"filetype": [
"png",
"jpeg"
],
"required": false,
"type": "media",
"name": "img_poster"
},
{
"name": "show_announce",
"nullable": false,
"default": false,
"required": false,
"type": "boolean"
},
{
"name": "time_advertising_start",
"nullable": false,
"required": true,
"type": "datetime",
"earlier_than": "time_advertising_end"
},
{
"name": "show_website",
"nullable": false,
"default": false,
"required": false,
"type": "boolean"
},
{
"name": "catchphrase_de",
"nullable": true,
"required": false,
"type": "string",
"maxlength": 500
},
{
"required": false,
"type": "objectid",
"name": "_id"
}
],
"label": "Update a Event"
},
"DELETE": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Delete a Event"
},
"GET": {
"params": [
{
"required": true,
"type": "string",
"name": "_id"
}
],
"label": "Retrieve a Event"
}
}
},
"description": {
"fields": {
"additional_fields": "must be provided in form of a JSON-Schema. You can add here fields you want to know from people signing up going further than their email-address",
"price": "Price of the event as Integer in Rappen.",
"allow_email_signup": "If False, only AMIV-Members can sign up for this event",
"spots": "For no limit, set to '0'. If no signup required, set to '-1'. Otherwise just provide an integer."
},
"methods": {
"GET": "You are always allowed, even without session, to view AMIV-Events"
},
"general": "An Event is basically everything happening in the AMIV. All time fields have the format YYYY-MM-DDThh:mmZ, e.g. 2014-12-20T11:50:06Z"
}
}
},
"api_name": "API",
"base": "http:///",
"server_name": null
}
/*
IMPORTS
*/
@font-face {
font-family: DINPro;
font-weight: normal;
src: url(../../res/fonts/DINPro-Light.ttf);
}
@font-face {
font-family: DINPro;
font-weight: bold;
src: url(../../res/fonts/DINPro-Bold.ttf);
}
/*
GENERAL SETUP
*/
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
body {
font-family: DINPro;
overflow: hidden;
}
/*
UTILITY CLASSES
*/
.smooth {
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
[contenteditable=true]:empty:before {
content: attr(placeholder);
display: block; /* For Firefox */
}
/*
LOGIN PANEL
*/
.loginPanel {
width: 100%;
height: 100%;
top: 0%;
left: 0%;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
z-index: 20;
//background: rgba(30,30,30,.9);
background: rgba(31, 45, 84, 1);
}
.loginPanel>div {
box-shadow: 0 0 2em #000;
}
.loginPanel .login-logo {
width: 70%;
}
/*
MAIN FRAMEWORK
*/
.wrapper-main {
height: 100%;
width: 100%;
top: 0;
left: 0;
}
.wrapper-main.toggled {
padding-left: 250px;
}
.wrapper-sidebar {
z-index: 10;
left: 250px;
width: 0px;
height: 100%;
margin-left: -250px;
overflow-y: auto;
position: fixed;
background: #222;
color: #fff;
box-shadow: 0 0 1em rgba(0, 0, 0, 0.5);
}
.wrapper-main.toggled .wrapper-sidebar {
width: 250px;
}
.nav-sidebar .navbar ul {
float: none;
display: block;
}
.wrapper-sidebar .navbar li {
float: none;
display: block;
}
@media(min-width:768px) {
.wrapper-main {
padding-left: 250px;
}
.wrapper-main.toggled {
padding-left: 0px;
}
.wrapper-sidebar {
width: 250px;
}
.wrapper-main.toggled .wrapper-sidebar {
width: 0px;
}
}
.wrapper-sidebar ul li a {
color: inherit;
}
.wrapper-sidebar ul li a:hover, .wrapper-sidebar ul li a:active, .wrapper-sidebar ul li a:focus {
color: #000;
}
.wrapper-sidebar>div {
padding: 1em;
}
.wrapper-sidebar .sidebar-logo {
width: 100%;
}
.wrapper-content {
margin-top: -20px;
//height: calc(100vh - 51px);
background: #eee;
width: 100%;
overflow: auto;
}
/*
MAIN NAVBAR
*/
.navbar-main .container-fluid>ul>li {
float: left;
}
/*
LOG BAR
*/
.alertCont {
position: fixed;
z-index: 10000;
left: 50%;
top: 10px;
transform: translateX(-50%);
}
'use strict';
// Library for all tool actions
var tools = {
//Log Function & Utility Vars
alertElems: [],
alertNum: 0,
alertType: {
's': 'alert-success',
'i': 'alert-info',
'w': 'alert-warning',
'e': 'alert-danger'
},
log: function(msg, type, timeout) {
timeout = timeout || 5000;
tools.alertNum++;
$('.alertCont').append('<div id="alertBox-' + tools.alertNum + '" class="alert ' + tools.alertType[type] + ' alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' + msg + '</div>');
tools.alertElems.push(tools.alertNum);
setTimeout(function() {
$('#alertBox-' + tools.alertElems[0]).alert('close');
tools.alertElems.shift();
}, timeout);
console.log(msg);
},
// Modal function
modalFunc: {
init: 0,
},
modalClose: function() {
$('.modalCont').modal('hide');
},
modal: function(attr) {
attr = attr || {};
if (attr.cancel !== undefined && typeof(attr.cancel) == 'function')
tools.modalFunc.cancel = attr.cancel;
if (!tools.modalFunc.init) {
$('.modalCont').on('hide.bs.modal', tools.modalFunc.cancel);
tools.modalFunc.init = 1;
}
$('.modalCont .modal-title').html(attr.head);
$('.modalCont .modal-body').html(attr.body);
$('.modalCont .modal-footer').html('<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>');
var modalBtnId = 0;
for (var curBtn in attr.button) {
if (attr.button[curBtn].type === undefined || attr.button[curBtn].type == '')
attr.button[curBtn].type = 'primary';
$('.modalCont .modal-footer').append('<button type="button" class="btn btn-' + attr.button[curBtn].type + ' modal-btn-' + modalBtnId + '">' + curBtn + '</button>');
if (attr.button[curBtn].callback !== undefined && typeof(attr.button[curBtn].callback) == 'function')
$('.modal-btn-' + modalBtnId).off('click').on('click', attr.button[curBtn].callback);
if (attr.button[curBtn].close === true)
$('.modal-btn-' + modalBtnId).on('click', tools.modalClose);
modalBtnId++;
}
$('.modalCont').modal('show');
},
// Ajax loading gunction and getting the tools
curTool: '',
getTool: function(tool) {
//Setting home if no other tool is selected
if (window.location.hash == '' || window.location.hash == null)
window.location.hash = 'home';
// If tool is specfied, get it
var nextTool = (tool && typeof tool != 'object') ? tool : window.location.hash.slice(1);
if (tools.curTool == nextTool)
return;
tools.curTool = nextTool;
window.location.hash = tools.curTool;
$('#wheel-logo').css('transform', 'rotate(360deg)');
$('#main-content').fadeOut(100, function() {
// Reset Custom menu
tools.ui.menu();
$.ajax({
url: 'tools/' + tools.curTool + '.tool',
dataType: 'html',
error: function() {
tools.log('Tool not found', 'e');
}
}).done(function(data) {
$('#main-content').html(data);
$('#main-content').fadeIn(250, function() {
$('#wheel-logo').css('transform', 'rotate(0deg)');
});
});
});
},
// UI Stuff
ui: {
//Toggle the sidemenu
toggleSideMenu: function() {
$('.wrapper-main').toggleClass('toggled');
},
login: function() {
$('.loginPanel').css({
'top': '-200%'
});
},
logout: function() {
$('.loginPanel').css({
'top': '0%'
});
},
resizeTool: function() {
$('.wrapper-content').height($(window).height() - $('.navbar-main').height());
},
menuId: 0,
menu: function(attr) {
var custMenu = $('.cust-menu');
custMenu.html('');
for (var cur in attr) {
tools.ui.menuId++;
if (attr[cur].link == '' || attr[cur].link === undefined)
attr[cur].link = 'javascript:void(0);';
custMenu.append('<li><a href="' + attr[cur].link + '" id="cust-menu-link-' + tools.ui.menuId + '">' + cur + '</a></li>');
if (typeof(attr[cur].callback) == 'function')
$('#cust-menu-link-' + tools.ui.menuId).on('click', attr[cur].callback);
}
}
},
// Memory to store stuff
memStore: function(type, name, val) {
window[type].setItem(name, val);
},
memGet: function(type, name) {
return window[type].getItem(name);
},
mem: {
local: {
set: function(name, val) {
tools.memStore('localStorage', tools.curTool + name, val);
},
get: function(name) {
return tools.memGet('localStorage', tools.curTool + name);
},
},
session: {
set: function(name, val) {
tools.memStore('sessionStorage', tools.curTool + name, val);
},
get: function(name) {
return tools.memGet('sessionStorage', tools.curTool + name);
},
}
}
}
/*
Initialization of page
*/
//Turning off cache for ajax on dev stage
$.ajaxSetup({
cache: false
});
//Binding tool change whenever the hash is changed
window.onhashchange = tools.getTool;
//Resizing Body when menu changes size and calling it on ready
window.onresize = tools.ui.resizeTool;
// Login function
function loginFunc() {
$('.loginPanel input').attr('readonly', 1);
amivcore.login($('#loginUsername').val(), $('#loginPassword').val(), function(ret) {
if (ret !== true)
tools.log('Wrong Credentials', 'w');
$('.loginPanel input').removeAttr('readonly');
});
}
// When document loaded
$(document).ready(function() {
// Resizing main wrapper-main
tools.ui.resizeTool();
// Binding the buttons
$('.toggleSidebarBtn').click(tools.ui.toggleSideMenu);
$('.loginAction').click(loginFunc);
$('.logoutAction').click(amivcore.logout);
$('.loginPanel').keypress(function(e) {
if (e.which == 13) {
e.preventDefault();
loginFunc();
}
});
});
amivcore.on('ready', function() {
tools.getTool();
});
amivcore.on('login', function() {
tools.ui.login();
});
amivcore.on('logout', function() {
tools.ui.logout();
});
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/;
index index.html;
try_files $uri /index.html =404;
}
source diff could not be displayed: it is too large. Options to address this: view the blob.
{
"name": "amiv-admintools",
"version": "0.0.1",
"description": "Admintools to access the AMIV API.",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --hot --inline",
"build": "webpack -p --config webpack.config.prod.js",
"build-dev": "webpack -p --config webpack.config.dev.js",
"build-staging": "webpack -p --config webpack.config.staging.js",
"build-local": "webpack -p --config webpack.config.local.js",
"lint": "eslint src/**/*.js src/*.js"
},
"repository": {
"type": "git",
"url": "git@gitlab.ethz.ch:amiv/amiv-admintool.git"
},
"author": "Hermann Blum et al",
"dependencies": {
"@material/drawer": "^0.30.0",
"@material/select": "^0.35.1",
"ajv": "^5.5.0",
"amiv-web-ui-components": "git+https://git@gitlab.ethz.ch/amiv/web-ui-components.git#360e65da63f4511db1f6ac56ce103be9254d1d9f",
"axios": "^0.17.1",
"client-oauth2": "^4.2.0",
"mithril": "^1.1.6",
"mithril-infinite": "^1.2.4",
"polythene-core-css": "^1.2.0",
"polythene-css": "^1.2.0",
"polythene-mithril": "1.2.0",
"querystring": "^0.2.0",
"showdown": "^1.9.0"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-object-rest-spread": "^7.2.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"compression-webpack-plugin": "^2.0.0",
"css-loader": "^2.1.0",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-import-resolver-webpack": "^0.10.1",
"eslint-loader": "^3.0.0",
"eslint-plugin-import": "^2.14.0",
"file-loader": "^3.0.1",
"style-loader": "^0.23.1",
"webpack": "^4.28.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14"
}
}
res/bg/beer.jpg

344 KiB

res/bg/cab-1440.jpg

366 KiB