From 81b2e9a5375832e99d42b7832864862783cce738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Tue, 28 Apr 2020 09:04:53 +0200 Subject: [PATCH 1/7] Initial template cleanup and submit config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- js/submit.js | 9566 +++++++++++++++++++++++- js/survey.jquery.min.js | 11 - lib/Controller/PageController.php | 60 +- src/main.js | 3 +- src/submit.js | 36 + src/views/{Fill.vue => Submit.vue} | 2 +- .stylelintrc.js => stylelint.config.js | 0 templates/expired.php | 31 + templates/expired.tmpl.php | 35 - templates/forms.tmpl.php | 28 - templates/no.acc.tmpl.php | 35 - templates/no.create.tmpl.php | 35 - templates/no.delete.tmpl.php | 35 - templates/notfound.php | 31 + templates/submit.tmpl.php | 45 - webpack.common.js | 29 +- 16 files changed, 9657 insertions(+), 325 deletions(-) delete mode 100644 js/survey.jquery.min.js create mode 100644 src/submit.js rename src/views/{Fill.vue => Submit.vue} (98%) rename .stylelintrc.js => stylelint.config.js (100%) create mode 100644 templates/expired.php delete mode 100644 templates/expired.tmpl.php delete mode 100644 templates/forms.tmpl.php delete mode 100644 templates/no.acc.tmpl.php delete mode 100644 templates/no.create.tmpl.php delete mode 100644 templates/no.delete.tmpl.php create mode 100644 templates/notfound.php delete mode 100644 templates/submit.tmpl.php diff --git a/js/submit.js b/js/submit.js index a86d81c..a8e4fe6 100644 --- a/js/submit.js +++ b/js/submit.js @@ -1,60 +1,9516 @@ -var form = [] -var questions = [] +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/js/"; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./src/submit.js"); +/******/ }) +/************************************************************************/ +/******/ ({ -function sendDataToServer(survey) { - form.answers = survey.data; - form.userId = OC.getCurrentUser().uid; - if(form.userId == ''){ - form.userId = 'anon_' + Date.now() + '_' + Math.floor(Math.random() * 10000) - } - form.questions = questions; - $.post(OC.generateUrl('apps/forms/insert/submission'), form) - .then((response) => { - }, (error) => { - /* eslint-disable-next-line no-console */ - console.log(error.response) - }); -} +/***/ "./node_modules/@nextcloud/l10n/dist/index.js": +/*!****************************************************!*\ + !*** ./node_modules/@nextcloud/l10n/dist/index.js ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { -function cssUpdate(survey, options){ - console.log(options.cssClasses) - var classes = options.cssClasses - classes.root = 'sq-root' - classes.title = 'sq-title' - classes.item = 'sq-item' - classes.label = 'sq-label' - classes.description = 'sv-q-description' +"use strict"; - if (options.question.isRequired) { - classes.title = 'sq-title sq-title-required' - classes.root = 'sq-root sq-root-required' - } -} -$(document).ready(function () { - var formJSON = $('#surveyContainer').attr('form') - var questionJSON = $('#surveyContainer').attr('questions') - - form = JSON.parse(formJSON) - questions = JSON.parse(questionJSON) - - var surveyJSON = { - title: form.title, - description: form.description, - questions: [] - }; - - questions.forEach(q => { - var qChoices = [] - q.options.forEach(o => { - qChoices.push(o.text); - }); - surveyJSON.questions.push({type: q.type, name: q.text, choices: qChoices, isRequired: 'true'}); - }); - - $('#surveyContainer').Survey({ - model: new Survey.Model(surveyJSON), - onUpdateQuestionCssClasses: cssUpdate, - onComplete: sendDataToServer, - }); +Object.defineProperty(exports, "__esModule", { + value: true }); +exports.getLocale = getLocale; +exports.getLanguage = getLanguage; +exports.translate = translate; +exports.translatePlural = translatePlural; +exports.getFirstDay = getFirstDay; +exports.getDayNames = getDayNames; +exports.getDayNamesShort = getDayNamesShort; +exports.getDayNamesMin = getDayNamesMin; +exports.getMonthNames = getMonthNames; +exports.getMonthNamesShort = getMonthNamesShort; + +/// + +/** + * Returns the user's locale + */ +function getLocale() { + if (typeof OC === 'undefined') { + console.warn('No OC found'); + return 'en'; + } + + return OC.getLocale(); +} +/** + * Returns the user's language + */ + + +function getLanguage() { + if (typeof OC === 'undefined') { + console.warn('No OC found'); + return 'en'; + } + + return OC.getLanguage(); +} + +/** + * Translate a string + * + * @param {string} app the id of the app for which to translate the string + * @param {string} text the string to translate + * @param {object} vars map of placeholder key to value + * @param {number} number to replace %n with + * @param {object} [options] options object + * @return {string} + */ +function translate(app, text, vars, count, options) { + if (typeof OC === 'undefined') { + console.warn('No OC found'); + return text; + } + + return OC.L10N.translate(app, text, vars, count, options); +} +/** + * Translate a plural string + * + * @param {string} app the id of the app for which to translate the string + * @param {string} textSingular the string to translate for exactly one object + * @param {string} textPlural the string to translate for n objects + * @param {number} count number to determine whether to use singular or plural + * @param {Object} vars of placeholder key to value + * @param {object} options options object + * @return {string} + */ + + +function translatePlural(app, textSingular, textPlural, count, vars, options) { + if (typeof OC === 'undefined') { + console.warn('No OC found'); + return textSingular; + } + + return OC.L10N.translatePlural(app, textSingular, textPlural, count, vars, options); +} +/** + * Get the first day of the week + * + * @return {number} + */ + + +function getFirstDay() { + if (typeof window.firstDay === 'undefined') { + console.warn('No firstDay found'); + return 1; + } + + return window.firstDay; +} +/** + * Get a list of day names (full names) + * + * @return {string[]} + */ + + +function getDayNames() { + if (typeof window.dayNames === 'undefined') { + console.warn('No dayNames found'); + return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + } + + return window.dayNames; +} +/** + * Get a list of day names (short names) + * + * @return {string[]} + */ + + +function getDayNamesShort() { + if (typeof window.dayNamesShort === 'undefined') { + console.warn('No dayNamesShort found'); + return ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.']; + } + + return window.dayNamesShort; +} +/** + * Get a list of day names (minified names) + * + * @return {string[]} + */ + + +function getDayNamesMin() { + if (typeof window.dayNamesMin === 'undefined') { + console.warn('No dayNamesMin found'); + return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; + } + + return window.dayNamesMin; +} +/** + * Get a list of month names (full names) + * + * @return {string[]} + */ + + +function getMonthNames() { + if (typeof window.monthNames === 'undefined') { + console.warn('No monthNames found'); + return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + } + + return window.monthNames; +} +/** + * Get a list of month names (short names) + * + * @return {string[]} + */ + + +function getMonthNamesShort() { + if (typeof window.monthNamesShort === 'undefined') { + console.warn('No monthNamesShort found'); + return ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.']; + } + + return window.monthNamesShort; +} +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ "./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js?!./src/views/Submit.vue?vue&type=script&lang=js&": +/*!*****************************************************************************************************************************************!*\ + !*** ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/views/Submit.vue?vue&type=script&lang=js& ***! + \*****************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +/* harmony default export */ __webpack_exports__["default"] = ({ + name: 'Submit' +}); + +/***/ }), + +/***/ "./node_modules/process/browser.js": +/*!*****************************************!*\ + !*** ./node_modules/process/browser.js ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + + +/***/ }), + +/***/ "./node_modules/setimmediate/setImmediate.js": +/*!***************************************************!*\ + !*** ./node_modules/setimmediate/setImmediate.js ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) { + "use strict"; + + if (global.setImmediate) { + return; + } + + var nextHandle = 1; // Spec says greater than zero + var tasksByHandle = {}; + var currentlyRunningATask = false; + var doc = global.document; + var registerImmediate; + + function setImmediate(callback) { + // Callback can either be a function or a string + if (typeof callback !== "function") { + callback = new Function("" + callback); + } + // Copy function arguments + var args = new Array(arguments.length - 1); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i + 1]; + } + // Store and register the task + var task = { callback: callback, args: args }; + tasksByHandle[nextHandle] = task; + registerImmediate(nextHandle); + return nextHandle++; + } + + function clearImmediate(handle) { + delete tasksByHandle[handle]; + } + + function run(task) { + var callback = task.callback; + var args = task.args; + switch (args.length) { + case 0: + callback(); + break; + case 1: + callback(args[0]); + break; + case 2: + callback(args[0], args[1]); + break; + case 3: + callback(args[0], args[1], args[2]); + break; + default: + callback.apply(undefined, args); + break; + } + } + + function runIfPresent(handle) { + // From the spec: "Wait until any invocations of this algorithm started before this one have completed." + // So if we're currently running a task, we'll need to delay this invocation. + if (currentlyRunningATask) { + // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a + // "too much recursion" error. + setTimeout(runIfPresent, 0, handle); + } else { + var task = tasksByHandle[handle]; + if (task) { + currentlyRunningATask = true; + try { + run(task); + } finally { + clearImmediate(handle); + currentlyRunningATask = false; + } + } + } + } + + function installNextTickImplementation() { + registerImmediate = function(handle) { + process.nextTick(function () { runIfPresent(handle); }); + }; + } + + function canUsePostMessage() { + // The test against `importScripts` prevents this implementation from being installed inside a web worker, + // where `global.postMessage` means something completely different and can't be used for this purpose. + if (global.postMessage && !global.importScripts) { + var postMessageIsAsynchronous = true; + var oldOnMessage = global.onmessage; + global.onmessage = function() { + postMessageIsAsynchronous = false; + }; + global.postMessage("", "*"); + global.onmessage = oldOnMessage; + return postMessageIsAsynchronous; + } + } + + function installPostMessageImplementation() { + // Installs an event handler on `global` for the `message` event: see + // * https://developer.mozilla.org/en/DOM/window.postMessage + // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages + + var messagePrefix = "setImmediate$" + Math.random() + "$"; + var onGlobalMessage = function(event) { + if (event.source === global && + typeof event.data === "string" && + event.data.indexOf(messagePrefix) === 0) { + runIfPresent(+event.data.slice(messagePrefix.length)); + } + }; + + if (global.addEventListener) { + global.addEventListener("message", onGlobalMessage, false); + } else { + global.attachEvent("onmessage", onGlobalMessage); + } + + registerImmediate = function(handle) { + global.postMessage(messagePrefix + handle, "*"); + }; + } + + function installMessageChannelImplementation() { + var channel = new MessageChannel(); + channel.port1.onmessage = function(event) { + var handle = event.data; + runIfPresent(handle); + }; + + registerImmediate = function(handle) { + channel.port2.postMessage(handle); + }; + } + + function installReadyStateChangeImplementation() { + var html = doc.documentElement; + registerImmediate = function(handle) { + // Create a diff --git a/.stylelintrc.js b/stylelint.config.js similarity index 100% rename from .stylelintrc.js rename to stylelint.config.js diff --git a/templates/expired.php b/templates/expired.php new file mode 100644 index 0000000..0b47977 --- /dev/null +++ b/templates/expired.php @@ -0,0 +1,31 @@ + + * + * @author Inigo Jiron + * @author John Molakvoæ + * + * @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 . + * + */ + +?> +
+
+

t('Form Expired')); ?>

+

t('This Form has expired and is no longer taking answers')); ?>

+
diff --git a/templates/expired.tmpl.php b/templates/expired.tmpl.php deleted file mode 100644 index 0583d66..0000000 --- a/templates/expired.tmpl.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * @author Inigo Jiron - * - * @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 . - * - */ - - use OCP\Util; - Util::addStyle('forms', 'main'); -?> -
-
-

- t('Form Expired')); ?> -

-

- t('This Form has expired and is no longer taking answers.')); ?> -

-
diff --git a/templates/forms.tmpl.php b/templates/forms.tmpl.php deleted file mode 100644 index 3cdbf44..0000000 --- a/templates/forms.tmpl.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * @author Vinzenz Rosenkranz - * @author René Gieling - * - * @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 . - * - */ - -?> - - -
diff --git a/templates/no.acc.tmpl.php b/templates/no.acc.tmpl.php deleted file mode 100644 index cc16b01..0000000 --- a/templates/no.acc.tmpl.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * @author Vinzenz Rosenkranz - * - * @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 . - * - */ - - use OCP\Util; - Util::addStyle('forms', 'main'); -?> -
-
-

- t('Access denied')); ?> -

-

- t('You are not allowed to view this form or the form does not exist.')); ?> -

-
diff --git a/templates/no.create.tmpl.php b/templates/no.create.tmpl.php deleted file mode 100644 index cd56285..0000000 --- a/templates/no.create.tmpl.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * @author Vinzenz Rosenkranz - * - * @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 . - * - */ - - use OCP\Util; - Util::addStyle('forms', 'main'); -?> -
-
-

- t('Access denied')); ?> -

-

- t('You are not allowed to edit this form or the form does not exist.')); ?> -

-
diff --git a/templates/no.delete.tmpl.php b/templates/no.delete.tmpl.php deleted file mode 100644 index f847d26..0000000 --- a/templates/no.delete.tmpl.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * @author Vinzenz Rosenkranz - * - * @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 . - * - */ - - use OCP\Util; - Util::addStyle('forms', 'main'); -?> -
-
-

- t('Access denied')); ?> -

-

- t('You are either not allowed to delete this form or it doesn\'t exist.')); ?> -

-
diff --git a/templates/notfound.php b/templates/notfound.php new file mode 100644 index 0000000..c2bb0cd --- /dev/null +++ b/templates/notfound.php @@ -0,0 +1,31 @@ + + * + * @author Inigo Jiron + * @author John Molakvoæ + * + * @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 . + * + */ + +?> +
+
+

t('Form not found')); ?>

+

t('This form does not exists')); ?>

+
diff --git a/templates/submit.tmpl.php b/templates/submit.tmpl.php deleted file mode 100644 index b2c9f57..0000000 --- a/templates/submit.tmpl.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * @author Vinzenz Rosenkranz - * - * @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 . - * - */ - - use OCP\Util; - - Util::addStyle('forms', 'submit'); - - Util::addScript('forms', 'submit'); - Util::addScript('forms', 'survey.jquery.min'); - - /** @var \OCA\Forms\Db\Form $form */ - $form = $_['form']; - /** @var OCA\Forms\Db\Question[] $questions */ - $questions = $_['questions']; - -?> - -getIsAnonymous()):?> -*NOTE: This form is anonymous - - -
diff --git a/webpack.common.js b/webpack.common.js index 8e996ba..7c70b11 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,14 +1,23 @@ const path = require('path') -const { VueLoaderPlugin } = require('vue-loader') +const webpack = require('webpack') + const StyleLintPlugin = require('stylelint-webpack-plugin') +const VueLoaderPlugin = require('vue-loader/lib/plugin') + +const appName = process.env.npm_package_name +const appVersion = process.env.npm_package_version +console.info('Building', appName, appVersion, '\n') module.exports = { - entry: path.join(__dirname, 'src', 'main.js'), + entry: { + forms: path.resolve(path.join('src', 'main.js')), + submit: path.resolve(path.join('src', 'submit.js')), + }, output: { - path: path.resolve(__dirname, './js'), + path: path.resolve('./js'), publicPath: '/js/', - filename: 'forms.js', - chunkFilename: 'chunks/[name].js', + filename: `[name].js`, + chunkFilename: `${appName}.[name].js?v=[contenthash]`, }, module: { rules: [ @@ -36,18 +45,14 @@ module.exports = { loader: 'babel-loader', exclude: /node_modules/, }, - { - test: /\.(png|jpg|gif|svg)$/, - loader: 'url-loader', - options: { - limit: 8192, - }, - }, ], }, plugins: [ new VueLoaderPlugin(), new StyleLintPlugin(), + // Make appName & appVersion available as a constant + new webpack.DefinePlugin({ appName }), + new webpack.DefinePlugin({ appVersion }), ], resolve: { extensions: ['*', '.js', '.vue'], From f29d89557b24e431f96b86a72fa314d800841865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Tue, 28 Apr 2020 09:47:25 +0200 Subject: [PATCH 2/7] Cleanup PageController MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- lib/Controller/PageController.php | 112 +++++++++++------------------- 1 file changed, 39 insertions(+), 73 deletions(-) diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 3f45d2f..2d05d67 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -29,7 +29,6 @@ namespace OCA\Forms\Controller; -use OCA\Forms\AppInfo\Application; use OCA\Forms\Db\Form; use OCA\Forms\Db\FormMapper; use OCA\Forms\Db\Submission; @@ -42,61 +41,57 @@ use OCA\Forms\Db\QuestionMapper; use OCP\AppFramework\Controller; use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; -use OCP\IGroup; use OCP\IGroupManager; use OCP\IRequest; -use OCP\ILogger; use OCP\IURLGenerator; -use OCP\IUserManager; -use OCP\User; //To do: replace according to API +use OCP\IUserSession; use OCP\Util; class PageController extends Controller { protected $appName; - private $userId; + + /** @var FormMapper */ private $formMapper; + + /** @var SubmissionMapper */ private $submissionMapper; + + /** @var AnswerMapper */ private $answerMapper; - - private $questionMapper; - private $optionMapper; - + + /** @var IURLGenerator */ private $urlGenerator; - private $userMgr; + + /** @var IGroupManager */ private $groupManager; - - /** @var ILogger */ - private $logger; + + /** @var IUserSession */ + private $userSession; public function __construct(string $appName, IRequest $request, - IUserManager $userMgr, IGroupManager $groupManager, IURLGenerator $urlGenerator, FormMapper $formMapper, - $userId, QuestionMapper $questionMapper, OptionMapper $optionMapper, SubmissionMapper $SubmissionMapper, AnswerMapper $AnswerMapper, - ILogger $logger) { - parent::__construct(Application::APP_ID, $request); - $this->userMgr = $userMgr; + IUserSession $userSession) { + parent::__construct($appName, $request); + $this->groupManager = $groupManager; $this->urlGenerator = $urlGenerator; $this->appName = $appName; - $this->userId = $userId; $this->formMapper = $formMapper; - $this->questionMapper = $questionMapper; $this->optionMapper = $optionMapper; $this->submissionMapper = $SubmissionMapper; $this->answerMapper = $AnswerMapper; - $this->logger = $logger; + $this->userSession = $userSession; } /** @@ -126,6 +121,8 @@ class PageController extends Controller { /** * @NoAdminRequired * @NoCSRFRequired + * + * TODO: Implement cloning * * @return TemplateResponse */ @@ -170,6 +167,8 @@ class PageController extends Controller { // Inject style on all templates Util::addStyle($this->appName, 'forms'); + // TODO: check if already submitted + try { $form = $this->formMapper->findByHash($hash); } catch (DoesNotExistException $e) { @@ -186,72 +185,35 @@ class PageController extends Controller { return new TemplateResponse('forms', 'expired'); } - $renderAs = is_null($this->userId) ? 'user' : 'public'; + $renderAs = $this->userSession->isLoggedIn() ? 'user' : 'public'; Util::addScript($this->appName, 'submit'); return new TemplateResponse($this->appName, 'main', [], $renderAs); } - /** - * @NoAdminRequired - */ - public function getQuestions(int $formId): array { - $questionList = []; - try{ - $questionEntities = $this->questionMapper->findByForm($formId); - foreach ($questionEntities as $questionEntity) { - $question = $questionEntity->read(); - $question['options'] = $this->getOptions($question['id']); - $questionList[] = $question; - } - } catch (DoesNotExistException $e) { - //handle silently - } - - return $questionList; - } - - /** - * @NoAdminRequired - */ - public function getOptions(int $questionId): array { - $optionList = []; - - try{ - $optionEntities = $this->optionMapper->findByQuestion($questionId); - foreach ($optionEntities as $optionEntity) { - $optionList[] = $optionEntity->read(); - } - - } catch (DoesNotExistException $e) { - //handle silently - } - - return $optionList; - } - - /** * @NoAdminRequired * @PublicPage + * + * Process a new submission * @param int $formId * @param string $userId * @param array $answers * @param array $questions * @return RedirectResponse */ - public function insertSubmission($id, $userId, $answers, $questions) { + public function insertSubmission(int $id, string $userId, array $answers, array $questions) { $form = $this->formMapper->findById($id); $anonID = "anon-user-". hash('md5', (time() + rand())); - //Insert Submission + // Insert Submission $submission = new Submission(); $submission->setFormId($id); - if($form->getIsAnonymous()){ + if ($form->getIsAnonymous()){ $submission->setUserId($anonID); - }else{ + } else { $submission->setUserId($userId); } $submission->setTimestamp(time()); @@ -288,22 +250,26 @@ class PageController extends Controller { /** * @NoAdminRequired * Check if user has access to this form + * + * @param Form $form + * @return boolean */ private function hasUserAccess(Form $form): bool { $access = $form->getAccess(); $ownerId = $form->getOwnerId(); - + $user = $this->userSession->getUser(); + if ($access['type'] === 'public') { return true; } // Refuse access, if not public and no user logged in. - if ($this->userId === null) { + if (!$user) { return false; } // Always grant access to owner. - if ($ownerId === $this->userId) { + if ($ownerId === $user->getUID()) { return true; } @@ -311,7 +277,7 @@ class PageController extends Controller { if ($form->getSubmitOnce()) { $participants = $this->submissionMapper->findParticipantsByForm($form->getId()); foreach($participants as $participant) { - if ($participant === $this->userId) { + if ($participant === $user->getUID()) { return false; } } @@ -324,13 +290,13 @@ class PageController extends Controller { // Selected Access remains. // Grant Access, if user is in users-Array. - if (in_array($this->userId, $access['users'])) { + if (in_array($user->getUID(), $access['users'])) { return true; } // Check if access granted by group. foreach ($access['groups'] as $group) { - if( $this->groupManager->isInGroup($this->userId, $group) ) { + if( $this->groupManager->isInGroup($user->getUID(), $group) ) { return true; } } From 7462be7bfedc8a2d55cdd5ad5a2fa36dfc8dc121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Tue, 28 Apr 2020 10:50:44 +0200 Subject: [PATCH 3/7] Initial load and display of public form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- .eslintrc.js | 3 + js/submit.js | 9516 ----------------- lib/Controller/PageController.php | 50 +- package-lock.json | 8 + package.json | 1 + src/components/Questions/Question.vue | 17 +- src/components/Questions/QuestionLong.vue | 2 +- src/components/Questions/QuestionMultiple.vue | 20 +- src/components/Questions/QuestionShort.vue | 2 +- src/models/AnswerTypes.js | 4 + src/views/Create.vue | 2 +- src/views/Submit.vue | 134 +- webpack.common.js | 4 +- 13 files changed, 227 insertions(+), 9536 deletions(-) delete mode 100644 js/submit.js diff --git a/.eslintrc.js b/.eslintrc.js index 2f845f6..4a44f68 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,7 @@ module.exports = { + globals: { + appName: true, + }, extends: [ '@nextcloud', ] diff --git a/js/submit.js b/js/submit.js deleted file mode 100644 index a8e4fe6..0000000 --- a/js/submit.js +++ /dev/null @@ -1,9516 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = "/js/"; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./src/submit.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/@nextcloud/l10n/dist/index.js": -/*!****************************************************!*\ - !*** ./node_modules/@nextcloud/l10n/dist/index.js ***! - \****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getLocale = getLocale; -exports.getLanguage = getLanguage; -exports.translate = translate; -exports.translatePlural = translatePlural; -exports.getFirstDay = getFirstDay; -exports.getDayNames = getDayNames; -exports.getDayNamesShort = getDayNamesShort; -exports.getDayNamesMin = getDayNamesMin; -exports.getMonthNames = getMonthNames; -exports.getMonthNamesShort = getMonthNamesShort; - -/// - -/** - * Returns the user's locale - */ -function getLocale() { - if (typeof OC === 'undefined') { - console.warn('No OC found'); - return 'en'; - } - - return OC.getLocale(); -} -/** - * Returns the user's language - */ - - -function getLanguage() { - if (typeof OC === 'undefined') { - console.warn('No OC found'); - return 'en'; - } - - return OC.getLanguage(); -} - -/** - * Translate a string - * - * @param {string} app the id of the app for which to translate the string - * @param {string} text the string to translate - * @param {object} vars map of placeholder key to value - * @param {number} number to replace %n with - * @param {object} [options] options object - * @return {string} - */ -function translate(app, text, vars, count, options) { - if (typeof OC === 'undefined') { - console.warn('No OC found'); - return text; - } - - return OC.L10N.translate(app, text, vars, count, options); -} -/** - * Translate a plural string - * - * @param {string} app the id of the app for which to translate the string - * @param {string} textSingular the string to translate for exactly one object - * @param {string} textPlural the string to translate for n objects - * @param {number} count number to determine whether to use singular or plural - * @param {Object} vars of placeholder key to value - * @param {object} options options object - * @return {string} - */ - - -function translatePlural(app, textSingular, textPlural, count, vars, options) { - if (typeof OC === 'undefined') { - console.warn('No OC found'); - return textSingular; - } - - return OC.L10N.translatePlural(app, textSingular, textPlural, count, vars, options); -} -/** - * Get the first day of the week - * - * @return {number} - */ - - -function getFirstDay() { - if (typeof window.firstDay === 'undefined') { - console.warn('No firstDay found'); - return 1; - } - - return window.firstDay; -} -/** - * Get a list of day names (full names) - * - * @return {string[]} - */ - - -function getDayNames() { - if (typeof window.dayNames === 'undefined') { - console.warn('No dayNames found'); - return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - } - - return window.dayNames; -} -/** - * Get a list of day names (short names) - * - * @return {string[]} - */ - - -function getDayNamesShort() { - if (typeof window.dayNamesShort === 'undefined') { - console.warn('No dayNamesShort found'); - return ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.']; - } - - return window.dayNamesShort; -} -/** - * Get a list of day names (minified names) - * - * @return {string[]} - */ - - -function getDayNamesMin() { - if (typeof window.dayNamesMin === 'undefined') { - console.warn('No dayNamesMin found'); - return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; - } - - return window.dayNamesMin; -} -/** - * Get a list of month names (full names) - * - * @return {string[]} - */ - - -function getMonthNames() { - if (typeof window.monthNames === 'undefined') { - console.warn('No monthNames found'); - return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - } - - return window.monthNames; -} -/** - * Get a list of month names (short names) - * - * @return {string[]} - */ - - -function getMonthNamesShort() { - if (typeof window.monthNamesShort === 'undefined') { - console.warn('No monthNamesShort found'); - return ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.']; - } - - return window.monthNamesShort; -} -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ "./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js?!./src/views/Submit.vue?vue&type=script&lang=js&": -/*!*****************************************************************************************************************************************!*\ - !*** ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/views/Submit.vue?vue&type=script&lang=js& ***! - \*****************************************************************************************************************************************/ -/*! exports provided: default */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -/* harmony default export */ __webpack_exports__["default"] = ({ - name: 'Submit' -}); - -/***/ }), - -/***/ "./node_modules/process/browser.js": -/*!*****************************************!*\ - !*** ./node_modules/process/browser.js ***! - \*****************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - - -/***/ }), - -/***/ "./node_modules/setimmediate/setImmediate.js": -/*!***************************************************!*\ - !*** ./node_modules/setimmediate/setImmediate.js ***! - \***************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) { - "use strict"; - - if (global.setImmediate) { - return; - } - - var nextHandle = 1; // Spec says greater than zero - var tasksByHandle = {}; - var currentlyRunningATask = false; - var doc = global.document; - var registerImmediate; - - function setImmediate(callback) { - // Callback can either be a function or a string - if (typeof callback !== "function") { - callback = new Function("" + callback); - } - // Copy function arguments - var args = new Array(arguments.length - 1); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i + 1]; - } - // Store and register the task - var task = { callback: callback, args: args }; - tasksByHandle[nextHandle] = task; - registerImmediate(nextHandle); - return nextHandle++; - } - - function clearImmediate(handle) { - delete tasksByHandle[handle]; - } - - function run(task) { - var callback = task.callback; - var args = task.args; - switch (args.length) { - case 0: - callback(); - break; - case 1: - callback(args[0]); - break; - case 2: - callback(args[0], args[1]); - break; - case 3: - callback(args[0], args[1], args[2]); - break; - default: - callback.apply(undefined, args); - break; - } - } - - function runIfPresent(handle) { - // From the spec: "Wait until any invocations of this algorithm started before this one have completed." - // So if we're currently running a task, we'll need to delay this invocation. - if (currentlyRunningATask) { - // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a - // "too much recursion" error. - setTimeout(runIfPresent, 0, handle); - } else { - var task = tasksByHandle[handle]; - if (task) { - currentlyRunningATask = true; - try { - run(task); - } finally { - clearImmediate(handle); - currentlyRunningATask = false; - } - } - } - } - - function installNextTickImplementation() { - registerImmediate = function(handle) { - process.nextTick(function () { runIfPresent(handle); }); - }; - } - - function canUsePostMessage() { - // The test against `importScripts` prevents this implementation from being installed inside a web worker, - // where `global.postMessage` means something completely different and can't be used for this purpose. - if (global.postMessage && !global.importScripts) { - var postMessageIsAsynchronous = true; - var oldOnMessage = global.onmessage; - global.onmessage = function() { - postMessageIsAsynchronous = false; - }; - global.postMessage("", "*"); - global.onmessage = oldOnMessage; - return postMessageIsAsynchronous; - } - } - - function installPostMessageImplementation() { - // Installs an event handler on `global` for the `message` event: see - // * https://developer.mozilla.org/en/DOM/window.postMessage - // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages - - var messagePrefix = "setImmediate$" + Math.random() + "$"; - var onGlobalMessage = function(event) { - if (event.source === global && - typeof event.data === "string" && - event.data.indexOf(messagePrefix) === 0) { - runIfPresent(+event.data.slice(messagePrefix.length)); - } - }; - - if (global.addEventListener) { - global.addEventListener("message", onGlobalMessage, false); - } else { - global.attachEvent("onmessage", onGlobalMessage); - } - - registerImmediate = function(handle) { - global.postMessage(messagePrefix + handle, "*"); - }; - } - - function installMessageChannelImplementation() { - var channel = new MessageChannel(); - channel.port1.onmessage = function(event) { - var handle = event.data; - runIfPresent(handle); - }; - - registerImmediate = function(handle) { - channel.port2.postMessage(handle); - }; - } - - function installReadyStateChangeImplementation() { - var html = doc.documentElement; - registerImmediate = function(handle) { - // Create a - diff --git a/src/components/Questions/QuestionShort.vue b/src/components/Questions/QuestionShort.vue index 100b872..e52e7c3 100644 --- a/src/components/Questions/QuestionShort.vue +++ b/src/components/Questions/QuestionShort.vue @@ -32,7 +32,7 @@ question.options.length > 0, }, multiple: { component: QuestionMultiple, icon: 'icon-answer-checkbox', label: t('forms', 'Checkboxes'), + // Define conditions where this questions is not ok + validate: question => question.options.length > 0, }, short: { diff --git a/src/views/Create.vue b/src/views/Create.vue index 12d6524..807e931 100644 --- a/src/views/Create.vue +++ b/src/views/Create.vue @@ -392,7 +392,7 @@ export default { font-size: 2em; font-weight: bold; padding-left: 14px; // align with description (compensate font size diff) - overflow-x: hidden; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } diff --git a/src/views/Submit.vue b/src/views/Submit.vue index 1e3dc9d..5edfa10 100644 --- a/src/views/Submit.vue +++ b/src/views/Submit.vue @@ -21,11 +21,143 @@ --> + diff --git a/webpack.common.js b/webpack.common.js index 7c70b11..a828262 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -4,8 +4,8 @@ const webpack = require('webpack') const StyleLintPlugin = require('stylelint-webpack-plugin') const VueLoaderPlugin = require('vue-loader/lib/plugin') -const appName = process.env.npm_package_name -const appVersion = process.env.npm_package_version +const appName = process.env.npm_package_name.toString() +const appVersion = process.env.npm_package_version.toString() console.info('Building', appName, appVersion, '\n') module.exports = { From e0d41aef0cdd620dad1462c44913b30f0ce48005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Tue, 28 Apr 2020 15:36:12 +0200 Subject: [PATCH 4/7] Insert Submission, new API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- appinfo/routes.php | 2 +- lib/Controller/ApiController.php | 89 ++++++++++++++++++- lib/Controller/PageController.php | 77 +--------------- src/components/Questions/QuestionMultiple.vue | 23 ++++- src/views/Create.vue | 2 +- src/views/Submit.vue | 40 +++++++-- 6 files changed, 147 insertions(+), 86 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index eaeb20d..23817ae 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -32,7 +32,6 @@ return [ ['name' => 'page#getResult', 'url' => '/{hash}/results', 'verb' => 'GET'], ['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'], - ['name' => 'page#insert_submission', 'url' => '/insert/submission', 'verb' => 'POST'], // Forms ['name' => 'api#getForms', 'url' => '/api/v1/forms', 'verb' => 'GET'], @@ -53,6 +52,7 @@ return [ ['name' => 'api#deleteOption', 'url' => '/api/v1/option/{id}', 'verb' => 'DELETE'], ['name' => 'api#getSubmissions', 'url' => '/api/v1/submissions/{hash}', 'verb' => 'GET'], + ['name' => 'api#insertSubmission', 'url' => '/api/v1/submissions/insert', 'verb' => 'POST'], ['name' => 'system#get_site_users_and_groups', 'url' => '/get/siteusers', 'verb' => 'POST'], ] diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index be8b82d..98d6644 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -38,6 +38,7 @@ use OCP\AppFramework\Db\IMapperException; use OCP\ILogger; use OCP\IRequest; use OCP\IUser; +use OCP\IUserSession; use OCP\Security\ISecureRandom; use OCA\Forms\Db\Form; @@ -62,12 +63,13 @@ class ApiController extends Controller { /** @var ILogger */ private $logger; - /** @var string */ - private $userId; + /** @var IUserSession */ + private $userSession; public function __construct( IRequest $request, - $userId, + $userId, // TODO remove & replace with userSession below. + IUserSession $userSession, FormMapper $formMapper, SubmissionMapper $submissionMapper, AnswerMapper $answerMapper, @@ -77,6 +79,7 @@ class ApiController extends Controller { ) { parent::__construct(Application::APP_ID, $request); $this->userId = $userId; + $this->userSession = $userSession; $this->formMapper = $formMapper; $this->submissionMapper = $submissionMapper; $this->answerMapper = $answerMapper; @@ -603,4 +606,84 @@ class ApiController extends Controller { return new Http\JSONResponse($result); } + + /** + * @NoAdminRequired + * @PublicPage + * + * Process a new submission + * @param int $formId + * @param array $answers [question_id => arrayOfString] + */ + public function insertSubmission(int $formId, array $answers) { + $this->logger->debug('Inserting submission: formId: {formId}, answers: {answers}', [ + 'formId' => $formId, + 'answers' => $answers, + ]); + + try { + $form = $this->formMapper->findById($formId); + $questions = $this->getQuestions($formId); + } catch (IMapperException $e) { + $this->logger->debug('Could not find form'); + return new Http\JSONResponse(['message' => 'Could not find form'], Http::STATUS_BAD_REQUEST); + } + + $user = $this->userSession->getUser(); + // TODO check again hasUserAccess?! + + // Create Submission + $submission = new Submission(); + $submission->setFormId($formId); + $submission->setTimestamp(time()); + + // If not logged in or anonymous use anonID + if (!$user || $form->getIsAnonymous()){ + $anonID = "anon-user-". hash('md5', (time() + rand())); + $submission->setUserId($anonID); + } else { + $submission->setUserId($user->getUID()); + } + + // Insert new submission + $this->submissionMapper->insert($submission); + $submissionId = $submission->getId(); + + // Process Answers + foreach($answers as $questionId => $answerArray) { + // Search corresponding Question, skip processing if not found + $questionIndex = array_search($questionId, array_column($questions, 'id')); + if ($questionIndex === false) { + continue; + } else { + $question = $questions[$questionIndex]; + } + + foreach($answerArray as $answer) { + if($question['type'] === 'multiple' || $question['type'] === 'multiple_unique') { + // Search corresponding option, skip processing if not found + $optionIndex = array_search($answer, array_column($question['options'], 'id')); + if($optionIndex === false) { + continue; + } else { + $option = $question['options'][$optionIndex]; + } + + // Load option-text + $answerText = $option['text']; + + } else { + $answerText = $answer; // Not a multiple-question, answerText is given answer + } + + $answerEntity = new Answer(); + $answerEntity->setSubmissionId($submissionId); + $answerEntity->setQuestionId($question['id']); + $answerEntity->setText($answerText); + $this->answerMapper->insert($answerEntity); + } + } + + return new Http\JSONResponse([]); + } } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index c2e028f..1cfa186 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -29,19 +29,16 @@ namespace OCA\Forms\Controller; +use Exception; use OCA\Forms\Db\Form; use OCA\Forms\Db\FormMapper; -use OCA\Forms\Db\Submission; -use OCA\Forms\Db\SubmissionMapper; -use OCA\Forms\Db\Answer; -use OCA\Forms\Db\AnswerMapper; - -use OCA\Forms\Db\OptionMapper; +use OCA\Forms\Db\Question; use OCA\Forms\Db\QuestionMapper; +use OCA\Forms\Db\Option; +use OCA\Forms\Db\OptionMapper; use OCP\AppFramework\Controller; use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\IGroupManager; use OCP\IRequest; @@ -57,12 +54,6 @@ class PageController extends Controller { /** @var FormMapper */ private $formMapper; - /** @var SubmissionMapper */ - private $submissionMapper; - - /** @var AnswerMapper */ - private $answerMapper; - /** @var IURLGenerator */ private $urlGenerator; @@ -82,8 +73,6 @@ class PageController extends Controller { FormMapper $formMapper, QuestionMapper $questionMapper, OptionMapper $optionMapper, - SubmissionMapper $SubmissionMapper, - AnswerMapper $AnswerMapper, IUserSession $userSession, IInitialStateService $initialStateService) { parent::__construct($appName, $request); @@ -94,8 +83,6 @@ class PageController extends Controller { $this->formMapper = $formMapper; $this->questionMapper = $questionMapper; $this->optionMapper = $optionMapper; - $this->submissionMapper = $SubmissionMapper; - $this->answerMapper = $AnswerMapper; $this->userSession = $userSession; $this->initialStateService = $initialStateService; } @@ -237,62 +224,6 @@ class PageController extends Controller { return new TemplateResponse($this->appName, 'main', [], $renderAs); } - /** - * @NoAdminRequired - * @PublicPage - * - * Process a new submission - * @param int $formId - * @param string $userId - * @param array $answers - * @param array $questions - * @return RedirectResponse - */ - public function insertSubmission(int $id, string $userId, array $answers, array $questions) { - - $form = $this->formMapper->findById($id); - $anonID = "anon-user-". hash('md5', (time() + rand())); - - // Insert Submission - $submission = new Submission(); - $submission->setFormId($id); - if ($form->getIsAnonymous()){ - $submission->setUserId($anonID); - - } else { - $submission->setUserId($userId); - } - $submission->setTimestamp(time()); - $this->submissionMapper->insert($submission); - $submissionId = $submission->getId(); - - //Insert Answers - foreach($questions as $question) { - // If question is answered, the questionText exists as key in $answers. Does not exist, when a (non-mandatory) question was not answered. - if (array_key_exists($question['text'], $answers)) { - if($question['type'] === "checkbox"){ - foreach(($answers[$question['text']]) as $ansText) { - $answer = new Answer(); - $answer->setSubmissionId($submissionId); - $answer->setQuestionId($question['id']); - $answer->setText($ansText); - $this->answerMapper->insert($answer); - } - } else { - $answer = new Answer(); - $answer->setSubmissionId($submissionId); - $answer->setQuestionId($question['id']); - $answer->setText($answers[$question['text']]); - $this->answerMapper->insert($answer); - } - } - } - - $hash = $form->getHash(); - $url = $this->urlGenerator->linkToRoute('forms.page.goto_form', ['hash' => $hash]); - return new RedirectResponse($url); - } - /** * @NoAdminRequired * Check if user has access to this form diff --git a/src/components/Questions/QuestionMultiple.vue b/src/components/Questions/QuestionMultiple.vue index f251774..56069b9 100644 --- a/src/components/Questions/QuestionMultiple.vue +++ b/src/components/Questions/QuestionMultiple.vue @@ -43,7 +43,8 @@ }" :name="`${id}-answer`" :required="true /* TODO: implement required option */" - :type="isUnique ? 'radio' : 'checkbox'"> + :type="isUnique ? 'radio' : 'checkbox'" + @change="onChange($event, answer.id)">