2015-09-03 23:12:46 +02:00
|
|
|
|
BlazeLayout.setRoot('body');
|
2015-08-22 22:59:03 +02:00
|
|
|
|
|
2019-06-28 12:52:09 -05:00
|
|
|
|
const i18nTagToT9n = i18nTag => {
|
2015-12-07 12:36:51 +08:00
|
|
|
|
// t9n/i18n tags are same now, see: https://github.com/softwarerero/meteor-accounts-t9n/pull/129
|
|
|
|
|
// but we keep this conversion function here, to be aware that that they are different system.
|
|
|
|
|
return i18nTag;
|
|
|
|
|
};
|
|
|
|
|
|
2021-10-12 17:41:12 +02:00
|
|
|
|
let alreadyCheck = 1;
|
|
|
|
|
let isCheckDone = false;
|
|
|
|
|
|
2018-12-24 18:18:41 +02:00
|
|
|
|
const validator = {
|
|
|
|
|
set(obj, prop, value) {
|
|
|
|
|
if (prop === 'state' && value !== 'signIn') {
|
|
|
|
|
$('.at-form-authentication').hide();
|
|
|
|
|
} else if (prop === 'state' && value === 'signIn') {
|
|
|
|
|
$('.at-form-authentication').show();
|
|
|
|
|
}
|
|
|
|
|
// The default behavior to store the value
|
|
|
|
|
obj[prop] = value;
|
|
|
|
|
// Indicate success
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
};
|
2021-06-01 16:44:23 +03:00
|
|
|
|
|
2021-10-18 16:58:54 +02:00
|
|
|
|
// let isSettingDatabaseFctCallDone = false;
|
2021-10-18 15:26:01 +02:00
|
|
|
|
|
2019-02-01 19:00:44 +01:00
|
|
|
|
Template.userFormsLayout.onCreated(function() {
|
2019-06-28 12:52:09 -05:00
|
|
|
|
const templateInstance = this;
|
|
|
|
|
templateInstance.currentSetting = new ReactiveVar();
|
|
|
|
|
templateInstance.isLoading = new ReactiveVar(false);
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
|
|
|
|
Meteor.subscribe('setting', {
|
|
|
|
|
onReady() {
|
2019-06-28 12:52:09 -05:00
|
|
|
|
templateInstance.currentSetting.set(Settings.findOne());
|
2021-09-15 18:35:09 +02:00
|
|
|
|
let currSetting = templateInstance.currentSetting.curValue;
|
|
|
|
|
let oidcBtnElt = $("#at-oidc");
|
|
|
|
|
if(currSetting && currSetting !== undefined && currSetting.oidcBtnText !== undefined && oidcBtnElt != null && oidcBtnElt != undefined){
|
|
|
|
|
let htmlvalue = "<i class='fa fa-oidc'></i>" + currSetting.oidcBtnText;
|
|
|
|
|
oidcBtnElt.html(htmlvalue);
|
|
|
|
|
}
|
2021-10-18 15:26:01 +02:00
|
|
|
|
|
2021-10-18 16:58:54 +02:00
|
|
|
|
// isSettingDatabaseFctCallDone = true;
|
|
|
|
|
if(currSetting && currSetting !== undefined && currSetting.customLoginLogoImageUrl !== undefined)
|
|
|
|
|
document.getElementById("isSettingDatabaseCallDone").style.display = 'none';
|
|
|
|
|
else
|
|
|
|
|
document.getElementById("isSettingDatabaseCallDone").style.display = 'block';
|
2019-02-01 19:00:44 +01:00
|
|
|
|
return this.stop();
|
2019-02-01 21:26:04 +02:00
|
|
|
|
},
|
2019-02-01 19:00:44 +01:00
|
|
|
|
});
|
2020-04-14 08:48:51 +02:00
|
|
|
|
Meteor.call('isPasswordLoginDisabled', (_, result) => {
|
2020-04-13 21:06:27 +02:00
|
|
|
|
if (result) {
|
|
|
|
|
$('.at-pwd-form').hide();
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-11-20 02:38:00 +02:00
|
|
|
|
});
|
|
|
|
|
|
2015-09-03 23:12:46 +02:00
|
|
|
|
Template.userFormsLayout.onRendered(() => {
|
2019-06-28 12:52:09 -05:00
|
|
|
|
AccountsTemplates.state.form.keys = new Proxy(
|
|
|
|
|
AccountsTemplates.state.form.keys,
|
|
|
|
|
validator,
|
|
|
|
|
);
|
2018-12-24 18:18:41 +02:00
|
|
|
|
|
2015-12-07 12:36:51 +08:00
|
|
|
|
const i18nTag = navigator.language;
|
|
|
|
|
if (i18nTag) {
|
|
|
|
|
T9n.setLanguage(i18nTagToT9n(i18nTag));
|
|
|
|
|
}
|
2015-09-03 23:12:46 +02:00
|
|
|
|
EscapeActions.executeAll();
|
|
|
|
|
});
|
2015-08-25 23:39:00 +02:00
|
|
|
|
|
2015-12-07 12:36:51 +08:00
|
|
|
|
Template.userFormsLayout.helpers({
|
2018-11-20 02:38:00 +02:00
|
|
|
|
currentSetting() {
|
2019-02-01 19:00:44 +01:00
|
|
|
|
return Template.instance().currentSetting.get();
|
2018-11-20 02:38:00 +02:00
|
|
|
|
},
|
2021-06-01 16:44:23 +03:00
|
|
|
|
|
2021-10-18 16:58:54 +02:00
|
|
|
|
// isSettingDatabaseCallDone(){
|
|
|
|
|
// return isSettingDatabaseFctCallDone;
|
|
|
|
|
// },
|
2021-10-18 15:26:01 +02:00
|
|
|
|
|
2021-11-25 19:30:49 +01:00
|
|
|
|
isLegalNoticeLinkExist(){
|
|
|
|
|
const currSet = Template.instance().currentSetting.get();
|
|
|
|
|
if(currSet && currSet !== undefined && currSet != null){
|
|
|
|
|
return currSet.legalNotice !== undefined && currSet.legalNotice.trim() != "";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
|
2021-12-06 15:03:42 +01:00
|
|
|
|
getLegalNoticeWithWritTraduction(){
|
|
|
|
|
let spanLegalNoticeElt = $("#legalNoticeSpan");
|
|
|
|
|
if(spanLegalNoticeElt != null && spanLegalNoticeElt != undefined){
|
|
|
|
|
spanLegalNoticeElt.html(TAPi18n.__('acceptance_of_our_legalNotice', {}, T9n.getLanguage() || 'en'));
|
|
|
|
|
}
|
|
|
|
|
let atLinkLegalNoticeElt = $("#legalNoticeAtLink");
|
|
|
|
|
if(atLinkLegalNoticeElt != null && atLinkLegalNoticeElt != undefined){
|
|
|
|
|
atLinkLegalNoticeElt.html(TAPi18n.__('legalNotice', {}, T9n.getLanguage() || 'en'));
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
2019-04-24 12:28:11 +02:00
|
|
|
|
isLoading() {
|
|
|
|
|
return Template.instance().isLoading.get();
|
|
|
|
|
},
|
|
|
|
|
|
2018-12-16 23:46:04 +02:00
|
|
|
|
afterBodyStart() {
|
|
|
|
|
return currentSetting.customHTMLafterBodyStart;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
beforeBodyEnd() {
|
|
|
|
|
return currentSetting.customHTMLbeforeBodyEnd;
|
|
|
|
|
},
|
|
|
|
|
|
2015-12-07 12:36:51 +08:00
|
|
|
|
languages() {
|
2017-06-18 17:21:46 +01:00
|
|
|
|
return _.map(TAPi18n.getLanguages(), (lang, code) => {
|
2018-02-26 03:17:56 +01:00
|
|
|
|
const tag = code;
|
|
|
|
|
let name = lang.name;
|
|
|
|
|
if (lang.name === 'br') {
|
|
|
|
|
name = 'Brezhoneg';
|
2021-01-19 16:03:40 +02:00
|
|
|
|
} else if (lang.name === 'ar-EG') {
|
|
|
|
|
// ar-EG = Arabic (Egypt), simply Masri (مَصرى, [ˈmɑsˤɾi], Egyptian, Masr refers to Cairo)
|
|
|
|
|
name = 'مَصرى';
|
2021-04-16 21:47:39 +03:00
|
|
|
|
} else if (lang.name === 'de-CH') {
|
2021-04-27 19:45:18 +03:00
|
|
|
|
name = 'Deutsch (Schweiz)';
|
2021-11-15 18:17:27 +02:00
|
|
|
|
} else if (lang.name === 'de-AT') {
|
|
|
|
|
name = 'Deutsch (Österreich)';
|
|
|
|
|
} else if (lang.name === 'en-DE') {
|
|
|
|
|
name = 'English (Germany)';
|
2022-01-13 21:05:59 +02:00
|
|
|
|
} else if (lang.name === 'et-EE') {
|
|
|
|
|
name = 'eesti keel (Eesti)';
|
2021-02-24 15:52:11 +02:00
|
|
|
|
} else if (lang.name === 'fa-IR') {
|
|
|
|
|
// fa-IR = Persian (Iran)
|
|
|
|
|
name = 'فارسی/پارسی (ایران)';
|
2020-09-11 00:28:33 +03:00
|
|
|
|
} else if (lang.name === 'fr-BE') {
|
|
|
|
|
name = 'Français (Belgique)';
|
|
|
|
|
} else if (lang.name === 'fr-CA') {
|
|
|
|
|
name = 'Français (Canada)';
|
2021-08-11 23:10:20 +03:00
|
|
|
|
} else if (lang.name === 'fr-CH') {
|
|
|
|
|
name = 'Français (Schweiz)';
|
2021-11-15 18:17:27 +02:00
|
|
|
|
} else if (lang.name === 'gu-IN') {
|
|
|
|
|
// gu-IN = Gurajati (India)
|
|
|
|
|
name = 'ગુજરાતી';
|
2021-11-15 19:20:31 +02:00
|
|
|
|
} else if (lang.name === 'hi-IN') {
|
|
|
|
|
// hi-IN = Hindi (India)
|
|
|
|
|
name = 'हिंदी (भारत)';
|
2018-02-26 03:17:56 +01:00
|
|
|
|
} else if (lang.name === 'ig') {
|
|
|
|
|
name = 'Igbo';
|
2020-09-11 00:28:33 +03:00
|
|
|
|
} else if (lang.name === 'lv') {
|
|
|
|
|
name = 'Latviešu';
|
|
|
|
|
} else if (lang.name === 'latviešu valoda') {
|
|
|
|
|
name = 'Latviešu';
|
2021-11-15 19:20:31 +02:00
|
|
|
|
} else if (lang.name === 'ms-MY') {
|
|
|
|
|
// ms-MY = Malay (Malaysia)
|
|
|
|
|
name = 'بهاس ملايو';
|
2021-07-24 19:57:07 +03:00
|
|
|
|
} else if (lang.name === 'en-IT') {
|
|
|
|
|
name = 'English (Italy)';
|
2021-11-15 18:17:27 +02:00
|
|
|
|
} else if (lang.name === 'el-GR') {
|
|
|
|
|
// el-GR = Greek (Greece)
|
|
|
|
|
name = 'Ελληνικά (Ελλάδα)';
|
2021-02-24 15:52:11 +02:00
|
|
|
|
} else if (lang.name === 'Español') {
|
|
|
|
|
name = 'español';
|
2021-03-25 12:18:33 +02:00
|
|
|
|
} else if (lang.name === 'es_419') {
|
|
|
|
|
name = 'español de América Latina';
|
|
|
|
|
} else if (lang.name === 'es-419') {
|
|
|
|
|
name = 'español de América Latina';
|
|
|
|
|
} else if (lang.name === 'Español de América Latina') {
|
|
|
|
|
name = 'español de América Latina';
|
|
|
|
|
} else if (lang.name === 'es-LA') {
|
|
|
|
|
name = 'español de América Latina';
|
2021-02-24 15:52:11 +02:00
|
|
|
|
} else if (lang.name === 'Español de Argentina') {
|
|
|
|
|
name = 'español de Argentina';
|
|
|
|
|
} else if (lang.name === 'Español de Chile') {
|
|
|
|
|
name = 'español de Chile';
|
|
|
|
|
} else if (lang.name === 'Español de Colombia') {
|
|
|
|
|
name = 'español de Colombia';
|
|
|
|
|
} else if (lang.name === 'Español de México') {
|
|
|
|
|
name = 'español de México';
|
|
|
|
|
} else if (lang.name === 'es-PY') {
|
2021-03-25 12:24:49 +02:00
|
|
|
|
name = 'español de Paraguayo';
|
|
|
|
|
} else if (lang.name === 'Español de Paraguayo') {
|
|
|
|
|
name = 'español de Paraguayo';
|
2021-02-24 15:52:11 +02:00
|
|
|
|
} else if (lang.name === 'Español de Perú') {
|
|
|
|
|
name = 'español de Perú';
|
|
|
|
|
} else if (lang.name === 'Español de Puerto Rico') {
|
|
|
|
|
name = 'español de Puerto Rico';
|
2019-05-11 00:02:40 +03:00
|
|
|
|
} else if (lang.name === 'oc') {
|
|
|
|
|
name = 'Occitan';
|
2022-01-13 21:05:59 +02:00
|
|
|
|
} else if (lang.name === 'ru-UA') {
|
|
|
|
|
name = 'Русский (Украина)';
|
2020-09-11 00:28:33 +03:00
|
|
|
|
} else if (lang.name === 'st') {
|
|
|
|
|
name = 'Sãotomense';
|
2022-01-13 21:05:59 +02:00
|
|
|
|
} else if (lang.name === 'uk-UA') {
|
|
|
|
|
name = 'українська (Україна)';
|
2020-04-07 09:43:23 +00:00
|
|
|
|
} else if (lang.name === '繁体中文(台湾)') {
|
2021-11-15 18:17:27 +02:00
|
|
|
|
// Traditional Chinese (Taiwan)
|
2020-04-06 18:16:17 +08:00
|
|
|
|
name = '繁體中文(台灣)';
|
2018-02-26 03:17:56 +01:00
|
|
|
|
}
|
|
|
|
|
return { tag, name };
|
2017-06-18 17:21:46 +01:00
|
|
|
|
}).sort(function(a, b) {
|
|
|
|
|
if (a.name === b.name) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return a.name > b.name ? 1 : -1;
|
|
|
|
|
}
|
2015-12-07 12:36:51 +08:00
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
isCurrentLanguage() {
|
|
|
|
|
const t9nTag = i18nTagToT9n(this.tag);
|
|
|
|
|
const curLang = T9n.getLanguage() || 'en';
|
|
|
|
|
return t9nTag === curLang;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Template.userFormsLayout.events({
|
2019-06-28 12:52:09 -05:00
|
|
|
|
'change .js-userform-set-language'(event) {
|
|
|
|
|
const i18nTag = $(event.currentTarget).val();
|
2015-12-07 12:36:51 +08:00
|
|
|
|
T9n.setLanguage(i18nTagToT9n(i18nTag));
|
2019-06-28 12:52:09 -05:00
|
|
|
|
event.preventDefault();
|
2015-12-07 12:36:51 +08:00
|
|
|
|
},
|
2019-06-28 12:52:09 -05:00
|
|
|
|
'click #at-btn'(event, templateInstance) {
|
2019-02-01 19:00:44 +01:00
|
|
|
|
if (FlowRouter.getRouteName() === 'atSignIn') {
|
2019-06-28 12:52:09 -05:00
|
|
|
|
templateInstance.isLoading.set(true);
|
|
|
|
|
authentication(event, templateInstance).then(() => {
|
|
|
|
|
templateInstance.isLoading.set(false);
|
|
|
|
|
});
|
2018-12-24 18:18:41 +02:00
|
|
|
|
}
|
2021-11-11 19:25:19 +02:00
|
|
|
|
isCheckDone = false;
|
2021-11-10 16:48:47 +01:00
|
|
|
|
},
|
|
|
|
|
'click #at-signUp'(event, templateInstance){
|
2021-11-11 19:25:19 +02:00
|
|
|
|
isCheckDone = false;
|
2018-10-03 11:50:52 +03:00
|
|
|
|
},
|
2021-10-12 17:41:12 +02:00
|
|
|
|
'DOMSubtreeModified #at-oidc'(event){
|
|
|
|
|
if(alreadyCheck <= 2){
|
|
|
|
|
let currSetting = Settings.findOne();
|
|
|
|
|
let oidcBtnElt = $("#at-oidc");
|
|
|
|
|
if(currSetting && currSetting !== undefined && currSetting.oidcBtnText !== undefined && oidcBtnElt != null && oidcBtnElt != undefined){
|
|
|
|
|
let htmlvalue = "<i class='fa fa-oidc'></i>" + currSetting.oidcBtnText;
|
|
|
|
|
if(alreadyCheck == 1){
|
|
|
|
|
alreadyCheck++;
|
|
|
|
|
oidcBtnElt.html("");
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
alreadyCheck++;
|
|
|
|
|
oidcBtnElt.html(htmlvalue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
alreadyCheck = 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
'DOMSubtreeModified .at-form'(event){
|
|
|
|
|
if(alreadyCheck <= 2 && !isCheckDone){
|
|
|
|
|
if(document.getElementById("at-oidc") != null){
|
|
|
|
|
let currSetting = Settings.findOne();
|
|
|
|
|
let oidcBtnElt = $("#at-oidc");
|
|
|
|
|
if(currSetting && currSetting !== undefined && currSetting.oidcBtnText !== undefined && oidcBtnElt != null && oidcBtnElt != undefined){
|
|
|
|
|
let htmlvalue = "<i class='fa fa-oidc'></i>" + currSetting.oidcBtnText;
|
|
|
|
|
if(alreadyCheck == 1){
|
|
|
|
|
alreadyCheck++;
|
|
|
|
|
oidcBtnElt.html("");
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
alreadyCheck++;
|
|
|
|
|
isCheckDone = true;
|
|
|
|
|
oidcBtnElt.html(htmlvalue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
alreadyCheck = 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
2015-12-07 12:36:51 +08:00
|
|
|
|
});
|
|
|
|
|
|
2015-08-25 23:39:00 +02:00
|
|
|
|
Template.defaultLayout.events({
|
|
|
|
|
'click .js-close-modal': () => {
|
2015-09-03 23:12:46 +02:00
|
|
|
|
Modal.close();
|
|
|
|
|
},
|
|
|
|
|
});
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
2019-06-28 12:52:09 -05:00
|
|
|
|
async function authentication(event, templateInstance) {
|
2019-03-15 10:59:54 +02:00
|
|
|
|
const match = $('#at-field-username_and_email').val();
|
|
|
|
|
const password = $('#at-field-password').val();
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
2019-04-24 12:35:00 +02:00
|
|
|
|
if (!match || !password) return undefined;
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
2019-06-28 12:52:09 -05:00
|
|
|
|
const result = await getAuthenticationMethod(
|
|
|
|
|
templateInstance.currentSetting.get(),
|
|
|
|
|
match,
|
|
|
|
|
);
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
2019-04-24 12:35:00 +02:00
|
|
|
|
if (result === 'password') return undefined;
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
2019-03-15 10:59:54 +02:00
|
|
|
|
// Stop submit #at-pwd-form
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
event.stopImmediatePropagation();
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
2019-02-07 11:38:04 +01:00
|
|
|
|
switch (result) {
|
2019-06-28 12:52:09 -05:00
|
|
|
|
case 'ldap':
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
Meteor.loginWithLDAP(match, password, function() {
|
|
|
|
|
resolve(FlowRouter.go('/'));
|
|
|
|
|
});
|
2019-04-24 12:28:11 +02:00
|
|
|
|
});
|
2019-02-01 19:00:44 +01:00
|
|
|
|
|
2020-09-14 19:57:50 +03:00
|
|
|
|
case 'saml':
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
const provider = Meteor.settings.public.SAML_PROVIDER;
|
|
|
|
|
Meteor.loginWithSaml(
|
|
|
|
|
{
|
|
|
|
|
provider,
|
|
|
|
|
},
|
|
|
|
|
function() {
|
|
|
|
|
resolve(FlowRouter.go('/'));
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2019-06-28 12:52:09 -05:00
|
|
|
|
case 'cas':
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
Meteor.loginWithCas(match, password, function() {
|
|
|
|
|
resolve(FlowRouter.go('/'));
|
|
|
|
|
});
|
2019-04-24 12:28:11 +02:00
|
|
|
|
});
|
2019-02-07 11:38:04 +01:00
|
|
|
|
|
2019-06-28 12:52:09 -05:00
|
|
|
|
default:
|
|
|
|
|
return undefined;
|
2019-02-01 19:00:44 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-28 12:52:09 -05:00
|
|
|
|
function getAuthenticationMethod(
|
|
|
|
|
{ displayAuthenticationMethod, defaultAuthenticationMethod },
|
|
|
|
|
match,
|
|
|
|
|
) {
|
2019-02-01 19:00:44 +01:00
|
|
|
|
if (displayAuthenticationMethod) {
|
|
|
|
|
return $('.select-authentication').val();
|
|
|
|
|
}
|
|
|
|
|
return getUserAuthenticationMethod(defaultAuthenticationMethod, match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getUserAuthenticationMethod(defaultAuthenticationMethod, match) {
|
2019-06-28 12:52:09 -05:00
|
|
|
|
return new Promise(resolve => {
|
2019-02-01 19:00:44 +01:00
|
|
|
|
try {
|
|
|
|
|
Meteor.subscribe('user-authenticationMethod', match, {
|
|
|
|
|
onReady() {
|
|
|
|
|
const user = Users.findOne();
|
2019-02-01 21:26:04 +02:00
|
|
|
|
|
2019-02-01 19:00:44 +01:00
|
|
|
|
const authenticationMethod = user
|
|
|
|
|
? user.authenticationMethod
|
|
|
|
|
: defaultAuthenticationMethod;
|
2019-02-01 21:26:04 +02:00
|
|
|
|
|
2019-02-01 19:00:44 +01:00
|
|
|
|
resolve(authenticationMethod);
|
|
|
|
|
},
|
|
|
|
|
});
|
2019-06-28 12:52:09 -05:00
|
|
|
|
} catch (error) {
|
2019-02-01 19:00:44 +01:00
|
|
|
|
resolve(defaultAuthenticationMethod);
|
|
|
|
|
}
|
2019-02-01 21:26:04 +02:00
|
|
|
|
});
|
2019-02-01 19:00:44 +01:00
|
|
|
|
}
|