Merge pull request #882 from nextcloud/refactor-mandatory

Refactor Mandatory
This commit is contained in:
Chartman123 2021-04-10 18:46:29 +02:00 коммит произвёл GitHub
Родитель f2e1ba6a6a 724e455b21
Коммит be009d4734
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 443 добавлений и 141 удалений

Просмотреть файл

@ -1,5 +1,12 @@
# Changelog
## v2.3.0 - tbd
### Deprecated
- Question property `mandatory` is deprecated and replaced by `isRequired`. The old property will be removed in API version 2.
[\#882](https://github.com/nextcloud/forms/pull/882) ([chartman123](https://github.com/Chartman123))
## v2.2.4 - 2021-03-30
[Full Changelog](https://github.com/nextcloud/forms/compare/v2.2.3...v2.2.4)

Просмотреть файл

@ -26,40 +26,192 @@
return [
'routes' => [
// Internal views
['name' => 'page#views', 'url' => '/{hash}/{view}', 'verb' => 'GET'],
[
'name' => 'page#views',
'url' => '/{hash}/{view}',
'verb' => 'GET'
],
// Share-Link & public submit
['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'],
[
'name' => 'page#goto_form',
'url' => '/{hash}',
'verb' => 'GET'
],
// App Root
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
[
'name' => 'page#index',
'url' => '/',
'verb' => 'GET'
],
],
'ocs' => [
// Forms
['name' => 'api#getForms', 'url' => '/api/v1/forms', 'verb' => 'GET'],
['name' => 'api#newForm', 'url' => '/api/v1/form', 'verb' => 'POST'],
['name' => 'api#getForm', 'url' => '/api/v1/form/{id}', 'verb' => 'GET'],
['name' => 'api#cloneForm', 'url' => '/api/v1/form/clone/{id}', 'verb' => 'POST'],
['name' => 'api#updateForm', 'url' => '/api/v1/form/update', 'verb' => 'POST'],
['name' => 'api#deleteForm', 'url' => '/api/v1/form/{id}', 'verb' => 'DELETE'],
['name' => 'api#getSharedForms', 'url' => '/api/v1/shared_forms', 'verb' => 'GET'],
[
'name' => 'api#getForms',
'url' => '/api/{apiVersion}/forms',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#newForm',
'url' => '/api/{apiVersion}/form',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#getForm',
'url' => '/api/{apiVersion}/form/{id}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#cloneForm',
'url' => '/api/{apiVersion}/form/clone/{id}',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#updateForm',
'url' => '/api/{apiVersion}/form/update',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#deleteForm',
'url' => '/api/{apiVersion}/form/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#getSharedForms',
'url' => '/api/{apiVersion}/shared_forms',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
// Questions
['name' => 'api#newQuestion', 'url' => '/api/v1/question', 'verb' => 'POST'],
['name' => 'api#updateQuestion', 'url' => '/api/v1/question/update', 'verb' => 'POST'],
['name' => 'api#reorderQuestions', 'url' => '/api/v1/question/reorder', 'verb' => 'POST'],
['name' => 'api#deleteQuestion', 'url' => '/api/v1/question/{id}', 'verb' => 'DELETE'],
[
'name' => 'api#newQuestion',
'url' => '/api/{apiVersion}/question',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#updateQuestion',
'url' => '/api/{apiVersion}/question/update',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#reorderQuestions',
'url' => '/api/{apiVersion}/question/reorder',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#deleteQuestion',
'url' => '/api/{apiVersion}/question/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
// Answers
['name' => 'api#newOption', 'url' => '/api/v1/option', 'verb' => 'POST'],
['name' => 'api#updateOption', 'url' => '/api/v1/option/update', 'verb' => 'POST'],
['name' => 'api#deleteOption', 'url' => '/api/v1/option/{id}', 'verb' => 'DELETE'],
// Options
[
'name' => 'api#newOption',
'url' => '/api/{apiVersion}/option',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#updateOption',
'url' => '/api/{apiVersion}/option/update',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#deleteOption',
'url' => '/api/{apiVersion}/option/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
// Submissions
['name' => 'api#getSubmissions', 'url' => '/api/v1/submissions/{hash}', 'verb' => 'GET'],
['name' => 'api#exportSubmissions', 'url' => '/api/v1/submissions/export/{hash}', 'verb' => 'GET'],
['name' => 'api#exportSubmissionsToCloud', 'url' => '/api/v1/submissions/export', 'verb' => 'POST'],
['name' => 'api#deleteAllSubmissions', 'url' => '/api/v1/submissions/{formId}', 'verb' => 'DELETE'],
['name' => 'api#insertSubmission', 'url' => '/api/v1/submission/insert', 'verb' => 'POST'],
['name' => 'api#deleteSubmission', 'url' => '/api/v1/submission/{id}', 'verb' => 'DELETE'],
[
'name' => 'api#getSubmissions',
'url' => '/api/{apiVersion}/submissions/{hash}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#exportSubmissions',
'url' => '/api/{apiVersion}/submissions/export/{hash}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#exportSubmissionsToCloud',
'url' => '/api/{apiVersion}/submissions/export',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#deleteAllSubmissions',
'url' => '/api/{apiVersion}/submissions/{formId}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#insertSubmission',
'url' => '/api/{apiVersion}/submission/insert',
'verb' => 'POST',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
[
'name' => 'api#deleteSubmission',
'url' => '/api/{apiVersion}/submission/{id}',
'verb' => 'DELETE',
'requirements' => [
'apiVersion' => 'v1(\.1)?'
]
],
]
];

Просмотреть файл

@ -20,10 +20,13 @@ This file contains the API-Documentation. For more information on the returned D
}
```
## Deprecation warning ⚠️
Starting with API version 1.1, the `mandatory` property of questions is deprecated and replaced by `isRequired`. It will be removed in API version 2.
## Form Endpoints
### List owned Forms
Returns condensed objects of all Forms beeing owned by the authenticated user.
- Endpoint: `/api/v1/forms`
- Endpoint: `/api/v1.1/forms`
- Method: `GET`
- Parameters: None
- Response: Array of condensed Form Objects, sorted as newest first.
@ -48,7 +51,7 @@ Returns condensed objects of all Forms beeing owned by the authenticated user.
### List shared Forms
Returns condensed objects of all Forms, that are shared to the authenticated user via instance ([access-type](DataStructure.md#share-types) `registered` or `selected`) and have not expired yet.
- Endpoint: `/api/v1/shared_forms`
- Endpoint: `/api/v1.1/shared_forms`
- Method: `GET`
- Parameters: None
- Response: Array of condensed Form Objects, sorted as newest first, similar to [List owned Forms](#list-owned-forms).
@ -57,7 +60,7 @@ See above, 'List owned forms'
```
### Create a new Form
- Endpoint: `/api/v1/form`
- Endpoint: `/api/v1.1/form`
- Method: `POST`
- Parameters: None
- Response: The new form object.
@ -82,7 +85,7 @@ See above, 'List owned forms'
### Request full data of a form
Returns the full-depth object of the requested form (without submissions).
- Endpoint: `/api/v1/form/{id}`
- Endpoint: `/api/v1.1/form/{id}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -111,7 +114,8 @@ Returns the full-depth object of the requested form (without submissions).
"formId": 3,
"order": 1,
"type": "dropdown",
"mandatory": false,
"mandatory": false, // deprecated, will be removed in API v2
"isRequired": false,
"text": "Question 1",
"options": [
{
@ -131,7 +135,8 @@ Returns the full-depth object of the requested form (without submissions).
"formId": 3,
"order": 2,
"type": "short",
"mandatory": true,
"mandatory": true, // deprecated, will be removed in API v2
"isRequired": true,
"text": "Question 2",
"options": []
}
@ -141,7 +146,7 @@ Returns the full-depth object of the requested form (without submissions).
### Clone a form
Creates a clone of a form (without submissions).
- Endpoint: `/api/v1/form/clone/{id}`
- Endpoint: `/api/v1.1/form/clone/{id}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -154,7 +159,7 @@ See section 'Request full data of a form'.
### Update form properties
Update a single or multiple properties of a form-object. Concerns **only** the Form-Object, properties of Questions, Options and Submissions, as well as their creation or deletion, are handled separately.
- Endpoint: `/api/v1/form/update`
- Endpoint: `/api/v1.1/form/update`
- Method: `POST`
- Parameters:
| Parameter | Type | Description |
@ -168,7 +173,7 @@ Update a single or multiple properties of a form-object. Concerns **only** the F
```
### Delete a form
- Endpoint: `/api/v1/form/{id}`
- Endpoint: `/api/v1.1/form/{id}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -183,7 +188,7 @@ Update a single or multiple properties of a form-object. Concerns **only** the F
Contains only manipulative question-endpoints. To retrieve questions, request the full form data.
### Create a new question
- Endpoint: `/api/v1/question`
- Endpoint: `/api/v1.1/question`
- Method: `POST`
- Parameters:
| Parameter | Type | Optional | Description |
@ -198,7 +203,8 @@ Contains only manipulative question-endpoints. To retrieve questions, request th
"formId": 3,
"order": 3,
"type": "short",
"mandatory": false,
"mandatory": false, // deprecated, will be removed in API v2
"isRequired": false,
"text": "",
"options": []
}
@ -206,7 +212,7 @@ Contains only manipulative question-endpoints. To retrieve questions, request th
### Update question properties
Update a single or multiple properties of a question-object.
- Endpoint: `/api/v1/question/update`
- Endpoint: `/api/v1.1/question/update`
- Method: `POST`
- Parameters:
| Parameter | Type | Description |
@ -221,7 +227,7 @@ Update a single or multiple properties of a question-object.
### Reorder questions
Reorders all Questions of a single form
- Endpoint: `/api/v1/question/reorder`
- Endpoint: `/api/v1.1/question/reorder`
- Method: `POST`
- Parameters:
| Parameter | Type | Description |
@ -245,7 +251,7 @@ Reorders all Questions of a single form
```
### Delete a question
- Endpoint: `/api/v1/question/{id}`
- Endpoint: `/api/v1.1/question/{id}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -260,7 +266,7 @@ Reorders all Questions of a single form
Contains only manipulative question-endpoints. To retrieve options, request the full form data.
### Create a new Option
- Endpoint: `/api/v1/option`
- Endpoint: `/api/v1.1/option`
- Method: `POST`
- Parameters:
| Parameter | Type | Description |
@ -277,7 +283,7 @@ Contains only manipulative question-endpoints. To retrieve options, request the
```
### Update option properties
- Endpoint: `/api/v1/option/update`
- Endpoint: `/api/v1.1/option/update`
- Method: `POST`
- Parameters:
| Parameter | Type | Description |
@ -291,7 +297,7 @@ Contains only manipulative question-endpoints. To retrieve options, request the
```
### Delete an option
- Endpoint: `/api/v1/option/{id}`
- Endpoint: `/api/v1.1/option/{id}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -306,7 +312,7 @@ Contains only manipulative question-endpoints. To retrieve options, request the
## Submission Endpoints
### Get Form Submissions
Get all Submissions to a Form
- Endpoint: `/api/v1/submissions/{hash}`
- Endpoint: `/api/v1.1/submissions/{hash}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -365,7 +371,8 @@ Get all Submissions to a Form
"formId": 3,
"order": 1,
"type": "dropdown",
"mandatory": false,
"mandatory": false, // deprecated, will be removed in API v2
"isRequired": false,
"text": "Question 1",
"options": [
{
@ -390,7 +397,8 @@ Get all Submissions to a Form
"formId": 3,
"order": 2,
"type": "short",
"mandatory": true,
"mandatory": true, // deprecated will be removed in API v2
"isRequired": true,
"text": "Question 2",
"options": []
}
@ -400,7 +408,7 @@ Get all Submissions to a Form
### Get Submissions as csv (Download)
Returns all submissions to the form in form of a csv-file.
- Endpoint: `/api/v1/submissions/export/{hash}`
- Endpoint: `/api/v1.1/submissions/export/{hash}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -415,7 +423,7 @@ Returns all submissions to the form in form of a csv-file.
### Export Submissions to Cloud (Files-App)
Creates a csv file and stores it to the cloud, resp. Files-App.
- Endpoint: `/api/v1/submissions/export`
- Endpoint: `/api/v1.1/submissions/export`
- Method: `POST`
- Parameters:
| Parameter | Type | Description |
@ -429,7 +437,7 @@ Creates a csv file and stores it to the cloud, resp. Files-App.
### Delete Submissions
Delete all Submissions to a form
- Endpoint: `/api/v1/submissions/{formId}`
- Endpoint: `/api/v1.1/submissions/{formId}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|
@ -442,7 +450,7 @@ Delete all Submissions to a form
### Insert a Submission
Store Submission to Database
- Endpoint: `/api/v1/submission/insert`
- Endpoint: `/api/v1.1/submission/insert`
- Method: `POST`
- Parameters:
| Parameter | Type | Description |
@ -462,7 +470,7 @@ Store Submission to Database
- Response: **Status-Code OK**.
### Delete a single Submission
- Endpoint: `/api/v1/submission/{id}`
- Endpoint: `/api/v1.1/submission/{id}`
- Url-Parameter:
| Parameter | Type | Description |
|-----------|---------|-------------|

Просмотреть файл

@ -1,7 +1,7 @@
# Forms Data Structure
**State: Forms v2.2.0 - 22.01.2021**
**State: Forms v2.3.0 - 09.04.2021**
This document describes the Obect-Structure, that is used within the Forms App and on Forms API. It does partially **not** equal the actual database structure behind.
This document describes the Object-Structure, that is used within the Forms App and on Forms API v1.1. It does partially **not** equal the actual database structure behind.
## Data Structures
### Form
@ -46,7 +46,8 @@ This document describes the Obect-Structure, that is used within the Forms App a
| formId | Integer | | The id of the form, the question belongs to |
| order | Integer | unique within form; *not* `0` | The order of the question within that form. Value `0` indicates deleted questions within database (typ. not visible outside) |
| type | [Question-Type](#question-types) | | Type of the question |
| mandatory | Boolean | | If the question is mandatory to fill the form |
| _mandatory_ | _Boolean_ | | _deprecated: will be removed in API v2, replaced by `isRequired`_ |
| isRequired | Boolean | | If the question is required to fill the form |
| text | String | max. 2048 ch. | The question-text |
| options | Array of [Options](#option) | | Array of options belonging to the question. Only relevant for question-type with predefined options. |
```
@ -55,7 +56,8 @@ This document describes the Obect-Structure, that is used within the Forms App a
"formId": 3,
"order": 1,
"type": "dropdown",
"mandatory": false,
"mandatory": false, // deprecated, will be removed in API v2
"isRequired": false,
"text": "Question 1",
"options": []
}

Просмотреть файл

@ -215,6 +215,12 @@ class ApiController extends OCSController {
throw new OCSForbiddenException();
}
// TODO remove after removal of API v1.1
// Backward compatibility for mandatory
foreach ($form['questions'] as $index => $question) {
$form['questions'][$index]['mandatory'] = $question['isRequired'];
}
return new DataResponse($form);
}
@ -483,13 +489,17 @@ class ApiController extends OCSController {
$question->setOrder($questionOrder);
$question->setType($type);
$question->setText($text);
$question->setMandatory(false);
$question->setIsRequired(false);
$question = $this->questionMapper->insert($question);
$response = $question->read();
$response['options'] = [];
// TODO remove after removal of API v1.1
// Backward compatibility for mandatory
$response['mandatory'] = $response['isRequired'];
return new DataResponse($response);
}
@ -634,6 +644,14 @@ class ApiController extends OCSController {
throw new OCSForbiddenException('Please use reorderQuestions() to change order');
}
// TODO remove after removal of API v1.1
// Rename deprecated mandatory key to isRequired
if (key_exists('mandatory', $keyValuePairs)) {
$this->logger->info('Key \'mandatory\' is deprecated, please use \'isRequired\'.');
$keyValuePairs['isRequired'] = $keyValuePairs['mandatory'];
unset($keyValuePairs['mandatory']);
}
// Create QuestionEntity with given Params & Id.
$question = Question::fromParams($keyValuePairs);
$question->setId($id);
@ -903,6 +921,12 @@ class ApiController extends OCSController {
// Load currently active questions
$questions = $this->formsService->getQuestions($form->getId());
// TODO remove after removal of API v1.1
// Backward compatibility for mandatory
foreach ($questions as $index => $question) {
$questions[$index]['mandatory'] = $question['isRequired'];
}
$response = [
'submissions' => $submissions,
'questions' => $questions,

Просмотреть файл

@ -47,7 +47,7 @@ class Question extends Entity {
protected $formId;
protected $order;
protected $type;
protected $mandatory;
protected $isRequired;
protected $text;
public const TYPES = [
@ -64,7 +64,7 @@ class Question extends Entity {
$this->addType('formId', 'integer');
$this->addType('order', 'integer');
$this->addType('type', 'string');
$this->addType('mandatory', 'bool');
$this->addType('isRequired', 'bool');
$this->addType('text', 'string');
}
@ -74,7 +74,7 @@ class Question extends Entity {
'formId' => $this->getFormId(),
'order' => $this->getOrder(),
'type' => htmlspecialchars_decode($this->getType()),
'mandatory' => $this->getMandatory(),
'isRequired' => $this->getIsRequired(),
'text' => htmlspecialchars_decode($this->getText()),
];
}

Просмотреть файл

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 Christian Hartmann <chris-hartmann@gmx.de>
*
* @author Christian Hartmann <chris-hartmann@gmx.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Forms\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version020300Date20210406114130 extends SimpleMigrationStep {
/** @var IDBConnection */
protected $connection;
/**
* @param IDBConnection $connection
*/
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
}
private const TYPE_BOOLEAN = 'boolean';
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('forms_v2_questions');
if (!$table->hasColumn('is_required')) {
$table->addColumn('is_required', self::TYPE_BOOLEAN, [
'notnull' => false,
'default' => 0,
]);
return $schema;
}
return null;
}
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
$qb_update = $this->connection->getQueryBuilder();
$qb_update->update('forms_v2_questions')
->set('is_required', 'mandatory');
$qb_update->execute();
}
}

Просмотреть файл

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 Christian Hartmann <chris-hartmann@gmx.de>
*
* @author Christian Hartmann <chris-hartmann@gmx.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Forms\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version020300Date20210406133704 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('forms_v2_questions');
if ($table->hasColumn('mandatory') && $table->hasColumn('is_required')) {
$table->dropColumn('mandatory');
return $schema;
}
return null;
}
}

Просмотреть файл

@ -202,7 +202,7 @@ export default {
// Load Owned forms
try {
const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'forms')
const response = await axios.get(generateOcsUrl('apps/forms/api/v1.1', 2) + 'forms')
this.forms = OcsResponse2Data(response)
} catch (error) {
showError(t('forms', 'An error occurred while loading the forms list'))
@ -211,7 +211,7 @@ export default {
// Load shared forms
try {
const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + 'shared_forms')
const response = await axios.get(generateOcsUrl('apps/forms/api/v1.1', 2) + 'shared_forms')
this.sharedForms = OcsResponse2Data(response)
} catch (error) {
showError(t('forms', 'An error occurred while loading the forms list'))
@ -227,7 +227,7 @@ export default {
async onNewForm() {
try {
// Request a new empty form
const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'form')
const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'form')
const newForm = OcsResponse2Data(response)
this.forms.unshift(newForm)
this.$router.push({ name: 'edit', params: { hash: newForm.hash } })
@ -245,7 +245,7 @@ export default {
*/
async onCloneForm(id) {
try {
const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + `form/clone/${id}`)
const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + `form/clone/${id}`)
const newForm = OcsResponse2Data(response)
this.forms.unshift(newForm)
this.$router.push({ name: 'edit', params: { hash: newForm.hash } })

Просмотреть файл

@ -183,7 +183,7 @@ export default {
// All good, let's delete
this.loading = true
try {
await axios.delete(generateOcsUrl('apps/forms/api/v1', 2) + `form/${this.form.id}`)
await axios.delete(generateOcsUrl('apps/forms/api/v1.1', 2) + `form/${this.form.id}`)
this.$emit('delete', this.form.id)
} catch (error) {
showError(t('forms', 'Error while deleting {title}', { title: this.formTitle }))

Просмотреть файл

@ -146,7 +146,7 @@ export default {
*/
async createAnswer(answer) {
try {
const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'option', {
const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'option', {
questionId: answer.questionId,
text: answer.text,
})
@ -174,7 +174,7 @@ export default {
*/
async updateAnswer(answer) {
try {
await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'option/update', {
await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'option/update', {
id: this.answer.id,
keyValuePairs: {
text: answer.text,

Просмотреть файл

@ -51,8 +51,8 @@
class="question__header-warning icon-error-color"
tabindex="0" />
<Actions v-if="!readOnly" class="question__header-menu" :force-menu="true">
<ActionCheckbox :checked="mandatory"
@update:checked="onMandatoryChange">
<ActionCheckbox :checked="isRequired"
@update:checked="onRequiredChange">
<!-- TRANSLATORS Making this question necessary to be answered when submitting to a form -->
{{ t('forms', 'Required') }}
</ActionCheckbox>
@ -99,7 +99,7 @@ export default {
type: String,
required: true,
},
mandatory: {
isRequired: {
type: Boolean,
required: true,
},
@ -131,11 +131,11 @@ export default {
computed: {
/**
* Extend text with asterisk if question is mandatory
* Extend text with asterisk if question is required
* @returns {Boolean}
*/
computedText() {
if (this.mandatory) {
if (this.isRequired) {
return this.text + ' *'
}
return this.text
@ -155,8 +155,8 @@ export default {
this.$emit('update:text', target.value)
},
onMandatoryChange(mandatory) {
this.$emit('update:mandatory', mandatory)
onRequiredChange(isRequired) {
this.$emit('update:isRequired', isRequired)
},
/**

Просмотреть файл

@ -24,14 +24,14 @@
<Question
v-bind.sync="$attrs"
:text="text"
:mandatory="mandatory"
:is-required="isRequired"
:edit.sync="edit"
:read-only="readOnly"
:max-question-length="maxStringLengths.questionText"
:title-placeholder="answerType.titlePlaceholder"
:warning-invalid="answerType.warningInvalid"
@update:text="onTitleChange"
@update:mandatory="onMandatoryChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
<div class="question__content">
<DatetimePicker
@ -103,7 +103,7 @@ export default {
*/
inputAttr() {
return {
required: this.mandatory,
required: this.isRequired,
}
},
},

Просмотреть файл

@ -24,7 +24,7 @@
<Question
v-bind.sync="$attrs"
:text="text"
:mandatory="mandatory"
:is-required="isRequired"
:edit.sync="edit"
:read-only="readOnly"
:max-question-length="maxStringLengths.questionText"
@ -33,13 +33,13 @@
:content-valid="contentValid"
:shift-drag-handle="shiftDragHandle"
@update:text="onTitleChange"
@update:mandatory="onMandatoryChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
<select v-if="!edit"
:id="text"
:name="text"
:multiple="isMultiple"
:required="mandatory"
:required="isRequired"
class="question__content"
@change="onChange">
<option value="">
@ -179,30 +179,6 @@ export default {
return this.values.indexOf(id) > -1
},
/**
* Is the provided answer required ?
* This is needed for checkboxes as html5
* doesn't allow to require at least ONE checked.
* So we require the one that are checked or all
* if none are checked yet.
* @param {number} id the answer id
* @returns {boolean}
*/
isRequired(id) {
// false, if question not mandatory
if (!this.mandatory) {
return false
}
// true for simple select
if (!this.isMultiple) {
return true
}
// For checkboxes, only required if no other is checked
return this.areNoneChecked
},
/**
* Update the options
* @param {Array} options options to change
@ -305,7 +281,7 @@ export default {
if (!option.local) {
// let's not await, deleting in background
axios.delete(generateOcsUrl('apps/forms/api/v1', 2) + `option/${option.id}`)
axios.delete(generateOcsUrl('apps/forms/api/v1.1', 2) + `option/${option.id}`)
.catch(error => {
showError(t('forms', 'There was an issue deleting this option'))
console.error(error)

Просмотреть файл

@ -24,21 +24,21 @@
<Question
v-bind.sync="$attrs"
:text="text"
:mandatory="mandatory"
:is-required="isRequired"
:edit.sync="edit"
:read-only="readOnly"
:max-question-length="maxStringLengths.questionText"
:title-placeholder="answerType.titlePlaceholder"
:warning-invalid="answerType.warningInvalid"
@update:text="onTitleChange"
@update:mandatory="onMandatoryChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
<div class="question__content">
<textarea ref="textarea"
:aria-label="t('forms', 'A long answer for the question “{text}”', { text })"
:placeholder="submissionInputPlaceholder"
:disabled="!readOnly"
:required="mandatory"
:required="isRequired"
:value="values[0]"
class="question__text"
:maxlength="maxStringLengths.answerText"

Просмотреть файл

@ -24,7 +24,7 @@
<Question
v-bind.sync="$attrs"
:text="text"
:mandatory="mandatory"
:is-required="isRequired"
:edit.sync="edit"
:read-only="readOnly"
:max-question-length="maxStringLengths.questionText"
@ -33,7 +33,7 @@
:content-valid="contentValid"
:shift-drag-handle="shiftDragHandle"
@update:text="onTitleChange"
@update:mandatory="onMandatoryChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
<ul class="question__content">
<template v-for="(answer, index) in options">
@ -49,7 +49,7 @@
'checkbox question__checkbox': !isUnique,
}"
:name="`${id}-answer`"
:required="isRequired(answer.id)"
:required="checkRequired(answer.id)"
:type="isUnique ? 'radio' : 'checkbox'"
@change="onChange($event, answer.id)"
@keydown.enter.exact.prevent="onKeydownEnter">
@ -195,9 +195,9 @@ export default {
* @param {number} id the answer id
* @returns {boolean}
*/
isRequired(id) {
// false, if question not mandatory
if (!this.mandatory) {
checkRequired(id) {
// false, if question not required
if (!this.isRequired) {
return false
}
@ -312,7 +312,7 @@ export default {
if (!option.local) {
// let's not await, deleting in background
axios.delete(generateOcsUrl('apps/forms/api/v1', 2) + `option/${option.id}`)
axios.delete(generateOcsUrl('apps/forms/api/v1.1', 2) + `option/${option.id}`)
.catch(error => {
showError(t('forms', 'There was an issue deleting this option'))
console.error(error)

Просмотреть файл

@ -24,21 +24,21 @@
<Question
v-bind.sync="$attrs"
:text="text"
:mandatory="mandatory"
:is-required="isRequired"
:edit.sync="edit"
:read-only="readOnly"
:max-question-length="maxStringLengths.questionText"
:title-placeholder="answerType.titlePlaceholder"
:warning-invalid="answerType.warningInvalid"
@update:text="onTitleChange"
@update:mandatory="onMandatoryChange"
@update:isRequired="onRequiredChange"
@delete="onDelete">
<div class="question__content">
<input ref="input"
:aria-label="t('forms', 'A short answer for the question “{text}”', { text })"
:placeholder="submissionInputPlaceholder"
:disabled="!readOnly"
:required="mandatory"
:required="isRequired"
:value="values[0]"
class="question__input"
:maxlength="maxStringLengths.answerText"

Просмотреть файл

@ -46,9 +46,9 @@ export default {
},
/**
* Mandatory-Setting
* Required-Setting
*/
mandatory: {
isRequired: {
type: Boolean,
required: true,
},
@ -119,13 +119,13 @@ export default {
}, 200),
/**
* Forward the mandatory change to the parent and store to db
* Forward the required change to the parent and store to db
*
* @param {Boolean} mandatoryValue new mandatory Value
* @param {Boolean} isRequiredValue new isRequired Value
*/
onMandatoryChange: debounce(function(mandatoryValue) {
this.$emit('update:mandatory', mandatoryValue)
this.saveQuestionProperty('mandatory', mandatoryValue)
onRequiredChange: debounce(function(isRequiredValue) {
this.$emit('update:isRequired', isRequiredValue)
this.saveQuestionProperty('isRequired', isRequiredValue)
}, 200),
/**
@ -170,7 +170,7 @@ export default {
async saveQuestionProperty(key, value) {
try {
// TODO: add loading status feedback ?
await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'question/update', {
await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'question/update', {
id: this.id,
keyValuePairs: {
[key]: value,

Просмотреть файл

@ -88,7 +88,7 @@ export default {
this.cancelFetchFullForm = cancel
try {
const response = await request(generateOcsUrl('apps/forms/api/v1', 2) + `form/${id}`)
const response = await request(generateOcsUrl('apps/forms/api/v1.1', 2) + `form/${id}`)
this.$emit('update:form', OcsResponse2Data(response))
this.isLoadingForm = false
} catch (error) {
@ -108,7 +108,7 @@ export default {
async saveFormProperty(key) {
try {
// TODO: add loading status feedback ?
await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'form/update', {
await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'form/update', {
id: this.form.id,
keyValuePairs: {
[key]: this.form[key],

Просмотреть файл

@ -204,8 +204,8 @@ export default {
return this.form.questions && this.form.questions.length === 0
},
isMandatoryUsed() {
return this.form.questions.reduce((isUsed, question) => isUsed || question.mandatory, false)
isRequiredUsed() {
return this.form.questions.reduce((isUsed, question) => isUsed || question.isRequired, false)
},
infoMessage() {
@ -219,7 +219,7 @@ export default {
message += t('forms', 'Responses are connected to your Nextcloud account.')
}
if (this.isMandatoryUsed) {
if (this.isRequiredUsed) {
message += ' ' + t('forms', 'An asterisk (*) indicates mandatory questions.')
}
@ -269,7 +269,7 @@ export default {
this.isLoadingQuestions = true
try {
const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'question', {
const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'question', {
formId: this.form.id,
type,
text,
@ -307,7 +307,7 @@ export default {
this.isLoadingQuestions = true
try {
await axios.delete(generateOcsUrl('apps/forms/api/v1', 2) + `question/${id}`)
await axios.delete(generateOcsUrl('apps/forms/api/v1.1', 2) + `question/${id}`)
const index = this.form.questions.findIndex(search => search.id === id)
this.form.questions.splice(index, 1)
} catch (error) {
@ -326,7 +326,7 @@ export default {
const newOrder = this.form.questions.map(question => question.id)
try {
await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'question/reorder', {
await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'question/reorder', {
formId: this.form.id,
newOrder,
})

Просмотреть файл

@ -192,7 +192,7 @@ export default {
* @returns {string}
*/
downloadUrl() {
return generateOcsUrl('apps/forms/api/v1', 2) + `submissions/export/${this.form.hash}`
return generateOcsUrl('apps/forms/api/v1.1', 2) + `submissions/export/${this.form.hash}`
},
},
@ -223,7 +223,7 @@ export default {
console.debug('Loading results for form', this.form.hash)
try {
const response = await axios.get(generateOcsUrl('apps/forms/api/v1', 2) + `submissions/${this.form.hash}`)
const response = await axios.get(generateOcsUrl('apps/forms/api/v1.1', 2) + `submissions/${this.form.hash}`)
// Append questions & submissions
this.$set(this.form, 'submissions', OcsResponse2Data(response).submissions)
@ -242,7 +242,7 @@ export default {
picker.pick()
.then(async(path) => {
try {
const response = await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'submissions/export', {
const response = await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'submissions/export', {
hash: this.form.hash,
path,
})
@ -258,7 +258,7 @@ export default {
this.loadingResults = true
try {
await axios.delete(generateOcsUrl('apps/forms/api/v1', 2) + `submission/${id}`)
await axios.delete(generateOcsUrl('apps/forms/api/v1.1', 2) + `submission/${id}`)
const index = this.form.submissions.findIndex(search => search.id === id)
this.form.submissions.splice(index, 1)
} catch (error) {
@ -276,7 +276,7 @@ export default {
this.loadingResults = true
try {
await axios.delete(generateOcsUrl('apps/forms/api/v1', 2) + `submissions/${this.form.id}`)
await axios.delete(generateOcsUrl('apps/forms/api/v1.1', 2) + `submissions/${this.form.id}`)
this.form.submissions = []
} catch (error) {
console.error(error)

Просмотреть файл

@ -152,8 +152,8 @@ export default {
})
},
isMandatoryUsed() {
return this.form.questions.reduce((isUsed, question) => isUsed || question.mandatory, false)
isRequiredUsed() {
return this.form.questions.reduce((isUsed, question) => isUsed || question.isRequired, false)
},
infoMessage() {
@ -164,7 +164,7 @@ export default {
if (!this.form.isAnonymous && this.isLoggedIn) {
message += t('forms', 'Responses are connected to your Nextcloud account.')
}
if (this.isMandatoryUsed) {
if (this.isRequiredUsed) {
message += ' ' + t('forms', 'An asterisk (*) indicates mandatory questions.')
}
@ -226,7 +226,7 @@ export default {
this.loading = true
try {
await axios.post(generateOcsUrl('apps/forms/api/v1', 2) + 'submission/insert', {
await axios.post(generateOcsUrl('apps/forms/api/v1.1', 2) + 'submission/insert', {
formId: this.form.id,
answers: this.answers,
})