Commit 55c0cb26 authored by Alex ORLUC's avatar Alex ORLUC
Browse files

Merge branch 'develop' into 'master'

Develop

See merge request maarch/MaarchCourrier!297
parents 6a7ebc29 07f44c2f
......@@ -2,7 +2,6 @@
[![coverage report](https://labs.maarch.org/maarch/MaarchCourrier/badges/develop/coverage.svg)](https://labs.maarch.org/maarch/MaarchCourrier/commits/develop)
# Maarch Courrier
Gestionnaire Électronique de Correspondances – Libre et Open Source –
......@@ -40,5 +39,4 @@ error_reporting = E_ALL & ~E_NOTICE
display_errors = On
## Le coin des developpeurs
[Maarch Developer handbook](https://labs.maarch.org/maarch/MaarchCourrier/blob/master/CONTRIBUTING.md)
[Maarch Developer handbook](https://labs.maarch.org/maarch/MaarchCourrier/blob/master/CONTRIBUTING.md)
\ No newline at end of file
......@@ -64,7 +64,7 @@ if ($notification['is_enabled'] === 'N') {
//SECOND STEP
writeLog(['message' => "Loading events for notification {$notificationId}", 'level' => 'INFO']);
$events = \Notification\models\NotificationsEventsModel::get(['select' => ['*'], 'where' => ['notification_sid = ?', 'exec_date is NULL'], 'data' => [$notification['notification_sid']]]);
$events = \Notification\models\NotificationsEventsModel::get(['select' => ['*'], 'where' => ['notification_sid = ?', 'exec_date is NULL'], 'data' => [$notification['notification_sid']], 'orderBy' => ['event_date desc']]);
$totalEventsToProcess = count($events);
$currentEvent = 0;
if ($totalEventsToProcess === 0) {
......
......@@ -102,7 +102,8 @@ class EntityController
'email' => $entity['email'],
'producerService' => $entity['producer_service'],
'business_id' => $entity['business_id'],
'external_id' => $entity['external_id']
'external_id' => $entity['external_id'],
'fastParapheurSubscriberId' => json_decode($entity['external_id'], true)['fastParapheurSubscriberId'],
];
$aEntities = EntityModel::getAllowedEntitiesByUserId(['userId' => $GLOBALS['login']]);
......@@ -219,6 +220,10 @@ class EntityController
return $response->withStatus(400)->withJson(['errors' => _ENTITY_ID_ALREADY_EXISTS]);
}
$externalId = [];
if (!empty($body['fastParapheurSubscriberId'])) {
$externalId['fastParapheurSubscriberId'] = $body['fastParapheurSubscriberId'];
}
$id = EntityModel::create([
'entity_id' => $body['entity_id'],
'entity_label' => $body['entity_label'],
......@@ -236,7 +241,8 @@ class EntityController
'entity_type' => $body['entity_type'],
'ldap_id' => $body['ldap_id'],
'entity_full_name' => $body['entity_full_name'],
'producer_service' => $body['producerService']
'producer_service' => $body['producerService'],
'external_id' => json_encode($externalId)
]);
HistoryController::add([
'tableName' => 'entities',
......@@ -274,7 +280,7 @@ class EntityController
return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']);
}
$entity = EntityModel::getByEntityId(['entityId' => $aArgs['id'], 'select' => [1]]);
$entity = EntityModel::getByEntityId(['entityId' => $aArgs['id'], 'select' => ['id', 'external_id']]);
if (empty($entity)) {
return $response->withStatus(400)->withJson(['errors' => 'Entity not found']);
}
......@@ -306,6 +312,12 @@ class EntityController
$body['producer_service'] = $aArgs['id'];
}
$externalId = json_decode($entity['external_id'], true);
if (!empty($body['fastParapheurSubscriberId'])) {
$externalId['fastParapheurSubscriberId'] = $body['fastParapheurSubscriberId'];
} else {
unset($externalId['fastParapheurSubscriberId']);
}
EntityModel::update(['set' => [
'entity_label' => $body['entity_label'],
'short_label' => $body['short_label'],
......@@ -322,7 +334,8 @@ class EntityController
'entity_type' => $body['entity_type'],
'ldap_id' => $body['ldap_id'],
'entity_full_name' => $body['entity_full_name'],
'producer_service' => $body['producerService']
'producer_service' => $body['producerService'],
'external_id' => json_encode($externalId)
],
'where' => ['entity_id = ?'],
'data' => [$aArgs['id']]
......
......@@ -109,7 +109,8 @@ abstract class EntityModelAbstract
'entity_type' => $args['entity_type'],
'ldap_id' => $args['ldap_id'],
'entity_full_name' => $args['entity_full_name'],
'producer_service' => $args['producer_service']
'producer_service' => $args['producer_service'],
'external_id' => $args['external_id']
]
]);
......
......@@ -367,7 +367,7 @@ class FastParapheurController
$config = $aArgs['config'];
// We need the SIRET field and the user_id of the signatory user's primary entity
$signatory = DatabaseModel::select([
'select' => ['user_id', 'business_id', 'entities.entity_label'],
'select' => ['user_id', 'external_id', 'entities.entity_label'],
'table' => ['listinstance', 'users_entities', 'entities'],
'left_join' => ['item_id = user_id', 'users_entities.entity_id = entities.entity_id'],
'where' => ['res_id = ?', 'item_mode = ?', 'process_date is null'],
......@@ -381,6 +381,7 @@ class FastParapheurController
'data' => [$aArgs['resIdMaster']]
])[0];
$signatory['business_id'] = json_decode($signatory['external_id'], true)['fastParapheurSubscriberId'];
if (empty($signatory['business_id']) || substr($signatory['business_id'], 0, 3) == 'org') {
$signatory['business_id'] = $config['data']['subscriberId'];
}
......
......@@ -61,7 +61,7 @@ use User\models\UserSignatureModel;
class UserController
{
const ALTERNATIVES_CONNECTIONS_METHODS = ['sso', 'cas', 'ldap', 'keycloak', 'shibboleth'];
const ALTERNATIVES_CONNECTIONS_METHODS = ['sso', 'cas', 'ldap', 'keycloak', 'shibboleth', 'azure_saml'];
public function get(Request $request, Response $response)
{
......
......@@ -373,7 +373,7 @@ class AuthenticationController
'eventType' => 'LOGIN',
'info' => _LOGIN . ' : ' . $login,
'moduleId' => 'authentication',
'eventId' => 'login'
'eventId' => 'userlogin'
]);
return $response->withStatus(204);
......@@ -395,6 +395,15 @@ class AuthenticationController
$logoutUrl = $disconnection['logoutUrl'];
}
HistoryController::add([
'tableName' => 'users',
'recordId' => $GLOBALS['id'],
'eventType' => 'LOGOUT',
'info' => _LOGOUT . ' : ' . $GLOBALS['login'],
'moduleId' => 'authentication',
'eventId' => 'userlogout'
]);
return $response->withJson(['logoutUrl' => $logoutUrl]);
}
......
......@@ -152,6 +152,12 @@ class AutoCompleteController
}
$curlResponse['response'][$key]['idToDisplay'] = $value['firstname'] . ' ' . $value['lastname'];
$curlResponse['response'][$key]['externalId']['maarchParapheur'] = $value['id'];
// Remove external value in signatureModes
$array = $curlResponse['response'][$key]['signatureModes'];
unset($array[array_search('external', $array)]);
$array = array_values($array);
$curlResponse['response'][$key]['signatureModes'] = $array;
}
return $response->withJson($curlResponse['response']);
} else {
......
......@@ -160,6 +160,7 @@ define('_DOCUMENT_UNSIGNED', 'Document unsigned');
define('_LINK_ADDED_TAG', 'Tag link added');
define('_LINK_DELETED_TAG', 'Tag link deleted');
define('_LOGIN', 'User connection');
define('_LOGOUT', 'User disconnection');
// ADMINISTRATION
define('_USERS', 'Users');
......
......@@ -160,6 +160,7 @@ define('_DOCUMENT_UNSIGNED', 'Document dé-signé');
define('_LINK_ADDED_TAG', 'Liaison de mot-clé ajoutée');
define('_LINK_DELETED_TAG', 'Liaison de mot-clé supprimée');
define('_LOGIN', 'Connexion de l\'utilisateur');
define('_LOGOUT', 'Déconnexion de l\'utilisateur');
// ADMINISTRATION
define('_USERS', 'Utilisateurs');
......
......@@ -80,10 +80,14 @@ export class SendExternalSignatoryBookActionComponent implements OnInit {
this.checkExternalSignatureBook();
}
onSubmit() {
this.loading = true;
if (this.data.resIds.length > 0) {
this.executeAction();
async onSubmit() {
if (this.hasEmptyOtpSignaturePosition()) {
this.notify.error(this.translate.instant('lang.mustSign'));
} else {
this.loading = true;
if (this.data.resIds.length > 0) {
this.executeAction();
}
}
}
......@@ -176,4 +180,33 @@ export class SendExternalSignatoryBookActionComponent implements OnInit {
this.resourcesToSign.splice(index, 1);
}
}
hasEmptyOtpSignaturePosition() {
if (this.signatoryBookEnabled == 'maarchParapheur') {
const externalUsers: any[] = this.maarchParapheur.appExternalVisaWorkflow.visaWorkflow.items.filter((user: any) => user.item_id === null && user.role === 'sign');
if (externalUsers.length > 0) {
let resToSign: any[] = this.maarchParapheur.resourcesToSign.filter((res: any) => res.hasOwnProperty('signaturePositions'));
let mustSign: boolean = false;
if (this.maarchParapheur.resourcesToSign.length > 1 && this.maarchParapheur.resourcesToSign.length - resToSign.length >= 1) {
return true;
} else if (resToSign.length === 0) {
return true;
} else {
resToSign = resToSign.map((res: any) => res.signaturePositions);
externalUsers.forEach((element: any) => {
for (let i = 0; i < resToSign.length; i++) {
const userIndex: number = this.maarchParapheur.appExternalVisaWorkflow.visaWorkflow.items.indexOf(element);
if (resToSign[i].filter((item: any) => item.sequence === userIndex).length === 0) {
mustSign = true;
break;
}
}
});
return mustSign;
}
}
} else {
return false;
}
}
}
......@@ -262,7 +262,7 @@ export class ContactsFormComponent implements OnInit {
type: 'string',
control: new FormControl({value: '', disabled: true}),
required: false,
display: true,
display: false,
filling: false,
values: []
},
......@@ -814,12 +814,19 @@ export class ContactsFormComponent implements OnInit {
this.checkFilling();
this.http.get('../rest/contacts/sector', {params: {'addressNumber': contact['addressNumber'], 'addressStreet': contact['addressStreet'], 'addressPostcode': contact['addressPostcode'], 'addressTown': contact['addressTown']}}).pipe(
tap((data: any) => {
const sectorIndex = this.contactForm.findIndex(element => element.id === 'sector');
if (data.sector !== null) {
const sectorIndex = this.contactForm.findIndex(element => element.id === 'sector');
this.contactForm[sectorIndex].control.setValue(data.sector.label);
this.contactForm[sectorIndex].display = true;
} else {
this.contactForm[sectorIndex].control.setValue('');
this.contactForm[sectorIndex].display = false;
}
}),
catchError((err: any) => {
this.notify.handleErrors(err);
return of(false);
})
).subscribe();
this.addressBANMode = disableBan ? false : true;
......
......@@ -326,6 +326,17 @@
</mat-form-field>
</div>
</div>
<div class="form-group" *ngIf="externalSignatoryBook == 'fastParapheur'">
<div class="col-sm-12">
<mat-form-field>
<input matInput [(ngModel)]="currentEntity.fastParapheurSubscriberId"
name="fastParapheurSubscriberId" id="fastParapheurSubscriberId"
title="{{'lang.fastParapheurSubscriberId' | translate}}" type="text"
placeholder="{{'lang.fastParapheurSubscriberId' | translate}}" maxlength="255"
pattern="^[\w-]*$">
</mat-form-field>
</div>
</div>
</mat-expansion-panel>
</mat-accordion>
<div class="form-group">
......
......@@ -72,6 +72,7 @@ export class EntitiesAdministrationComponent implements OnInit {
addressBANFilteredResult: Observable<string[]>;
addressBANCurrentDepartment: string = '75';
departmentList: any[] = [];
externalSignatoryBook: string;
constructor(
public translate: TranslateService,
......@@ -114,6 +115,21 @@ export class EntitiesAdministrationComponent implements OnInit {
this.initEntitiesTree();
this.initBanSearch();
this.initAutocompleteAddressBan();
this.initSignatoryBook();
}
initSignatoryBook() {
this.http.get('../rest/externalConnectionsEnabled').pipe(
tap((data: any) => {
Object.keys(data.connection).filter(connectionId => connectionId !== 'maileva').forEach(connectionId => {
this.externalSignatoryBook = connectionId;
});
}),
catchError((err: any) => {
this.notify.handleSoftErrors(err);
return of(false);
})
).subscribe();
}
initEntitiesTree() {
......
......@@ -110,6 +110,7 @@ export class ParametersCustomizationComponent implements OnInit, OnDestroy {
});
},
base_url: '../node_modules/tinymce/',
convert_urls: false,
height: '200',
suffix: '.min',
language: this.translate.instant('lang.langISO').replace('-', '_'),
......
......@@ -209,6 +209,7 @@ export class TemplateAdministrationComponent implements OnInit, OnDestroy {
tinymce.suffix = '.min';
tinymce.init({
selector: selectorId,
convert_urls: false,
statusbar: false,
language: this.translate.instant('lang.langISO').replace('-', '_'),
language_url: `../node_modules/tinymce-i18n/langs/${this.translate.instant('lang.langISO').replace('-', '_')}.js`,
......
......@@ -59,7 +59,7 @@
</a>
</div>
<div *ngIf="showLogout()" style="text-align:right;flex:2;">
<a (click)="authService.logout()" style="cursor:pointer; color: white;padding-right: 10px;">
<a (click)="authService.logout(true, false, true)" style="cursor:pointer; color: white;padding-right: 10px;">
<span class="badge action-header-profile">{{'lang.logout' | translate}}</span>
</a>
</div>
......
......@@ -78,7 +78,7 @@ export class HeaderRightComponent implements OnInit {
}
showLogout() {
return ['sso'].indexOf(this.authService.authMode) > -1 && this.functions.empty(this.authService.authUri) ? false : true;
return ['sso', 'azure_saml'].indexOf(this.authService.authMode) > -1 && this.functions.empty(this.authService.authUri) ? false : true;
}
goTo() {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment