First commit waiting for Budget Alert

This commit is contained in:
2025-09-04 13:37:35 +01:00
commit 2d681f27f5
4563 changed files with 1061534 additions and 0 deletions

View File

@ -0,0 +1,44 @@
apply plugin: "com.axelor.app-module"
apply from: "../version.gradle"
apply {
version = openSuiteVersion
}
axelor {
title "Axelor Admin"
description "Axelor Admin Module"
}
dependencies {
compile project(":modules:axelor-exception")
compile project(":modules:axelor-tool")
}
def updateJsp(jspFile, check, lines) {
def jsp = file("${rootProject.buildDir}/webapp/${jspFile}")
def text = lines.join("\n");
if (jsp.exists()) {
def jspText = jsp.getText('UTF-8')
if (jspText.indexOf(check) == -1) {
text = jspText + text
}
}
file(jsp).write(text, 'UTF-8')
}
//XXX: this task should be removed when we introduce per module webapp support in axelor open platform
task copyWebapp(type: Copy) {
destinationDir = file(rootProject.buildDir)
into("webapp/admin") {
from "src/main/webapp"
}
doLast {
// update index-head.jsp
updateJsp("index-head.jsp", "diagram-js.css", [
'<link href="admin/css/custom-width.css" rel="stylesheet">'])
}
}
rootProject.tasks.war.dependsOn copyWebapp

View File

@ -0,0 +1,52 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.db.repo;
import com.axelor.apps.base.db.ObjectDataConfigExport;
import com.axelor.db.JpaRepository;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.exception.service.TraceBackService;
public class ObjectDataConfigExportManagementRepository extends ObjectDataConfigExportRepository {
@Override
public ObjectDataConfigExport save(ObjectDataConfigExport entity) {
try {
Class<? extends Model> klass =
(Class<? extends Model>) Class.forName(entity.getModelSelect());
JpaRepository<? extends Model> repo = JpaRepository.of(klass);
Object obj = repo.all().filter("self.id = ?", entity.getModelSelectId()).fetchOne();
if (obj != null) {
Mapper mapper = Mapper.of(obj.getClass());
if (mapper.getNameField() != null && mapper.getNameField().get(obj) != null) {
entity.setRecordName(mapper.getNameField().get(obj).toString());
} else {
entity.setRecordName(mapper.get(obj, "id").toString());
}
}
} catch (ClassNotFoundException e) {
TraceBackService.trace(e);
}
return super.save(entity);
}
}

View File

@ -0,0 +1,44 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.exceptions;
public interface IExceptionMessages {
public static final String NO_CONFIG_REQUIRED = /*$$(*/ "No configuration required" /*)*/;
public static final String APP_IN_USE = /*$$(*/
"This app is used by %s. Please deactivate them before continue." /*)*/;
public static final String BULK_INSTALL_SUCCESS = /*$$(*/ "Apps installed successfully" /*)*/;
public static final String REFRESH_APP_SUCCESS = /*$$(*/ "Apps refreshed successfully" /*)*/;
public static final String REFRESH_APP_ERROR = /*$$(*/ "Error in refreshing app" /*)*/;
public static final String NO_LANGUAGE_SELECTED = /*$$(*/
"No application language set. Please set 'application.locale' property." /*)*/;
public static final String DEMO_DATA_SUCCESS = /*$$(*/ "Demo data loaded successfully" /*)*/;
public static final String ACCESS_CONFIG_IMPORTED = /*$$(*/
"Access config imported successfully" /*)*/;
public static final String OBJECT_DATA_REPLACE_MISSING = /*$$(*/ "No record found for: %s" /*)*/;
public static final String ROLE_IMPORT_SUCCESS = /*$$(*/ "Roles imported successfully" /*)*/;
}

View File

@ -0,0 +1,94 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.job;
import com.axelor.db.JPA;
import com.axelor.exception.service.TraceBackService;
import com.google.inject.persist.Transactional;
import com.google.inject.servlet.RequestScoper;
import com.google.inject.servlet.ServletScopes;
import java.lang.invoke.MethodHandles;
import java.util.Collections;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class ThreadedJob implements Job {
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
@Transactional(rollbackOn = {Exception.class})
public void execute(JobExecutionContext context) throws JobExecutionException {
if (isRunning(context)) {
return;
}
String name = context.getJobDetail().getKey().getName();
Thread thread = new Thread(() -> executeInThreadedRequestScope(context));
thread.setUncaughtExceptionHandler(
(t, e) -> {
final Throwable cause =
e instanceof UncheckedJobExecutionException && e.getCause() != null
? e.getCause()
: e;
logger.error(cause.getMessage(), cause);
TraceBackService.trace(cause);
});
long startTime = System.currentTimeMillis();
try {
thread.start();
thread.join();
} catch (InterruptedException e) {
TraceBackService.trace(e);
Thread.currentThread().interrupt();
} finally {
float duration = (System.currentTimeMillis() - startTime) / 1000f;
logger.info("Job {} duration: {} s", name, duration);
JPA.clear();
}
}
public abstract void executeInThread(JobExecutionContext context);
private void executeInThreadedRequestScope(JobExecutionContext context) {
RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap());
try (RequestScoper.CloseableScope ignored = scope.open()) {
executeInThread(context);
}
}
private boolean isRunning(JobExecutionContext context) {
try {
return context
.getScheduler()
.getCurrentlyExecutingJobs()
.stream()
.anyMatch(
j ->
j.getTrigger().equals(context.getTrigger())
&& !j.getFireInstanceId().equals(context.getFireInstanceId()));
} catch (SchedulerException e) {
return false;
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.job;
public class UncheckedJobExecutionException extends RuntimeException {
private static final long serialVersionUID = -3636564667401795286L;
public UncheckedJobExecutionException(Throwable cause) {
super(cause);
}
public UncheckedJobExecutionException(String message) {
super(message);
}
}

View File

@ -0,0 +1,49 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.module;
import com.axelor.app.AxelorModule;
import com.axelor.apps.base.db.repo.ObjectDataConfigExportManagementRepository;
import com.axelor.apps.base.db.repo.ObjectDataConfigExportRepository;
import com.axelor.apps.base.service.ObjectDataAnonymizeService;
import com.axelor.apps.base.service.ObjectDataAnonymizeServiceImpl;
import com.axelor.apps.base.service.ObjectDataExportService;
import com.axelor.apps.base.service.ObjectDataExportServiceImpl;
import com.axelor.apps.base.service.app.AccessConfigImportService;
import com.axelor.apps.base.service.app.AccessConfigImportServiceImpl;
import com.axelor.apps.base.service.app.AccessTemplateService;
import com.axelor.apps.base.service.app.AccessTemplateServiceImpl;
import com.axelor.apps.base.service.app.AppService;
import com.axelor.apps.base.service.app.AppServiceImpl;
import com.axelor.apps.base.service.app.DataBackupService;
import com.axelor.apps.base.service.app.DataBackupServiceImpl;
public class AdminModule extends AxelorModule {
@Override
protected void configure() {
bind(AppService.class).to(AppServiceImpl.class);
bind(ObjectDataExportService.class).to(ObjectDataExportServiceImpl.class);
bind(ObjectDataAnonymizeService.class).to(ObjectDataAnonymizeServiceImpl.class);
bind(AccessTemplateService.class).to(AccessTemplateServiceImpl.class);
bind(AccessConfigImportService.class).to(AccessConfigImportServiceImpl.class);
bind(DataBackupService.class).to(DataBackupServiceImpl.class);
bind(ObjectDataConfigExportRepository.class)
.to(ObjectDataConfigExportManagementRepository.class);
}
}

View File

@ -0,0 +1,26 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.ObjectDataConfig;
import com.axelor.exception.AxelorException;
public interface ObjectDataAnonymizeService {
public void anonymize(ObjectDataConfig objectDataConfig, Long recordId) throws AxelorException;
}

View File

@ -0,0 +1,224 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.DataConfigLine;
import com.axelor.apps.base.db.ObjectDataConfig;
import com.axelor.apps.base.db.repo.DataConfigLineRepository;
import com.axelor.apps.base.exceptions.IExceptionMessages;
import com.axelor.db.JPA;
import com.axelor.db.JpaRepository;
import com.axelor.db.Model;
import com.axelor.db.Query;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.mail.db.repo.MailMessageRepository;
import com.axelor.meta.db.MetaField;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ObjectDataAnonymizeServiceImpl implements ObjectDataAnonymizeService {
@Inject private MailMessageRepository mailMessageRepo;
public void anonymize(ObjectDataConfig objectDataConfig, Long recordId) throws AxelorException {
try {
String rootModel = objectDataConfig.getModelSelect();
for (DataConfigLine line : objectDataConfig.getDataConfigLineList()) {
String path =
line.getTypeSelect() == DataConfigLineRepository.TYPE_PATH
? line.getMetaFieldPath().getName()
: line.getPath();
Class<? extends Model> modelClass =
ObjectDataCommonService.findModelClass(line.getMetaModel());
Query<? extends Model> query =
ObjectDataCommonService.createQuery(recordId, line, modelClass);
List<? extends Model> data = query.fetch();
Mapper mapper = Mapper.of(modelClass);
int reset = line.getResetPathSelect();
if (reset != DataConfigLineRepository.RESET_NONE
&& line.getTypeSelect() == DataConfigLineRepository.TYPE_PATH) {
if (reset == DataConfigLineRepository.RESET_DELETE) {
deleteLink(mapper, path, data);
} else {
replaceLink(mapper, path, data, rootModel, line.getRecordSelectId());
}
}
deleteFields(line.getToDeleteMetaFieldSet(), mapper, data);
}
} catch (Exception e) {
TraceBackService.trace(e);
throw new AxelorException(TraceBackRepository.TYPE_TECHNICAL, e.getMessage());
}
}
@Transactional
public void deleteLink(Mapper mapper, String path, List<? extends Model> data) {
path = path.split("\\.")[0];
Property property = mapper.getProperty(path);
if (property == null || property.isRequired()) {
return;
}
for (Model record : data) {
mapper.set(record, path, null);
JPA.save(record);
}
}
@Transactional(rollbackOn = {Exception.class})
public void replaceLink(
Mapper mapper, String path, List<? extends Model> data, String rootModel, Long recordValue)
throws ClassNotFoundException, AxelorException {
path = path.split("\\.")[0];
Property property = mapper.getProperty(path);
if (property == null) {
return;
}
Class<? extends Model> klass = (Class<? extends Model>) Class.forName(rootModel);
JpaRepository<? extends Model> repo = JpaRepository.of(klass);
Model object = repo.all().filter("self.id = ?1", recordValue).fetchOne();
if (object == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_NO_VALUE,
I18n.get(IExceptionMessages.OBJECT_DATA_REPLACE_MISSING),
recordValue);
}
for (Model record : data) {
mapper.set(record, path, object);
JPA.save(record);
}
}
@Transactional
public void deleteFields(Set<MetaField> fields, Mapper mapper, List<? extends Model> data) {
if (fields.isEmpty()) {
return;
}
Map<String, Object> defaultValues = getDefaultValues(mapper, fields);
for (Model record : data) {
for (String field : defaultValues.keySet()) {
Object object = defaultValues.get(field);
mapper.set(record, field, object);
}
JPA.save(record);
mailMessageRepo
.all()
.filter(
"self.relatedId = ?1 AND self.relatedModel = ?2",
record.getId(),
mapper.getBeanClass().getName())
.delete();
}
}
private Map<String, Object> getDefaultValues(Mapper mapper, Set<MetaField> fields) {
Map<String, Object> defaultValues = new HashMap<>();
LocalDate defaultDate = LocalDate.of(1900, 01, 01);
for (MetaField field : fields) {
String name = field.getName();
Property property = mapper.getProperty(name);
if (!property.isRequired()) {
defaultValues.put(name, null);
continue;
}
if (property.getTarget() != null) {
continue;
}
switch (field.getTypeName()) {
case "String":
{
defaultValues.put(name, "anonymous");
break;
}
case "BigDecimal":
{
defaultValues.put(name, BigDecimal.ZERO);
break;
}
case "Integer":
{
defaultValues.put(name, new Integer(0));
break;
}
case "Boolean":
{
defaultValues.put(name, Boolean.FALSE);
break;
}
case "Long":
{
defaultValues.put(name, new Long(0));
break;
}
case "LocalTime":
{
defaultValues.put(name, LocalTime.MIDNIGHT);
break;
}
case "LocalDateTime":
{
defaultValues.put(name, defaultDate.atStartOfDay());
break;
}
case "ZonedDateTime":
{
defaultValues.put(name, defaultDate.atStartOfDay(ZoneId.systemDefault()));
break;
}
default:
{
break;
}
}
}
return defaultValues;
}
}

View File

@ -0,0 +1,89 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.DataConfigLine;
import com.axelor.apps.base.db.repo.DataConfigLineRepository;
import com.axelor.db.JpaRepository;
import com.axelor.db.Model;
import com.axelor.db.Query;
import com.axelor.db.mapper.Mapper;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaModelRepository;
import java.util.Locale;
import java.util.ResourceBundle;
public class ObjectDataCommonService {
public static ResourceBundle getResourceBundle(String language) {
ResourceBundle bundle;
if (language == null) {
bundle = I18n.getBundle();
} else if (language.equals("fr")) {
bundle = I18n.getBundle(Locale.FRANCE);
} else {
bundle = I18n.getBundle(Locale.ENGLISH);
}
return bundle;
}
public static String getNameColumn(MetaField field) throws ClassNotFoundException {
MetaModel metaModel = Beans.get(MetaModelRepository.class).findByName(field.getTypeName());
return Mapper.of(findModelClass(metaModel)).getNameField().getName();
}
public static Class<? extends Model> findModelClass(MetaModel metaModel)
throws ClassNotFoundException {
Class<?> modelClass = Class.forName(metaModel.getFullName());
return (Class<? extends Model>) modelClass;
}
public static Query<? extends Model> createQuery(
Long recordId, DataConfigLine line, Class<? extends Model> modelClass) {
Query<? extends Model> query = JpaRepository.of(modelClass).all();
String filter =
line.getTypeSelect() == DataConfigLineRepository.TYPE_PATH
? createFilter(line.getMetaFieldPath().getName())
: line.getPath();
query.filter(filter, recordId);
return query;
}
public static String createFilter(String path) {
if (path == null || path.equals("id")) {
path = "id";
} else if (!path.endsWith(".id")) {
path = path + ".id";
}
return "self." + path + " = ?1";
}
}

View File

@ -0,0 +1,30 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.ObjectDataConfig;
import com.axelor.apps.base.db.ObjectDataConfigExport;
import com.axelor.exception.AxelorException;
import com.axelor.meta.db.MetaFile;
public interface ObjectDataExportService {
public MetaFile export(
ObjectDataConfig objectDataConfig, ObjectDataConfigExport objDataConfigExport)
throws AxelorException;
}

View File

@ -0,0 +1,272 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service;
import com.axelor.apps.base.db.DataConfigLine;
import com.axelor.apps.base.db.ObjectDataConfig;
import com.axelor.apps.base.db.ObjectDataConfigExport;
import com.axelor.common.Inflector;
import com.axelor.db.Model;
import com.axelor.db.Query;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.MetaStore;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.schema.views.Selection.Option;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.opencsv.CSVWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ObjectDataExportServiceImpl implements ObjectDataExportService {
private final Logger logger = LoggerFactory.getLogger(ObjectDataExportServiceImpl.class);
private Inflector inflector = Inflector.getInstance();
@Inject private MetaFiles metaFiles;
@Override
public MetaFile export(
ObjectDataConfig objectDataConfig, ObjectDataConfigExport objDataConfigExport)
throws AxelorException {
Long recordId = objDataConfigExport.getModelSelectId();
String language = objDataConfigExport.getLangSelect();
String format = objDataConfigExport.getExportFormatSelect();
try {
logger.debug(
"Exporting data for model: {}, id: {}, language: {}, format: {}",
objectDataConfig.getModelSelect(),
recordId,
language,
format);
Map<String, List<String[]>> data = createData(objectDataConfig, recordId, language);
if (format != null && format.equals("csv")) {
return writeCSV(data);
} else {
return writeExcel(data);
}
} catch (Exception e) {
TraceBackService.trace(e);
throw new AxelorException(TraceBackRepository.TYPE_TECHNICAL, e.getMessage());
}
}
private Map<String, List<String[]>> createData(
ObjectDataConfig objectDataConfig, Long recordId, String language)
throws ClassNotFoundException {
Map<String, List<String[]>> data = new HashMap<>();
for (DataConfigLine line : objectDataConfig.getDataConfigLineList()) {
MetaModel metaModel = line.getMetaModel();
logger.debug("Create data for: {}", metaModel.getName());
Class<? extends Model> modelClass = ObjectDataCommonService.findModelClass(metaModel);
Query<? extends Model> query =
ObjectDataCommonService.createQuery(recordId, line, modelClass);
ResourceBundle bundle = ObjectDataCommonService.getResourceBundle(language);
String[][] fieldsData = createFieldsData(line.getToExportMetaFieldSet(), bundle);
Map<String, String> selectMap = getSelectMap(Mapper.of(modelClass));
List<String[]> dataList = fetchData(fieldsData, query, selectMap, bundle);
data.put(line.getTabName(), dataList);
}
return data;
}
private Map<String, String> getSelectMap(Mapper mapper) {
Map<String, String> selectionMap = new HashMap<>();
for (Property property : mapper.getProperties()) {
String selection = property.getSelection();
if (!Strings.isNullOrEmpty(selection)) {
selectionMap.put(property.getName(), selection);
}
}
return selectionMap;
}
private String[][] createFieldsData(Set<MetaField> metaFields, ResourceBundle bundle)
throws ClassNotFoundException {
List<String> names = new ArrayList<>();
List<String> labels = new ArrayList<>();
for (MetaField field : metaFields) {
String label = field.getLabel();
String name = field.getName();
if (Strings.isNullOrEmpty(label)) {
label = inflector.humanize(name);
}
if (field.getRelationship() != null) {
name = name + "." + ObjectDataCommonService.getNameColumn(field);
}
names.add(name);
String translated = bundle.getString(label);
if (!Strings.isNullOrEmpty(translated)) {
labels.add(translated);
} else {
labels.add(label);
}
}
logger.debug("Fields: {}", names);
logger.debug("Labels: {}", labels);
return new String[][] {
names.toArray(new String[names.size()]), labels.toArray(new String[labels.size()])
};
}
private List<String[]> fetchData(
String[][] fieldsData,
Query<? extends Model> query,
Map<String, String> selectMap,
ResourceBundle bundle) {
List<String[]> dataList = new ArrayList<>();
dataList.add(fieldsData[1]);
List<Map> records = query.select(fieldsData[0]).fetch(0, 0);
for (Map<String, Object> record : records) {
List<String> datas = new ArrayList<>();
for (String field : fieldsData[0]) {
Object object = record.get(field);
if (object == null) {
datas.add("");
continue;
}
if (selectMap.containsKey(field)) {
String selection = selectMap.get(field);
Option option = MetaStore.getSelectionItem(selection, object.toString());
if (option == null) {
datas.add("");
continue;
}
String optionTitle = option.getTitle();
optionTitle = bundle.getString(optionTitle);
if (optionTitle != null) {
datas.add(optionTitle);
} else {
datas.add(option.getTitle());
}
} else {
datas.add(object.toString());
}
}
dataList.add(datas.toArray(new String[fieldsData[0].length]));
}
return dataList;
}
private MetaFile writeCSV(Map<String, List<String[]>> data) throws IOException {
File zipFile = MetaFiles.createTempFile("Data", ".zip").toFile();
ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zipFile));
for (String model : data.keySet()) {
File modelFile = MetaFiles.createTempFile(model, ".csv").toFile();
CSVWriter writer = new CSVWriter(new FileWriter(modelFile), ';');
writer.writeAll(data.get(model));
writer.close();
zout.putNextEntry(new ZipEntry(model + ".csv"));
zout.write(IOUtils.toByteArray(new FileInputStream(modelFile)));
zout.closeEntry();
}
zout.close();
return metaFiles.upload(zipFile);
}
private MetaFile writeExcel(Map<String, List<String[]>> data) throws IOException {
XSSFWorkbook workBook = new XSSFWorkbook();
for (String model : data.keySet()) {
XSSFSheet sheet = workBook.createSheet(model);
int count = 0;
for (String[] record : data.get(model)) {
XSSFRow row = sheet.createRow(count);
int cellCount = 0;
for (String val : record) {
XSSFCell cell = row.createCell(cellCount);
cell.setCellValue(val);
cellCount++;
}
count++;
}
}
File excelFile = MetaFiles.createTempFile("Data", ".xls").toFile();
FileOutputStream out = new FileOutputStream(excelFile);
workBook.write(out);
out.close();
return metaFiles.upload(excelFile);
}
}

View File

@ -0,0 +1,26 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.exception.AxelorException;
import com.axelor.meta.db.MetaFile;
public interface AccessConfigImportService {
public void importAccessConfig(MetaFile metaFile) throws AxelorException;
}

View File

@ -0,0 +1,271 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.apps.base.db.AccessConfig;
import com.axelor.apps.base.db.App;
import com.axelor.apps.base.db.repo.AccessConfigRepository;
import com.axelor.auth.db.Permission;
import com.axelor.auth.db.Role;
import com.axelor.auth.db.repo.PermissionRepository;
import com.axelor.auth.db.repo.RoleRepository;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaMenu;
import com.axelor.meta.db.repo.MetaMenuRepository;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class AccessConfigImportServiceImpl implements AccessConfigImportService {
@Inject private AppService appService;
@Inject private AccessConfigRepository accessConfigRepo;
@Inject private PermissionRepository permissionRepo;
@Inject private RoleRepository roleRepo;
@Inject private MetaMenuRepository metaMenuRepo;
@Override
public void importAccessConfig(MetaFile metaFile) throws AxelorException {
if (metaFile == null) {
return;
}
try {
OPCPackage pkg = OPCPackage.open(MetaFiles.getPath(metaFile).toString());
XSSFWorkbook workBook = new XSSFWorkbook(pkg);
processWorkbook(workBook);
} catch (InvalidFormatException | IOException e) {
TraceBackService.trace(e);
throw new AxelorException(TraceBackRepository.TYPE_TECHNICAL, e.getMessage());
}
}
private void processWorkbook(XSSFWorkbook workBook) {
Iterator<XSSFSheet> sheetIter = workBook.iterator();
while (sheetIter.hasNext()) {
XSSFSheet sheet = sheetIter.next();
String name = sheet.getSheetName();
if (name.endsWith("-menu")) {
importMenuAccess(sheet);
} else {
importObjectAccess(sheet);
}
}
}
private void importObjectAccess(XSSFSheet sheet) {
App app = appService.getApp(sheet.getSheetName());
if (app == null) {
return;
}
Iterator<Row> rowIter = sheet.iterator();
Map<Integer, AccessConfig> accessMap = null;
while (rowIter.hasNext()) {
if (accessMap == null) {
accessMap = getAccessConfig(rowIter.next(), app);
continue;
}
createObjectRoles(accessMap, rowIter.next());
}
}
@Transactional
public Map<Integer, AccessConfig> getAccessConfig(Row row, App app) {
Map<Integer, AccessConfig> configMap = new HashMap<>();
Iterator<Cell> cellIter = row.iterator();
cellIter.next();
while (cellIter.hasNext()) {
Cell cell = cellIter.next();
String name = cell.getStringCellValue();
if (name != null) {
AccessConfig config =
accessConfigRepo.all().filter("self.name = ?1 and self.app = ?2", name, app).fetchOne();
if (config == null) {
config = new AccessConfig(name);
config.setApp(app);
config = accessConfigRepo.save(config);
}
configMap.put(cell.getColumnIndex(), config);
}
}
return configMap;
}
private void createObjectRoles(Map<Integer, AccessConfig> accessMap, Row row) {
Iterator<Cell> cellIter = row.iterator();
String obj = cellIter.next().getStringCellValue();
while (cellIter.hasNext()) {
Cell cell = cellIter.next();
String value = cell.getStringCellValue();
if (cell == null || Strings.isNullOrEmpty(value) || invalidValue(value)) {
continue;
}
AccessConfig config = accessMap.get(cell.getColumnIndex());
Permission permission = getPermission(obj, value.trim(), config);
addRole(config, permission);
}
}
private boolean invalidValue(String value) {
return !"rwcde".startsWith(value);
}
@Transactional
public Permission getPermission(String model, String value, AccessConfig config) {
String[] objs = model.split("\\.");
String obj = objs[objs.length - 1];
String pkg = objs[objs.length - 3];
String name = "perm." + pkg + "." + obj + "." + value;
Permission permission =
permissionRepo.all().filter("self.name = ?1 and self.object = ?2", name, model).fetchOne();
if (permission == null) {
permission = new Permission(name);
permission.setObject(model);
}
boolean defaultRight = value.equals("all");
permission.setCanCreate(defaultRight);
permission.setCanRead(defaultRight);
permission.setCanWrite(defaultRight);
permission.setCanRemove(defaultRight);
permission.setCanExport(defaultRight);
if (!defaultRight) {
for (char c : value.toCharArray()) {
switch (c) {
case 'r':
{
permission.setCanRead(true);
break;
}
case 'c':
{
permission.setCanCreate(true);
break;
}
case 'w':
{
permission.setCanWrite(true);
break;
}
case 'd':
{
permission.setCanRemove(true);
break;
}
case 'e':
{
permission.setCanExport(true);
break;
}
}
}
}
return permissionRepo.save(permission);
}
@Transactional
public void addRole(AccessConfig config, Permission permission) {
String name = config.getApp().getCode() + "." + config.getName();
Role role = roleRepo.findByName(name);
if (role == null) {
role = new Role(name);
}
role.addPermission(permission);
role = roleRepo.save(role);
config.addRoleSetItem(role);
accessConfigRepo.save(config);
}
private void importMenuAccess(XSSFSheet sheet) {
App app = appService.getApp(sheet.getSheetName().split("-")[0]);
if (app == null) {
return;
}
Iterator<Row> rowIter = sheet.iterator();
Map<Integer, AccessConfig> accessMap = null;
while (rowIter.hasNext()) {
if (accessMap == null) {
accessMap = getAccessConfig(rowIter.next(), app);
continue;
}
createMenuRoles(accessMap, rowIter.next());
}
}
private void createMenuRoles(Map<Integer, AccessConfig> accessMap, Row row) {
Iterator<Cell> cellIter = row.iterator();
String menu = cellIter.next().getStringCellValue().trim();
while (cellIter.hasNext()) {
Cell cell = cellIter.next();
String value = cell.getStringCellValue();
if (cell == null || Strings.isNullOrEmpty(value)) {
continue;
}
AccessConfig config = accessMap.get(cell.getColumnIndex());
addRole(config, menu);
}
}
@Transactional
public void addRole(AccessConfig config, String menu) {
String name = config.getApp().getCode() + "." + config.getName();
Role role = roleRepo.findByName(name);
if (role == null) {
role = new Role(name);
}
MetaMenu metaMenu = metaMenuRepo.findByName(menu);
if (metaMenu != null) {
metaMenu.addRole(role);
metaMenuRepo.save(metaMenu);
}
config.addRoleSetItem(role);
accessConfigRepo.save(config);
}
}

View File

@ -0,0 +1,26 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.exception.AxelorException;
import com.axelor.meta.db.MetaFile;
public interface AccessTemplateService {
public MetaFile generateTemplate() throws AxelorException;
}

View File

@ -0,0 +1,338 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.apps.base.db.App;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaAction;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaJsonRecord;
import com.axelor.meta.db.MetaMenu;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaMenuRepository;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.google.inject.Inject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class AccessTemplateServiceImpl implements AccessTemplateService {
private Map<String, String> menuApp;
private Map<String, String> objMenu;
private List<String> configMenus;
private List<String> appMenus;
private static final String[] objectHeaders = new String[] {"Object", "User", "Manager"};
private static final String[] menuHeaders = new String[] {"Menu", "User", "Manager"};
private String defaultApp = null;
@Inject private MetaMenuRepository metaMenuRepo;
@Inject private AppService appService;
@Inject private MetaFiles metaFiles;
@Inject private MetaModelRepository metaModelRepo;
@Override
public MetaFile generateTemplate() throws AxelorException {
try {
menuApp = new HashMap<>();
objMenu = new HashMap<>();
configMenus = new ArrayList<>();
appMenus = new ArrayList<>();
defaultApp = null;
App app = appService.getApp("base");
if (app == null) {
return null;
}
defaultApp = app.getCode();
getMenusPerApp();
updateNoMenuObjects();
return createExcel();
} catch (Exception e) {
TraceBackService.trace(e);
throw new AxelorException(TraceBackRepository.TYPE_TECHNICAL, e.getMessage());
}
}
private void getMenusPerApp() {
List<MetaMenu> menus = metaMenuRepo.all().filter("self.parent is null").order("id").fetch();
processMenu(menus.iterator());
}
private void processMenu(Iterator<MetaMenu> menuIter) {
if (!menuIter.hasNext()) {
return;
}
MetaMenu menu = menuIter.next();
String app = getApp(menu);
menuApp.put(menu.getName(), app);
MetaAction action = menu.getAction();
if (action != null && action.getType().equals("action-view")) {
String model = action.getModel();
if (validModel(model)) {
String menuName = getObjMenu(menu);
if (menuName != null) {
objMenu.put(model, menuName);
}
}
}
List<MetaMenu> menus = metaMenuRepo.all().filter("self.parent = ?1", menu).order("id").fetch();
processMenu(menus.iterator());
processMenu(menuIter);
}
private boolean validModel(String model) {
if (model == null) {
return false;
}
if (objMenu.containsKey(model)) {
return false;
}
if (model.equals(MetaJsonRecord.class.getName())) {
return false;
}
return true;
}
private String getObjMenu(MetaMenu menu) {
if (appMenus.contains(menu.getName())) {
return menu.getName();
}
if (configMenus.contains(menu.getName())) {
return menu.getName();
}
MetaMenu parent = menu.getParent();
while (parent != null) {
if (configMenus.contains(parent.getName())) {
break;
}
if (appMenus.contains(parent.getName())) {
break;
}
parent = parent.getParent();
}
if (parent != null) {
return parent.getName();
}
return null;
}
private String getApp(MetaMenu menu) {
String appCode = null;
String condition = menu.getConditionToCheck();
if (condition != null) {
String[] cond = condition.split("__config__\\.app\\.isApp\\('");
if (cond.length > 1) {
App app = appService.getApp(cond[1].split("'")[0]);
if (app != null) {
if (condition.trim().equals("__config__.app.isApp('" + app.getCode() + "')")
&& menu.getAction() == null) {
appMenus.add(menu.getName());
}
appCode = app.getCode();
}
}
}
MetaMenu parent = menu.getParent();
if (appCode == null && parent != null && menuApp.containsKey(parent.getName())) {
appCode = menuApp.get(parent.getName());
}
if (parent == null && appCode == null) {
configMenus.add(menu.getName());
}
if (menu.getTitle().equals("Configuration") || menu.getTitle().equals("Configurations")) {
configMenus.add(menu.getName());
appMenus.remove(menu.getName());
}
if (appCode == null) {
return defaultApp;
}
return appCode;
}
private MetaFile createExcel() throws FileNotFoundException, IOException {
XSSFWorkbook workBook = new XSSFWorkbook();
List<String> keys = new ArrayList<>();
keys.addAll(objMenu.keySet());
Collections.sort(keys);
Set<String> menuProcessed = new HashSet<>();
for (String obj : keys) {
String menu = objMenu.get(obj);
String app = menuApp.get(menu);
if (app == null) {
continue;
}
writeObjectSheet(workBook, obj, menu, app);
if (!menuProcessed.contains(menu)) {
writeMenuSheet(workBook, menu, app);
menuProcessed.add(menu);
}
}
List<String> menusRemaining = new ArrayList<>();
menusRemaining.addAll(appMenus);
for (String menu : menusRemaining) {
writeMenuSheet(workBook, menu, menuApp.get(menu));
}
return createMetaFile(workBook);
}
public MetaFile createMetaFile(XSSFWorkbook workBook) throws IOException, FileNotFoundException {
Path path = MetaFiles.createTempFile("AccessConfigTemplate", ".xlsx");
File file = path.toFile();
FileOutputStream fout = new FileOutputStream(file);
workBook.write(fout);
fout.close();
return metaFiles.upload(file);
}
public void writeObjectSheet(XSSFWorkbook workBook, String obj, String menu, String app) {
XSSFSheet sheet = workBook.getSheet(app);
if (sheet == null) {
sheet = workBook.createSheet(app);
writeRow(sheet, objectHeaders);
}
String usersRights = appMenus.contains(menu) ? "rwcde" : "r";
writeRow(sheet, new String[] {obj, usersRights, "rwcde"});
}
private void writeMenuSheet(XSSFWorkbook workBook, String menu, String app) {
XSSFSheet sheet = workBook.getSheet(app + "-menu");
if (sheet == null) {
sheet = workBook.createSheet(app + "-menu");
writeRow(sheet, menuHeaders);
}
String usersRights = configMenus.contains(menu) ? "" : "x";
writeRow(sheet, new String[] {menu, usersRights, "x"});
appMenus.remove(menu);
}
private void writeRow(XSSFSheet sheet, String[] values) {
XSSFRow row = sheet.createRow(sheet.getPhysicalNumberOfRows());
for (int i = 0; i < values.length; i++) {
XSSFCell cell = row.createCell(i);
cell.setCellValue(values[i]);
}
}
private void updateNoMenuObjects() {
List<MetaModel> metaModels =
metaModelRepo.all().filter("self.fullName not in ?1", objMenu.keySet()).fetch();
Iterator<MetaModel> modelIter = metaModels.iterator();
String appMenu = objMenu.get(App.class.getName());
while (modelIter.hasNext()) {
MetaModel model = modelIter.next();
try {
Class klass = Class.forName(model.getFullName());
if (App.class.isAssignableFrom(klass)) {
objMenu.put(model.getFullName(), appMenu);
modelIter.remove();
} else if (addObject(model)) {
modelIter.remove();
} else {
objMenu.put(model.getFullName(), appMenu);
}
} catch (ClassNotFoundException e) {
continue;
}
}
}
private boolean addObject(MetaModel model) {
for (String key : objMenu.keySet()) {
if (model.getFullName().contains(key)) {
objMenu.put(model.getFullName(), objMenu.get(key));
return true;
}
}
String pkgName = model.getPackageName();
for (String key : objMenu.keySet()) {
String objPkg = key.substring(0, key.lastIndexOf("."));
if (pkgName.equals(objPkg)) {
objMenu.put(model.getFullName(), objMenu.get(key));
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,45 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.apps.base.db.App;
import com.axelor.exception.AxelorException;
import java.io.IOException;
import java.util.Collection;
public interface AppService {
public App importDataDemo(App app) throws AxelorException;
public App getApp(String type);
public boolean isApp(String type);
public App installApp(App app, String language) throws AxelorException;
public App unInstallApp(App app) throws AxelorException;
public void refreshApp() throws IOException, ClassNotFoundException;
public void bulkInstall(Collection<App> apps, Boolean importDeomo, String language)
throws AxelorException;
public App importRoles(App app) throws AxelorException;
public void importRoles() throws AxelorException;
}

View File

@ -0,0 +1,568 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.app.AppSettings;
import com.axelor.apps.base.db.App;
import com.axelor.apps.base.db.repo.AppRepository;
import com.axelor.apps.base.exceptions.IExceptionMessages;
import com.axelor.common.FileUtils;
import com.axelor.common.Inflector;
import com.axelor.data.Importer;
import com.axelor.data.csv.CSVConfig;
import com.axelor.data.csv.CSVImporter;
import com.axelor.data.csv.CSVInput;
import com.axelor.data.xml.XMLImporter;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.i18n.I18n;
import com.axelor.meta.MetaScanner;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class AppServiceImpl implements AppService {
private final Logger log = LoggerFactory.getLogger(AppService.class);
private static final String DIR_DEMO = "demo";
private static final String DIR_INIT = "data-init" + File.separator + "app";
private static final String DIR_ROLES = "roles";
private static final String CONFIG_PATTERN = "-config.xml";
private static final String IMG_DIR = "img";
private static final String EXT_DIR = "extra";
private static Pattern patCsv = Pattern.compile("^\\<\\s*csv-inputs");
private static Pattern patXml = Pattern.compile("^\\<\\s*xml-inputs");
private Inflector inflector = Inflector.getInstance();
@Inject private AppRepository appRepo;
@Inject private MetaModelRepository metaModelRepo;
@Override
public App importDataDemo(App app) throws AxelorException {
if (app.getDemoDataLoaded()) {
return app;
}
log.debug("Demo import: App code: {}, App lang: {}", app.getCode(), app.getLanguageSelect());
importParentData(app);
String lang = getLanguage(app);
if (lang == null) {
throw new AxelorException(
TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
I18n.get(IExceptionMessages.NO_LANGUAGE_SELECTED));
}
importData(app, DIR_DEMO, true);
app = appRepo.find(app.getId());
app.setDemoDataLoaded(true);
return saveApp(app);
}
@Transactional
public App saveApp(App app) {
return appRepo.save(app);
}
private void importData(App app, String dataDir, boolean useLang) {
String modules = app.getModules();
if (modules == null) {
return;
}
String code = app.getCode();
String lang = useLang ? getLanguage(app) : "";
log.debug("Data import: DataDir: {}, App code: {}, App lang: {}", dataDir, code, lang);
for (String module : modules.split(",")) {
File tmp = extract(module, dataDir, lang, code);
if (tmp == null) {
continue;
}
log.debug("Importing from module: {}", module);
importPerConfig(code, new File(tmp, dataDir));
}
}
private void importPerConfig(String appCode, File dataDir) {
try {
File[] configs =
dataDir.listFiles(
new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith(appCode + "-") && name.endsWith(CONFIG_PATTERN);
}
});
if (configs.length == 0) {
log.debug("No config file found for the app: {}", appCode);
return;
}
Arrays.sort(configs);
for (File config : configs) {
runImport(config, dataDir);
}
} finally {
clean(dataDir);
}
}
private String getLanguage(App app) {
String lang = app.getLanguageSelect();
if (app.getLanguageSelect() == null) {
lang = AppSettings.get().get("application.locale");
}
return lang;
}
private void importParentData(App app) throws AxelorException {
List<App> depends = getDepends(app, true);
for (App parent : depends) {
parent = appRepo.find(parent.getId());
if (!parent.getDemoDataLoaded()) {
importDataDemo(parent);
}
}
}
private App importDataInit(App app) {
String lang = getLanguage(app);
if (lang == null) {
return app;
}
importData(app, DIR_INIT, true);
app = appRepo.find(app.getId());
app.setInitDataLoaded(true);
return app;
}
private void runImport(File config, File data) {
log.debug(
"Running import with config path: {}, data path: {}",
config.getAbsolutePath(),
data.getAbsolutePath());
try {
Scanner scanner = new Scanner(config);
Importer importer = null;
while (scanner.hasNextLine()) {
String str = scanner.nextLine();
if (patCsv.matcher(str).find()) {
importer = new CSVImporter(config.getAbsolutePath(), data.getAbsolutePath(), null);
break;
}
if (patXml.matcher(str).find()) {
importer = new XMLImporter(config.getAbsolutePath(), data.getAbsolutePath());
break;
}
}
scanner.close();
if (importer != null) {
importer.run();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private File extract(String module, String dirName, String lang, String code) {
String dirNamePattern = dirName.replaceAll("/|\\\\", "(/|\\\\\\\\)");
List<URL> files = new ArrayList<URL>();
files.addAll(MetaScanner.findAll(module, dirNamePattern, code + "(-+.*)?" + CONFIG_PATTERN));
if (files.isEmpty()) {
return null;
}
if (lang.isEmpty()) {
files.addAll(MetaScanner.findAll(module, dirNamePattern, code + "*"));
} else {
String dirPath = dirName + "/";
files.addAll(fetchUrls(module, dirPath + IMG_DIR));
files.addAll(fetchUrls(module, dirPath + EXT_DIR));
files.addAll(fetchUrls(module, dirPath + lang));
}
final File tmp = Files.createTempDir();
final String dir = dirName.replace("\\", "/");
for (URL file : files) {
String name = file.toString();
name = name.substring(name.lastIndexOf(dir));
if (!lang.isEmpty()) {
name = name.replace(dir + "/" + lang, dir);
}
if (File.separatorChar == '\\') {
name = name.replace("/", "\\");
}
try {
copy(file.openStream(), tmp, name);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return tmp;
}
private List<URL> fetchUrls(String module, String fileName) {
final String fileNamePattern = fileName.replaceAll("/|\\\\", "(/|\\\\\\\\)");
return MetaScanner.findAll(module, fileNamePattern, "(.+?)");
}
private void copy(InputStream in, File toDir, String name) throws IOException {
File dst = FileUtils.getFile(toDir, name);
Files.createParentDirs(dst);
FileOutputStream out = new FileOutputStream(dst);
try {
ByteStreams.copy(in, out);
} finally {
out.close();
}
}
private void clean(File file) {
if (file.isDirectory()) {
for (File child : file.listFiles()) {
clean(child);
}
file.delete();
} else if (file.exists()) {
file.delete();
}
}
@Override
public App getApp(String code) {
return appRepo.findByCode(code);
}
@Override
public boolean isApp(String code) {
App app = getApp(code);
if (app == null) {
return false;
}
return app.getActive();
}
private List<App> getDepends(App app, Boolean active) {
List<App> apps = new ArrayList<App>();
app = appRepo.find(app.getId());
for (App depend : app.getDependsOnSet()) {
if (depend.getActive().equals(active)) {
apps.add(depend);
}
}
return sortApps(apps);
}
private List<String> getNames(List<App> apps) {
List<String> names = new ArrayList<String>();
for (App app : apps) {
names.add(app.getName());
}
return names;
}
private List<App> getChildren(App app, Boolean active) {
String code = app.getCode();
String query = "self.dependsOnSet.code = ?1";
if (active != null) {
query = "(" + query + ") AND self.active = " + active;
}
List<App> apps = appRepo.all().filter(query, code).fetch();
log.debug("Parent app: {}, Total children: {}", app.getName(), apps.size());
return apps;
}
@Override
public App installApp(App app, String language) throws AxelorException {
app = appRepo.find(app.getId());
if (app.getActive()) {
return app;
}
if (language != null) {
app.setLanguageSelect(language);
} else {
language = app.getLanguageSelect();
}
List<App> apps = getDepends(app, false);
for (App parentApp : apps) {
installApp(parentApp, language);
}
log.debug("Init data loaded: {}, for app: {}", app.getInitDataLoaded(), app.getCode());
if (!app.getInitDataLoaded()) {
app = importDataInit(app);
}
app = appRepo.find(app.getId());
app.setActive(true);
return saveApp(app);
}
private List<App> sortApps(Collection<App> apps) {
List<App> appsList = new ArrayList<App>();
appsList.addAll(apps);
appsList.sort(
new Comparator<App>() {
@Override
public int compare(App app1, App app2) {
Integer order1 = app1.getInstallOrder();
Integer order2 = app2.getInstallOrder();
if (order1 < order2) {
return -1;
}
if (order1 > order2) {
return 1;
}
return 0;
}
});
log.debug("Apps sorted: {}", getNames(appsList));
return appsList;
}
@Override
public void refreshApp() throws IOException {
File dataDir = Files.createTempDir();
File imgDir = new File(dataDir, "img");
imgDir.mkdir();
CSVConfig csvConfig = new CSVConfig();
csvConfig.setInputs(new ArrayList<CSVInput>());
List<MetaModel> metaModels =
metaModelRepo
.all()
.filter(
"self.name != 'App' and self.name like 'App%' and self.packageName = ?1",
App.class.getPackage().getName())
.fetch();
log.debug("Total app models: {}", metaModels.size());
for (MetaModel metaModel : metaModels) {
Class<?> klass;
try {
klass = Class.forName(metaModel.getFullName());
} catch (ClassNotFoundException e) {
continue;
}
if (!App.class.isAssignableFrom(klass)) {
log.debug("Not a App class : {}", metaModel.getName());
continue;
}
Object obj = null;
Query query = JPA.em().createQuery("SELECT id FROM " + metaModel.getName());
try {
obj = query.setMaxResults(1).getSingleResult();
} catch (Exception ex) {
}
if (obj != null) {
continue;
}
log.debug("App without app record: {}", metaModel.getName());
String csvName = "base_" + inflector.camelize(klass.getSimpleName(), true) + ".csv";
String pngName = inflector.dasherize(klass.getSimpleName()) + ".png";
CSVInput input = new CSVInput();
input.setFileName(csvName);
input.setTypeName(klass.getName());
input.setCallable("com.axelor.csv.script.ImportApp:importApp");
input.setSearch("self.code =:code");
input.setSeparator(';');
csvConfig.getInputs().add(input);
InputStream stream = klass.getResourceAsStream("/data-init/input/" + csvName);
copyStream(stream, new File(dataDir, csvName));
stream = klass.getResourceAsStream("/data-init/input/img/" + pngName);
copyStream(stream, new File(imgDir, pngName));
}
if (!csvConfig.getInputs().isEmpty()) {
CSVImporter importer = new CSVImporter(csvConfig, dataDir.getAbsolutePath());
if (importer != null) {
importer.run();
}
}
}
private void copyStream(InputStream stream, File file) throws IOException {
if (stream != null) {
FileOutputStream out = new FileOutputStream(file);
try {
ByteStreams.copy(stream, out);
out.close();
} finally {
out.close();
}
}
}
@Override
public App unInstallApp(App app) throws AxelorException {
List<App> children = getChildren(app, true);
if (!children.isEmpty()) {
List<String> childrenNames = getNames(children);
throw new AxelorException(
TraceBackRepository.CATEGORY_INCONSISTENCY, IExceptionMessages.APP_IN_USE, childrenNames);
}
app.setActive(false);
return saveApp(app);
}
@Override
public void bulkInstall(Collection<App> apps, Boolean importDemo, String language)
throws AxelorException {
apps = sortApps(apps);
for (App app : apps) {
app = installApp(app, language);
if (importDemo != null && importDemo) {
importDataDemo(app);
}
}
}
@Override
public App importRoles(App app) throws AxelorException {
if (app.getIsRolesImported()) {
return app;
}
importParentRoles(app);
importData(app, DIR_ROLES, false);
app = appRepo.find(app.getId());
app.setIsRolesImported(true);
return saveApp(app);
}
private void importParentRoles(App app) throws AxelorException {
List<App> depends = getDepends(app, true);
for (App parent : depends) {
parent = appRepo.find(parent.getId());
if (!parent.getIsRolesImported()) {
importRoles(parent);
}
}
}
@Override
public void importRoles() throws AxelorException {
List<App> apps = appRepo.all().filter("self.isRolesImported = false").fetch();
apps = sortApps(apps);
for (App app : apps) {
importRoles(app);
}
}
}

View File

@ -0,0 +1,660 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.apps.base.db.App;
import com.axelor.apps.base.db.DataBackup;
import com.axelor.auth.db.AuditableModel;
import com.axelor.common.StringUtils;
import com.axelor.data.csv.CSVBind;
import com.axelor.data.csv.CSVConfig;
import com.axelor.data.csv.CSVInput;
import com.axelor.db.JpaRepository;
import com.axelor.db.Model;
import com.axelor.db.Query;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.exception.service.TraceBackService;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaJsonField;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.opencsv.CSVWriter;
import com.thoughtworks.xstream.XStream;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DataBackupCreateService {
private static final char SEPARATOR = ',';
private static final char QUOTE_CHAR = '"';
private static final char REFERENCE_FIELD_SEPARATOR = '|';
private static final int BUFFER_SIZE = 1000;
@Inject private MetaModelRepository metaModelRepo;
private Logger LOG = LoggerFactory.getLogger(getClass());
private boolean notNullReferenceFlag, referenceFlag;
private boolean byteArrFieldFlag = false;
private static Set<String> exceptColumnNameList =
ImmutableSet.of(
"importOrigin",
"importId",
"updatedBy",
"createdBy",
"updatedOn",
"createdOn",
"archived",
"version",
"attrs");
private static Map<Object, Object> AutoImportModelMap =
ImmutableMap.builder()
.put("com.axelor.apps.base.db.App", "self.code = :code")
.put("com.axelor.auth.db.Role", "self.name = :name")
.put("com.axelor.auth.db.User", "self.code = :code")
.put("com.axelor.auth.db.Permission", "self.name = :name")
.put("com.axelor.auth.db.Group", "self.code = :code")
.put("com.axelor.apps.base.db.Language", "self.code = :code")
.put("com.axelor.apps.base.db.BirtTemplate", "self.name = :name")
.put("com.axelor.apps.base.db.BirtTemplateParameter", "self.name = :name")
.put("com.axelor.apps.crm.db.EventCategory", "self.code = :code")
.put("com.axelor.apps.account.db.AccountChart", "self.code = :code")
.put("com.axelor.apps.bankpayment.db.BankOrderFileFormat", "self.name = :name")
.put("com.axelor.apps.bankpayment.db.BankStatementFileFormat", "self.name = :name")
.build();
List<String> fileNameList;
/* Generate csv Files for each individual MetaModel and single config file */
public File create(DataBackup dataBackup) throws InterruptedException {
File tempDir = Files.createTempDir();
String tempDirectoryPath = tempDir.getAbsolutePath();
fileNameList = new ArrayList<>();
List<MetaModel> metaModelList = getMetaModels();
LinkedList<CSVInput> simpleCsvs = new LinkedList<>();
LinkedList<CSVInput> refernceCsvs = new LinkedList<>();
LinkedList<CSVInput> notNullReferenceCsvs = new LinkedList<>();
Map<String, List<String>> subClassesMap = getSubClassesMap();
for (MetaModel metaModel : metaModelList) {
try {
List<String> subClasses = subClassesMap.get(metaModel.getFullName());
long totalRecord = getMetaModelDataCount(metaModel, subClasses);
if (totalRecord > 0) {
LOG.debug("Exporting Model : " + metaModel.getFullName());
notNullReferenceFlag = false;
referenceFlag = false;
CSVWriter csvWriter =
new CSVWriter(
new FileWriter(new File(tempDirectoryPath, metaModel.getName() + ".csv")),
SEPARATOR,
QUOTE_CHAR);
CSVInput csvInput =
writeCSVData(
metaModel, csvWriter, dataBackup, totalRecord, subClasses, tempDirectoryPath);
csvWriter.close();
if (notNullReferenceFlag) {
notNullReferenceCsvs.add(csvInput);
} else if (referenceFlag) {
refernceCsvs.add(csvInput);
CSVInput temcsv = new CSVInput();
temcsv.setFileName(csvInput.getFileName());
temcsv.setTypeName(csvInput.getTypeName());
if (dataBackup.getIsRelativeDate()) {
temcsv.setBindings(new ArrayList<>());
getCsvInputForDateorDateTime(metaModel, temcsv);
}
if (AutoImportModelMap.containsKey(csvInput.getTypeName())) {
temcsv.setSearch(AutoImportModelMap.get(csvInput.getTypeName()).toString());
}
if (Class.forName(metaModel.getFullName()).getSuperclass() == App.class) {
temcsv.setSearch("self.code = :code");
}
simpleCsvs.add(temcsv);
} else {
simpleCsvs.add(csvInput);
}
fileNameList.add(metaModel.getName() + ".csv");
}
} catch (ClassNotFoundException e) {
} catch (IOException e) {
TraceBackService.trace(e, DataBackupService.class.getName());
}
}
CSVConfig csvConfig = new CSVConfig();
csvConfig.setInputs(simpleCsvs);
csvConfig.getInputs().addAll(notNullReferenceCsvs);
csvConfig.getInputs().addAll(refernceCsvs);
csvConfig.getInputs().addAll(notNullReferenceCsvs);
generateConfig(tempDirectoryPath, csvConfig);
fileNameList.add(DataBackupServiceImpl.CONFIG_FILE_NAME);
File zippedFile = generateZIP(tempDirectoryPath, fileNameList);
return zippedFile;
}
void getCsvInputForDateorDateTime(MetaModel metaModel, CSVInput csvInput) {
try {
Mapper metaModelMapper = Mapper.of(Class.forName(metaModel.getFullName()));
Property[] properties = metaModelMapper.getProperties();
for (Property property : properties) {
String propertyType = property.getType().toString();
if ((propertyType.equals("DATE") || propertyType.equals("DATETIME"))
&& !property.getName().equals("createdOn")
&& !property.getName().equals("updatedOn")) {
getDateOrDateTimeHeader(property, csvInput);
}
}
} catch (ClassNotFoundException e) {
TraceBackService.trace(e);
}
}
/* Get All MetaModels */
private List<MetaModel> getMetaModels() {
String filterStr =
"self.packageName NOT LIKE '%meta%' AND self.packageName !='com.axelor.studio.db' AND self.name!='DataBackup'";
List<MetaModel> metaModels = metaModelRepo.all().filter(filterStr).order("fullName").fetch();
metaModels.add(metaModelRepo.findByName(MetaFile.class.getSimpleName()));
metaModels.add(metaModelRepo.findByName(MetaJsonField.class.getSimpleName()));
return metaModels;
}
private Map<String, List<String>> getSubClassesMap() {
List<MetaModel> metaModels = getMetaModels();
List<String> subClasses;
Map<String, List<String>> subClassMap = new HashMap<String, List<String>>();
for (MetaModel metaModel : metaModels) {
try {
subClasses = new ArrayList<>();
@SuppressWarnings("unchecked")
Class<AuditableModel> superClass =
(Class<AuditableModel>) Class.forName(metaModel.getFullName()).getSuperclass();
if (superClass != AuditableModel.class) {
if (!subClassMap.isEmpty() && subClassMap.containsKey(superClass.getName())) {
subClasses = subClassMap.get(superClass.getName());
}
subClasses.add(metaModel.getName());
subClassMap.put(superClass.getName(), subClasses);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return subClassMap;
}
/* Get All Data of Specific MetaModel */
private List<Model> getMetaModelDataList(
MetaModel metaModel, int start, Integer fetchLimit, List<String> subClasses)
throws ClassNotFoundException {
return getQuery(metaModel, subClasses).fetch(fetchLimit, start);
}
private long getMetaModelDataCount(MetaModel metaModel, List<String> subClasses)
throws InterruptedException, ClassNotFoundException {
Query<Model> query = getQuery(metaModel, subClasses);
long count = 0;
if (query != null) {
count = query.count();
}
return count;
}
private Query<Model> getQuery(MetaModel metaModel, List<String> subClasses)
throws ClassNotFoundException {
String whereStr = "";
if (subClasses != null && subClasses.size() > 0) {
for (String subClassName : subClasses) {
whereStr += whereStr.length() > 0 ? " AND " : "";
whereStr += "id NOT IN (select id from " + subClassName + ")";
}
}
@SuppressWarnings("unchecked")
Class<Model> klass = (Class<Model>) Class.forName(metaModel.getFullName());
JpaRepository<Model> model = null;
Query<Model> query = null;
try {
model = JpaRepository.of(klass);
} catch (Exception e) {
TraceBackService.trace(e, DataBackupService.class.getName());
}
if (model != null) {
query = JpaRepository.of(klass).all();
if (StringUtils.notEmpty(whereStr)) {
query.filter(whereStr);
}
}
return query;
}
private CSVInput writeCSVData(
MetaModel metaModel,
CSVWriter csvWriter,
DataBackup dataBackup,
long totalRecord,
List<String> subClasses,
String dirPath) {
CSVInput csvInput = new CSVInput();
boolean headerFlag = true;
List<String> dataArr = null;
List<String> headerArr = new ArrayList<>();
List<Model> dataList = null;
try {
Mapper metaModelMapper = Mapper.of(Class.forName(metaModel.getFullName()));
Property[] pro = metaModelMapper.getProperties();
Integer fetchLimit = dataBackup.getFetchLimit();
boolean isRelativeDate = dataBackup.getIsRelativeDate();
boolean updateImportId = dataBackup.getUpdateImportId();
csvInput.setFileName(metaModel.getName() + ".csv");
csvInput.setTypeName(metaModel.getFullName());
csvInput.setBindings(new ArrayList<>());
for (int i = 0; i < totalRecord; i = i + fetchLimit) {
dataList = getMetaModelDataList(metaModel, i, fetchLimit, subClasses);
if (dataList != null && dataList.size() > 0) {
for (Object dataObject : dataList) {
dataArr = new ArrayList<>();
for (Property property : pro) {
if (isPropertyExportable(property)) {
if (headerFlag) {
String headerStr =
getMetaModelHeader(dataObject, property, csvInput, isRelativeDate);
headerArr.add(headerStr);
}
dataArr.add(
getMetaModelData(
metaModel.getName(),
metaModelMapper,
property,
dataObject,
dirPath,
isRelativeDate,
updateImportId));
}
}
if (headerFlag) {
if (byteArrFieldFlag) {
csvInput.setCallable(
"com.axelor.apps.base.service.app.DataBackupRestoreService:importObjectWithByteArray");
byteArrFieldFlag = false;
}
csvWriter.writeNext(headerArr.toArray(new String[headerArr.size()]), true);
headerFlag = false;
}
csvWriter.writeNext(dataArr.toArray(new String[dataArr.size()]), true);
}
}
}
if (AutoImportModelMap.containsKey(csvInput.getTypeName())) {
csvInput.setSearch(AutoImportModelMap.get(csvInput.getTypeName()).toString());
} else if (Class.forName(metaModel.getFullName()).getSuperclass() == App.class) {
csvInput.setSearch("self.code = :code");
}
} catch (ClassNotFoundException e) {
}
return csvInput;
}
private boolean isPropertyExportable(Property property) {
if (!exceptColumnNameList.contains(property.getName())
&& ((StringUtils.isEmpty(property.getMappedBy()))
|| (!StringUtils.isEmpty(property.getMappedBy())
&& (property.getTarget() != null
&& property
.getTarget()
.getPackage()
.equals(Package.getPackage("com.axelor.meta.db"))
&& !property.getTarget().isAssignableFrom(MetaFile.class)
&& !property.getTarget().isAssignableFrom(MetaJsonField.class))))) {
return true;
}
return false;
}
/* Get Header For csv File */
private String getMetaModelHeader(
Object value, Property property, CSVInput csvInput, boolean isRelativeDate) {
String propertyTypeStr = property.getType().toString();
String propertyName = property.getName();
switch (propertyTypeStr) {
case "DATE":
case "DATETIME":
if (isRelativeDate) {
return getDateOrDateTimeHeader(property, csvInput);
}
return propertyName;
case "LONG":
return propertyName.equalsIgnoreCase("id") ? "importId" : propertyName;
case "BINARY":
byteArrFieldFlag = true;
return "byte_" + propertyName;
case "ONE_TO_ONE":
case "MANY_TO_ONE":
return getRelationalFieldHeader(property, csvInput, "ONE");
case "ONE_TO_MANY":
case "MANY_TO_MANY":
return getRelationalFieldHeader(property, csvInput, "MANY");
default:
return propertyName;
}
}
private String getDateOrDateTimeHeader(Property property, CSVInput csvInput) {
String propertyName = property.getName();
CSVBind csvBind = new CSVBind();
csvBind.setField(propertyName);
csvBind.setColumn(propertyName);
if (property.getType().toString().equals("DATE")) {
csvBind.setExpression(
"call:com.axelor.csv.script.ImportDateTime:importDate(" + propertyName + ")");
} else {
csvBind.setExpression(
"call:com.axelor.csv.script.ImportDateTime:importDateTime(" + propertyName + ")");
}
csvInput.getBindings().add(csvBind);
return propertyName;
}
private String getRelationalFieldHeader(
Property property, CSVInput csvInput, String relationship) {
csvInput.setSearch("self.importId = :importId");
CSVBind csvBind = new CSVBind();
String columnName = property.getName() + "_importId";
String search =
relationship.equalsIgnoreCase("ONE")
? "self.importId = :" + columnName
: "self.importId in :" + columnName;
if (property.getTarget() != null
&& property.getTarget().getPackage().equals(Package.getPackage("com.axelor.meta.db"))
&& !property.getTarget().getTypeName().equals("com.axelor.meta.db.MetaFile")) {
columnName = property.getName() + "_name";
search =
relationship.equalsIgnoreCase("ONE")
? "self.name = :" + columnName
: "self.name in :" + columnName;
}
csvBind.setColumn(columnName);
csvBind.setField(property.getName());
csvBind.setSearch(search);
csvBind.setUpdate(true);
if (relationship.equalsIgnoreCase("MANY")) {
csvBind.setExpression(columnName + ".split('\\\\|') as List");
}
csvInput.getBindings().add(csvBind);
referenceFlag = true;
if (property.isRequired()) {
notNullReferenceFlag = true;
}
return columnName;
}
/* Get Data For csv File */
private String getMetaModelData(
String metaModelName,
Mapper metaModelMapper,
Property property,
Object dataObject,
String dirPath,
boolean isRelativeDate,
boolean updateImportId) {
String id = metaModelMapper.get(dataObject, "id").toString();
Object value = metaModelMapper.get(dataObject, property.getName());
if (value == null) {
return "";
}
String propertyTypeStr = property.getType().toString();
switch (propertyTypeStr) {
case "LONG":
if (updateImportId) {
return ((Model) dataObject).getImportId();
}
return value.toString();
case "DATE":
if (isRelativeDate) {
return createRelativeDate((LocalDate) value);
}
return value.toString();
case "DATETIME":
if (isRelativeDate) {
if (property.getJavaType() == ZonedDateTime.class) {
return createRelativeDateTime(((ZonedDateTime) value).toLocalDateTime());
}
return createRelativeDateTime((LocalDateTime) value);
}
return property.getJavaType() == ZonedDateTime.class
? ((ZonedDateTime) value).toLocalDateTime().toString()
: value.toString();
case "BINARY":
String fileName = metaModelName + "_" + property.getName() + "_" + id + ".png";
try {
org.apache.commons.io.FileUtils.writeByteArrayToFile(
new File(dirPath, fileName), (byte[]) value);
fileNameList.add(fileName);
} catch (IOException e) {
e.printStackTrace();
}
return fileName;
case "ONE_TO_ONE":
case "MANY_TO_ONE":
return getRelationalFieldValue(property, value, updateImportId);
case "ONE_TO_MANY":
case "MANY_TO_MANY":
return getRelationalFieldData(property, value, updateImportId);
default:
return value.toString();
}
}
public String createRelativeDateTime(LocalDateTime dateT) {
LocalDateTime currentDateTime = LocalDateTime.now();
long years = currentDateTime.until(dateT, ChronoUnit.YEARS);
currentDateTime = currentDateTime.plusYears(years);
long months = currentDateTime.until(dateT, ChronoUnit.MONTHS);
currentDateTime = currentDateTime.plusMonths(months);
long days = currentDateTime.until(dateT, ChronoUnit.DAYS);
currentDateTime = currentDateTime.plusDays(days);
long hours = currentDateTime.until(dateT, ChronoUnit.HOURS);
currentDateTime = currentDateTime.plusHours(hours);
long minutes = currentDateTime.until(dateT, ChronoUnit.MINUTES);
currentDateTime = currentDateTime.plusMinutes(minutes);
long seconds = currentDateTime.until(dateT, ChronoUnit.SECONDS);
if (seconds < 0 || minutes < 0 || hours < 0 || days < 0 || months < 0 || years < 0) {
return "NOW["
+ ((years == 0) ? "" : (years + "y"))
+ ((months == 0) ? "" : (months + "M"))
+ ((days == 0) ? "" : (days + "d"))
+ ((hours == 0) ? "" : (hours + "H"))
+ ((minutes == 0) ? "" : (minutes + "m"))
+ ((seconds == 0) ? "" : (seconds + "s"))
+ "]";
}
return "NOW["
+ ((years == 0) ? "" : ("+" + years + "y"))
+ ((months == 0) ? "" : ("+" + months + "M"))
+ ((days == 0) ? "" : ("+" + days + "d"))
+ ((hours == 0) ? "" : ("+" + hours + "H"))
+ ((minutes == 0) ? "" : ("+" + minutes + "m"))
+ ((seconds == 0) ? "" : ("+" + seconds + "s"))
+ "]";
}
public String createRelativeDate(LocalDate date) {
LocalDate currentDate = LocalDate.now();
long years = currentDate.until(date, ChronoUnit.YEARS);
currentDate = currentDate.plusYears(years);
long months = currentDate.until(date, ChronoUnit.MONTHS);
currentDate = currentDate.plusMonths(months);
long days = currentDate.until(date, ChronoUnit.DAYS);
currentDate = currentDate.plusDays(days);
if (days < 0 || months < 0 || years < 0) {
return "TODAY["
+ ((years == 0) ? "" : years + "y")
+ ((months == 0) ? "" : months + "M")
+ ((days == 0) ? "" : days + "d")
+ "]";
} else if (days == 0 && months == 0 && years == 0) {
return "TODAY";
}
return "TODAY["
+ ((years == 0) ? "" : ("+" + years + "y"))
+ ((months == 0) ? "" : ("+" + months + "M"))
+ ((days == 0) ? "" : ("+" + days + "d"))
+ "]";
}
public String getRelationalFieldData(Property property, Object value, boolean updateImportId) {
StringBuilder idStringBuilder = new StringBuilder();
Collection<?> valueList = (Collection<?>) value;
String referenceData = "";
for (Object val : valueList) {
referenceData = getRelationalFieldValue(property, val, updateImportId);
if (StringUtils.notBlank(referenceData)) {
idStringBuilder.append(referenceData + REFERENCE_FIELD_SEPARATOR);
}
}
if (StringUtils.notBlank(idStringBuilder)) {
idStringBuilder.setLength(idStringBuilder.length() - 1);
}
return idStringBuilder.toString();
}
private String getRelationalFieldValue(Property property, Object val, boolean updateImportId) {
if (property.getTarget() != null
&& property.getTarget().getPackage().equals(Package.getPackage("com.axelor.meta.db"))
&& !property.getTarget().getTypeName().equals("com.axelor.meta.db.MetaFile")) {
try {
return Mapper.of(val.getClass()).get(val, "name").toString();
} catch (Exception e) {
return (updateImportId) ? ((Model) val).getImportId() : ((Model) val).getId().toString();
}
} else {
return (updateImportId) ? ((Model) val).getImportId() : ((Model) val).getId().toString();
}
}
private File generateZIP(String dirPath, List<String> fileNameList) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmSS");
String backupZipFileName = "DataBackup_" + LocalDateTime.now().format(formatter) + ".zip";
File zipFile = new File(dirPath, backupZipFileName);
try {
ZipOutputStream out = null;
out = new ZipOutputStream(new FileOutputStream(zipFile));
for (String fileName : fileNameList) {
ZipEntry e = new ZipEntry(fileName);
out.putNextEntry(e);
File file = new File(dirPath, fileName);
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file));
byte[] data;
while (bin.available() > 0) {
if (bin.available() < BUFFER_SIZE) {
data = new byte[bin.available()];
} else {
data = new byte[BUFFER_SIZE];
}
bin.read(data, 0, data.length);
out.write(data, 0, data.length);
}
bin.close();
out.closeEntry();
file.delete();
}
out.close();
} catch (IOException e) {
TraceBackService.trace(e, "Error From DataBackupCreateService - generateZIP()");
}
return zipFile;
}
/* Generate XML File from CSVConfig */
private void generateConfig(String dirPath, CSVConfig csvConfig) {
try {
File file = new File(dirPath, DataBackupServiceImpl.CONFIG_FILE_NAME);
FileWriter fileWriter = new FileWriter(file, true);
XStream xStream = new XStream();
xStream.processAnnotations(CSVConfig.class);
xStream.setMode(XStream.NO_REFERENCES);
fileWriter.append(xStream.toXML(csvConfig));
fileWriter.close();
} catch (IOException e) {
TraceBackService.trace(e, "Error From DataBackupCreateService - generateConfig()");
}
}
}

View File

@ -0,0 +1,162 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.common.StringUtils;
import com.axelor.data.Listener;
import com.axelor.data.csv.CSVImporter;
import com.axelor.db.JPA;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.exception.service.TraceBackService;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.common.base.Strings;
import com.google.common.io.Files;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DataBackupRestoreService {
/* Restore the Data using provided zip File and prepare Log File and Return it*/
public File restore(MetaFile zipedBackupFile) {
Logger LOG = LoggerFactory.getLogger(getClass());
File tempDir = Files.createTempDir();
String dirPath = tempDir.getAbsolutePath();
StringBuilder sb = new StringBuilder();
try {
unZip(zipedBackupFile, dirPath);
String configFName =
tempDir.getAbsolutePath() + File.separator + DataBackupServiceImpl.CONFIG_FILE_NAME;
CSVImporter csvImporter = new CSVImporter(configFName, tempDir.getAbsolutePath());
csvImporter.addListener(
new Listener() {
String modelName;
StringBuilder sb1 = new StringBuilder();
@Override
public void handle(Model bean, Exception e) {
if (e.getMessage() != null && !e.getMessage().equals("null")) {
if (bean != null) {
sb1.append(bean.getClass().getSimpleName() + " : \n" + e.getMessage() + "\n\n");
} else {
sb1.append(e.getMessage() + "\n\n");
}
}
}
@Override
public void imported(Model model) {
modelName = model.getClass().getSimpleName();
}
@Override
public void imported(Integer total, Integer count) {
String str = "", strError = "";
if (!StringUtils.isBlank(sb1)) {
strError = "Errors : \n" + sb1.toString();
}
str = "Total Records : {" + total + "} - Success Records : {" + count + "} \n";
if (total != 0 && count != 0) {
sb.append(modelName + " : \n");
}
sb.append(strError).append(str + "-----------------------------------------\n");
sb1.setLength(0);
}
});
csvImporter.run();
LOG.info("Data Restore Completed");
FileUtils.cleanDirectory(new File(tempDir.getAbsolutePath()));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmSS");
String logFileName = "DataBackupLog_" + LocalDateTime.now().format(formatter) + ".log";
File file = new File(tempDir.getAbsolutePath(), logFileName);
PrintWriter pw = new PrintWriter(file);
pw.write(sb.toString());
pw.close();
return file;
} catch (IOException e) {
TraceBackService.trace(e);
return null;
}
}
private boolean unZip(MetaFile zipMetaFile, String destinationDirectoryPath) throws IOException {
File zipFile = MetaFiles.getPath(zipMetaFile).toFile();
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
FileOutputStream fout =
new FileOutputStream(new File(destinationDirectoryPath, ze.getName()));
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
return true;
}
public boolean SeuencesExist() {
long total = (long) JPA.em().createQuery("SELECT count(*) FROM Sequence").getSingleResult();
long total1 = (long) JPA.em().createQuery("SELECT count(*) FROM MrpLineType").getSingleResult();
return total > 0 || total1 > 0 ? true : false;
}
public Object importObjectWithByteArray(Object bean, Map<String, Object> values)
throws IOException {
assert bean instanceof Model;
final Path path = (Path) values.get("__path__");
Mapper mapper = Mapper.of(bean.getClass());
for (String fieldName : values.keySet()) {
if (fieldName.startsWith("byte_")) {
String fileName = (String) values.get(fieldName);
if (Strings.isNullOrEmpty((fileName))) {
return bean;
}
try {
final File image = path.resolve(fileName).toFile();
byte[] bytes = new byte[(int) image.length()];
bytes = java.nio.file.Files.readAllBytes(image.toPath());
mapper.set(bean, fieldName.substring(5), bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return bean;
}
}

View File

@ -0,0 +1,29 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.apps.base.db.DataBackup;
public interface DataBackupService {
public void createBackUp(DataBackup dataBackup);
public void restoreBackUp(DataBackup dataBackup);
public boolean SeuencesExist();
}

View File

@ -0,0 +1,189 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.service.app;
import com.axelor.apps.base.db.DataBackup;
import com.axelor.apps.base.db.repo.DataBackupRepository;
import com.axelor.auth.AuditableRunner;
import com.axelor.db.JPA;
import com.axelor.exception.service.TraceBackService;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaJsonField;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.google.inject.servlet.RequestScoper;
import com.google.inject.servlet.ServletScopes;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DataBackupServiceImpl implements DataBackupService {
static final String CONFIG_FILE_NAME = "config.xml";
@Inject private DataBackupCreateService createService;
@Inject private DataBackupRestoreService restoreService;
@Inject private MetaFiles metaFiles;
@Inject private DataBackupRepository dataBackupRepository;
@Inject private MetaModelRepository metaModelRepo;
private ExecutorService executor = Executors.newCachedThreadPool();
@Transactional
@Override
public void createBackUp(DataBackup dataBackup) {
DataBackup obj = dataBackupRepository.find(dataBackup.getId());
obj.setStatusSelect(DataBackupRepository.DATA_BACKUP_STATUS_IN_PROGRESS);
dataBackupRepository.save(obj);
if (dataBackup.getUpdateImportId()) {
updateImportId();
}
try {
executor.submit(
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap());
try (RequestScoper.CloseableScope ignored = scope.open()) {
startBackup(obj);
}
return true;
}
});
} catch (Exception e) {
TraceBackService.trace(e);
}
}
@Transactional(rollbackOn = {Exception.class})
protected void startBackup(DataBackup dataBackup) throws Exception {
final AuditableRunner runner = Beans.get(AuditableRunner.class);
final Callable<Boolean> job =
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Logger LOG = LoggerFactory.getLogger(getClass());
DataBackup obj = Beans.get(DataBackupRepository.class).find(dataBackup.getId());
File backupFile = createService.create(obj);
dataBackupRepository.refresh(obj);
obj.setBackupMetaFile(metaFiles.upload(backupFile));
obj.setStatusSelect(DataBackupRepository.DATA_BACKUP_STATUS_CREATED);
Beans.get(DataBackupRepository.class).save(obj);
LOG.info("Data BackUp Saved");
return true;
}
};
runner.run(job);
}
@Transactional
@Override
public void restoreBackUp(DataBackup dataBackup) {
DataBackup obj = dataBackupRepository.find(dataBackup.getId());
obj.setStatusSelect(DataBackupRepository.DATA_BACKUP_STATUS_IN_PROGRESS);
dataBackupRepository.save(obj);
try {
executor.submit(
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
startRestore(dataBackup);
return true;
}
});
} catch (Exception e) {
TraceBackService.trace(e);
}
}
protected void startRestore(DataBackup dataBackup) throws Exception {
final AuditableRunner runner = Beans.get(AuditableRunner.class);
final Callable<Boolean> job =
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Logger LOG = LoggerFactory.getLogger(getClass());
DataBackup obj = Beans.get(DataBackupRepository.class).find(dataBackup.getId());
File logFile = restoreService.restore(obj.getBackupMetaFile());
save(logFile, obj);
LOG.info("Data Restore Saved");
return true;
}
public void save(File logFile, DataBackup obj) {
try {
JPA.em().getTransaction().begin();
obj = Beans.get(DataBackupRepository.class).find(dataBackup.getId());
if (logFile != null) {
obj.setStatusSelect(DataBackupRepository.DATA_BACKUP_STATUS_RESTORE);
obj.setLogMetaFile(metaFiles.upload(logFile));
} else {
obj.setStatusSelect(DataBackupRepository.DATA_BACKUP_STATUS_RESTORE_ERROR);
}
Beans.get(DataBackupRepository.class).save(obj);
JPA.em().getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
}
}
};
runner.run(job);
}
public boolean SeuencesExist() {
return restoreService.SeuencesExist();
}
@Transactional
public void updateImportId() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ddMMyyHHmm");
String filterStr =
"self.packageName NOT LIKE '%meta%' AND self.packageName !='com.axelor.studio.db' AND self.name!='DataBackup'";
List<MetaModel> metaModelList = metaModelRepo.all().filter(filterStr).fetch();
metaModelList.add(metaModelRepo.findByName(MetaFile.class.getSimpleName()));
metaModelList.add(metaModelRepo.findByName(MetaJsonField.class.getSimpleName()));
for (MetaModel metaModel : metaModelList) {
String currentDateTimeStr = "'" + LocalDateTime.now().format(formatter).toString() + "'";
String query =
"Update "
+ metaModel.getName()
+ " self SET self.importId = CONCAT(CAST(self.id as text),"
+ currentDateTimeStr
+ ") WHERE self.importId=null";
JPA.execute(query);
}
}
}

View File

@ -0,0 +1,196 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.web;
import com.axelor.apps.base.db.App;
import com.axelor.apps.base.db.repo.AppRepository;
import com.axelor.apps.base.exceptions.IExceptionMessages;
import com.axelor.apps.base.service.app.AccessConfigImportService;
import com.axelor.apps.base.service.app.AccessTemplateService;
import com.axelor.apps.base.service.app.AppService;
import com.axelor.common.Inflector;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.repo.MetaFileRepository;
import com.axelor.meta.db.repo.MetaViewRepository;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.axelor.rpc.Context;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Singleton
public class AppController {
public void importDataDemo(ActionRequest request, ActionResponse response)
throws AxelorException {
App app = request.getContext().asType(App.class);
app = Beans.get(AppRepository.class).find(app.getId());
Beans.get(AppService.class).importDataDemo(app);
response.setFlash(I18n.get(IExceptionMessages.DEMO_DATA_SUCCESS));
response.setReload(true);
}
public void installApp(ActionRequest request, ActionResponse response) throws AxelorException {
App app = request.getContext().asType(App.class);
app = Beans.get(AppRepository.class).find(app.getId());
Beans.get(AppService.class).installApp(app, null);
response.setSignal("refresh-app", true);
}
public void configure(ActionRequest request, ActionResponse response) {
App app = request.getContext().asType(App.class);
String code = app.getCode();
String appName = Inflector.getInstance().camelize(code);
String viewName = "app-" + code + "-config-form";
if (Beans.get(MetaViewRepository.class).findByName(viewName) == null) {
response.setFlash(I18n.get(IExceptionMessages.NO_CONFIG_REQUIRED));
} else {
response.setView(
ActionView.define(I18n.get("Configure") + ": " + app.getName())
.add("form", viewName)
.model("com.axelor.apps.base.db.App" + appName)
.context("_showRecord", app.getId())
.param("forceEdit", "true")
.map());
}
}
public void uninstallApp(ActionRequest request, ActionResponse response) throws AxelorException {
App app = request.getContext().asType(App.class);
app = Beans.get(AppRepository.class).find(app.getId());
Beans.get(AppService.class).unInstallApp(app);
response.setSignal("refresh-app", true);
}
public void bulkInstall(ActionRequest request, ActionResponse response) throws AxelorException {
Context context = request.getContext();
Set<Map<String, Object>> apps = new HashSet<Map<String, Object>>();
Collection<Map<String, Object>> appsSet =
(Collection<Map<String, Object>>) context.get("appsSet");
if (appsSet != null) {
apps.addAll(appsSet);
}
Boolean importDemo = (Boolean) context.get("importDemoData");
String language = (String) context.get("languageSelect");
AppRepository appRepository = Beans.get(AppRepository.class);
List<App> appList = new ArrayList<App>();
for (Map<String, Object> appData : apps) {
App app = appRepository.find(Long.parseLong(appData.get("id").toString()));
appList.add(app);
}
Beans.get(AppService.class).bulkInstall(appList, importDemo, language);
response.setFlash(I18n.get(IExceptionMessages.BULK_INSTALL_SUCCESS));
response.setSignal("refresh-app", true);
}
public void refreshApp(ActionRequest request, ActionResponse response) {
try {
Beans.get(AppService.class).refreshApp();
response.setNotify(I18n.get(IExceptionMessages.REFRESH_APP_SUCCESS));
response.setReload(true);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
response.setNotify(I18n.get(IExceptionMessages.REFRESH_APP_ERROR));
}
}
public void generateAccessTemplate(ActionRequest request, ActionResponse response)
throws AxelorException {
MetaFile accesssFile = Beans.get(AccessTemplateService.class).generateTemplate();
if (accesssFile == null) {
return;
}
response.setView(
ActionView.define(I18n.get("Export file"))
.model(App.class.getName())
.add(
"html",
"ws/rest/com.axelor.meta.db.MetaFile/"
+ accesssFile.getId()
+ "/content/download?v="
+ accesssFile.getVersion())
.param("download", "true")
.map());
}
public void importRoles(ActionRequest request, ActionResponse response) throws AxelorException {
App app = request.getContext().asType(App.class);
app = Beans.get(AppRepository.class).find(app.getId());
Beans.get(AppService.class).importRoles(app);
response.setReload(true);
response.setFlash(I18n.get(IExceptionMessages.ROLE_IMPORT_SUCCESS));
}
public void importAllRoles(ActionRequest request, ActionResponse response)
throws AxelorException {
Beans.get(AppService.class).importRoles();
response.setFlash(I18n.get(IExceptionMessages.ROLE_IMPORT_SUCCESS));
response.setReload(true);
}
public void importAccessConfig(ActionRequest request, ActionResponse response)
throws AxelorException {
Map<String, Object> metaFileMap = (Map<String, Object>) request.getContext().get("metaFile");
if (metaFileMap != null) {
Long fileId = Long.parseLong(metaFileMap.get("id").toString());
Beans.get(AccessConfigImportService.class)
.importAccessConfig(Beans.get(MetaFileRepository.class).find(fileId));
response.setFlash(I18n.get(IExceptionMessages.ACCESS_CONFIG_IMPORTED));
response.setCanClose(true);
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.web;
import com.axelor.apps.base.db.DataBackup;
import com.axelor.apps.base.service.app.DataBackupService;
import com.axelor.inject.Beans;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import java.io.IOException;
public class DataBackupController {
public void CreateBackUp(ActionRequest req, ActionResponse res) throws IOException {
DataBackup dataBackup = req.getContext().asType(DataBackup.class);
Beans.get(DataBackupService.class).createBackUp(dataBackup);
res.setReload(true);
}
public void RestoreBackUp(ActionRequest req, ActionResponse res) throws IOException {
DataBackupService dataBackupService = Beans.get(DataBackupService.class);
DataBackup dataBackup = req.getContext().asType(DataBackup.class);
if (dataBackupService.SeuencesExist()) {
res.setError("Remove all Sequences And MrpLineTypes to restore backup");
} else {
dataBackupService.restoreBackUp(dataBackup);
}
res.setReload(true);
}
}

View File

@ -0,0 +1,78 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.apps.base.web;
import com.axelor.apps.base.db.ObjectDataConfig;
import com.axelor.apps.base.db.ObjectDataConfigExport;
import com.axelor.apps.base.db.repo.ObjectDataConfigRepository;
import com.axelor.apps.base.service.ObjectDataAnonymizeService;
import com.axelor.apps.base.service.ObjectDataExportService;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.schema.actions.ActionView;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.axelor.rpc.Context;
public class ObjectDataExportController {
public void export(ActionRequest request, ActionResponse response) throws AxelorException {
ObjectDataConfigExport objDataConfigExport =
request.getContext().asType(ObjectDataConfigExport.class);
Long objectDataconfigId = objDataConfigExport.getObjectDataConfig().getId();
ObjectDataConfig objectDataConfig =
Beans.get(ObjectDataConfigRepository.class).find(objectDataconfigId);
MetaFile dataFile =
Beans.get(ObjectDataExportService.class).export(objectDataConfig, objDataConfigExport);
if (dataFile != null) {
response.setView(
ActionView.define(I18n.get("Data"))
.add(
"html",
"ws/rest/com.axelor.meta.db.MetaFile/"
+ dataFile.getId()
+ "/content/download?v="
+ dataFile.getVersion())
.param("download", "true")
.map());
}
response.setCanClose(true);
}
public void anonymize(ActionRequest request, ActionResponse response) throws AxelorException {
Context context = request.getContext();
Long recordId = Long.parseLong(context.get("modelSelectId").toString());
Long objectDataconfigId = Long.parseLong(context.get("objectDataConfigId").toString());
ObjectDataConfig objectDataConfig =
Beans.get(ObjectDataConfigRepository.class).find(objectDataconfigId);
Beans.get(ObjectDataAnonymizeService.class).anonymize(objectDataConfig, recordId);
response.setFlash("Data anonymized successfully");
response.setCanClose(true);
}
}

View File

@ -0,0 +1,38 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.auth.db;
public interface IMessage {
/** Common messages * */
public static final String IMPORT_OK = /*$$(*/ "Import completed successfully" /*)*/;
public static final String ERR_IMPORT = /*$$(*/ "Error in import. Please check log." /*)*/;
/** Permission assistant & group menu assistant* */
public static final String BAD_FILE = /*$$(*/ "Bad import file" /*)*/;
public static final String NO_HEADER = /*$$(*/ "No header row found" /*)*/;
public static final String BAD_HEADER = /*$$(*/ "Bad header row:" /*)*/;
public static final String NO_GROUP = /*$$(*/ "Groups not found: %s" /*)*/;
public static final String NO_ROLE = /*$$(*/ "Roles not found: %s" /*)*/;
public static final String NO_OBJECT = /*$$(*/ "Object not found: %s" /*)*/;
public static final String ERR_IMPORT_WITH_MSG = /*$$(*/
"Error in import: %s. Please check the server log" /*)*/;
public static final String NO_MENU = /*$$(*/ "Menu not found: %s" /*)*/;
}

View File

@ -0,0 +1,748 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.auth.service;
import com.axelor.apps.tool.StringTool;
import com.axelor.auth.db.Group;
import com.axelor.auth.db.IMessage;
import com.axelor.auth.db.Permission;
import com.axelor.auth.db.PermissionAssistant;
import com.axelor.auth.db.Role;
import com.axelor.auth.db.repo.GroupRepository;
import com.axelor.auth.db.repo.PermissionAssistantRepository;
import com.axelor.auth.db.repo.PermissionRepository;
import com.axelor.auth.db.repo.RoleRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.MetaPermission;
import com.axelor.meta.db.MetaPermissionRule;
import com.axelor.meta.db.repo.MetaFieldRepository;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.axelor.meta.db.repo.MetaPermissionRepository;
import com.axelor.meta.db.repo.MetaPermissionRuleRepository;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import org.apache.commons.io.output.FileWriterWithEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PermissionAssistantService {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Inject private MetaPermissionRepository metaPermissionRepository;
@Inject private PermissionRepository permissionRepository;
@Inject private MetaPermissionRuleRepository ruleRepository;
@Inject private GroupRepository groupRepository;
@Inject private MetaModelRepository modelRepository;
@Inject private MetaFieldRepository fieldRepository;
@Inject private MetaFiles metaFiles;
@Inject private RoleRepository roleRepo;
private String errorLog = "";
private Collection<String> header =
Arrays.asList(/*$$(*/ "Object" /*)*/, /*$$(*/ "Field" /*)*/, /*$$(*/ "Title" /*)*/);
private Collection<String> groupHeader =
Arrays.asList(
"",
/*$$(*/ "Read" /*)*/,
/*$$(*/ "Write" /*)*/,
/*$$(*/ "Create" /*)*/,
/*$$(*/ "Delete" /*)*/,
/*$$(*/ "Export" /*)*/,
/*$$(*/ "Condition" /*)*/,
/*$$(*/ "ConditionParams" /*)*/,
/*$$(*/ "Readonly If" /*)*/,
/*$$(*/ "Hide If" /*)*/);
private String getFileName(PermissionAssistant assistant) {
String userCode = assistant.getCreatedBy().getCode();
String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
String fileName = userCode + "-" + dateString + ".csv";
return fileName;
}
public void createFile(PermissionAssistant assistant) {
File permFile = new File(Files.createTempDir(), getFileName(assistant));
try {
try (FileWriterWithEncoding fileWriter =
new FileWriterWithEncoding(permFile, StandardCharsets.UTF_8)) {
CSVWriter csvWriter = new CSVWriter(fileWriter, ';');
writeGroup(csvWriter, assistant);
}
createMetaFile(permFile, assistant);
} catch (Exception e) {
LOG.error(e.getLocalizedMessage());
TraceBackService.trace(e);
}
}
@Transactional(rollbackOn = {Exception.class})
public void createMetaFile(File permFile, PermissionAssistant assistant) throws IOException {
assistant.setMetaFile(metaFiles.upload(permFile));
Beans.get(PermissionAssistantRepository.class).save(assistant);
}
private Collection<String> getTranslatedStrings(
Collection<String> strings, ResourceBundle bundle) {
return strings.stream().map(bundle::getString).collect(Collectors.toList());
}
private void writeGroup(CSVWriter csvWriter, PermissionAssistant assistant) {
String[] groupRow = null;
Integer count = header.size();
ResourceBundle bundle = I18n.getBundle(new Locale(assistant.getLanguage()));
List<String> headerRow = new ArrayList<String>();
headerRow.addAll(getTranslatedStrings(header, bundle));
if (assistant.getTypeSelect() == PermissionAssistantRepository.TYPE_GROUPS) {
groupRow = new String[header.size() + (assistant.getGroupSet().size() * groupHeader.size())];
for (Group group : assistant.getGroupSet()) {
groupRow[count + 1] = group.getCode();
headerRow.addAll(getTranslatedStrings(groupHeader, bundle));
count += groupHeader.size();
}
} else if (assistant.getTypeSelect() == PermissionAssistantRepository.TYPE_ROLES) {
groupRow = new String[header.size() + (assistant.getRoleSet().size() * groupHeader.size())];
for (Role role : assistant.getRoleSet()) {
groupRow[count + 1] = role.getName();
headerRow.addAll(getTranslatedStrings(groupHeader, bundle));
count += groupHeader.size();
}
}
LOG.debug("Header row created: {}", headerRow);
csvWriter.writeNext(groupRow);
csvWriter.writeNext(headerRow.toArray(groupRow));
writeObject(csvWriter, assistant, groupRow.length, bundle);
}
public Comparator<Object> compareField() {
return new Comparator<Object>() {
@Override
public int compare(Object field1, Object field2) {
return ((MetaField) field1).getName().compareTo(((MetaField) field2).getName());
}
};
}
private void writeObject(
CSVWriter csvWriter, PermissionAssistant assistant, Integer size, ResourceBundle bundle) {
MetaField userField = assistant.getMetaField();
for (MetaModel object : assistant.getObjectSet()) {
int colIndex = header.size() + 1;
String[] row = new String[size];
row[0] = object.getFullName();
if (assistant.getTypeSelect() == PermissionAssistantRepository.TYPE_GROUPS) {
for (Group group : assistant.getGroupSet()) {
String permName = getPermissionName(userField, object.getName(), group.getCode());
colIndex = writePermission(object, userField, row, colIndex, permName);
colIndex++;
}
} else if (assistant.getTypeSelect() == PermissionAssistantRepository.TYPE_ROLES) {
for (Role role : assistant.getRoleSet()) {
String permName = getPermissionName(userField, object.getName(), role.getName());
colIndex = writePermission(object, userField, row, colIndex, permName);
colIndex++;
}
}
csvWriter.writeNext(row);
if (!assistant.getFieldPermission()) {
continue;
}
List<MetaField> fieldList = object.getMetaFields();
Collections.sort(fieldList, compareField());
for (MetaField field : fieldList) {
colIndex = header.size() + 1;
row = new String[size];
row[1] = field.getName();
row[2] = getFieldTitle(field, bundle);
if (assistant.getTypeSelect() == PermissionAssistantRepository.TYPE_GROUPS) {
for (Group group : assistant.getGroupSet()) {
String permName = getPermissionName(null, object.getName(), group.getCode());
colIndex = writeFieldPermission(field, row, colIndex, permName);
colIndex++;
}
} else if (assistant.getTypeSelect() == PermissionAssistantRepository.TYPE_ROLES) {
for (Role role : assistant.getRoleSet()) {
String permName = getPermissionName(null, object.getName(), role.getName());
colIndex = writeFieldPermission(field, row, colIndex, permName);
colIndex++;
}
}
csvWriter.writeNext(row);
}
}
}
private String getPermissionName(MetaField userField, String objectName, String suffix) {
String permName = "perm." + objectName + "." + suffix;
if (userField != null) {
permName += "." + userField.getName();
}
return permName;
}
private String getFieldTitle(MetaField field, ResourceBundle bundle) {
String title = field.getLabel();
if (!Strings.isNullOrEmpty(title)) {
title = bundle.getString(title);
if (Strings.isNullOrEmpty(title)) {
title = field.getLabel();
}
}
return title;
}
private int writeFieldPermission(MetaField field, String[] row, int colIndex, String permName) {
MetaPermissionRule rule =
ruleRepository
.all()
.filter(
"self.metaPermission.name = ?1 and self.metaPermission.object = ?2 and self.field = ?3",
permName,
field.getMetaModel().getFullName(),
field.getName())
.fetchOne();
if (rule != null) {
row[colIndex++] = rule.getCanRead() == false ? "" : "x";
row[colIndex++] = rule.getCanWrite() == false ? "" : "x";
row[colIndex++] = "";
row[colIndex++] = "";
row[colIndex++] = rule.getCanExport() == false ? "" : "x";
row[colIndex++] = "";
row[colIndex++] = "";
row[colIndex++] =
Strings.isNullOrEmpty(rule.getReadonlyIf()) ? "" : rule.getReadonlyIf(); // readonly if
row[colIndex++] = Strings.isNullOrEmpty(rule.getHideIf()) ? "" : rule.getHideIf(); // hide if
}
return colIndex;
}
private int writePermission(
MetaModel object, MetaField userField, String[] row, int colIndex, String permName) {
Permission perm = permissionRepository.findByName(permName);
if (perm != null && perm.getObject().equals(object.getFullName())) {
row[colIndex++] = perm.getCanRead() == false ? "" : "x";
row[colIndex++] = perm.getCanWrite() == false ? "" : "x";
row[colIndex++] = perm.getCanCreate() == false ? "" : "x";
row[colIndex++] = perm.getCanRemove() == false ? "" : "x";
row[colIndex++] = perm.getCanExport() == false ? "" : "x";
row[colIndex++] = Strings.isNullOrEmpty(perm.getCondition()) ? "" : perm.getCondition();
row[colIndex++] =
Strings.isNullOrEmpty(perm.getConditionParams()) ? "" : perm.getConditionParams();
row[colIndex++] = ""; // readonly if
row[colIndex++] = ""; // hide if
} else if (userField != null) {
MetaField objectField =
fieldRepository
.all()
.filter(
"self.typeName = ?1 and self.metaModel = ?2 and self.relationship = 'ManyToOne'",
userField.getTypeName(),
object)
.fetchOne();
if (objectField != null) {
String condition = "";
String conditionParams = "__user__." + userField.getName();
if (userField.getRelationship().contentEquals("ManyToOne")) {
condition = "self." + objectField.getName() + " = ?";
} else {
condition = "self." + objectField.getName() + " in (?)";
}
row[colIndex++] = "x";
row[colIndex++] = "x";
row[colIndex++] = "x";
row[colIndex++] = "x";
row[colIndex++] = "x";
row[colIndex++] = condition;
row[colIndex++] = conditionParams;
row[colIndex++] = ""; // readonly if
row[colIndex++] = ""; // hide if
}
}
return colIndex;
}
private static boolean headerEquals(
Collection<String> standardRow,
Collection<String> translatedRow,
Collection<String> headerRow) {
if (standardRow.size() != translatedRow.size() || headerRow.size() != standardRow.size()) {
return false;
}
Iterator<String> itStandard = standardRow.iterator();
Iterator<String> itTranslated = translatedRow.iterator();
Iterator<String> it = headerRow.iterator();
while (it.hasNext()) {
String standard = itStandard.next();
String translated = itTranslated.next();
String name = it.next();
if (!StringTool.equalsIgnoreCaseAndAccents(standard, name)
&& !StringTool.equalsIgnoreCaseAndAccents(translated, name)) {
return false;
}
}
return true;
}
private boolean checkHeaderRow(Collection<String> headerRow, ResourceBundle bundle) {
Collection<String> translatedHeader = getTranslatedStrings(header, bundle);
Collection<String> translatedGroupHeader = getTranslatedStrings(groupHeader, bundle);
// Untranslated
Collection<String> standardRow = Lists.newArrayList(header);
for (int count = header.size(); count < headerRow.size(); count += groupHeader.size()) {
standardRow.addAll(groupHeader);
}
// Translated
Collection<String> translatedRow = Lists.newArrayList(translatedHeader);
for (int count = header.size(); count < headerRow.size(); count += groupHeader.size()) {
translatedRow.addAll(translatedGroupHeader);
}
LOG.debug("Standard Headers: {}", standardRow);
LOG.debug("File Headers: {}", headerRow);
return headerEquals(standardRow, translatedRow, headerRow);
}
public String importPermissions(PermissionAssistant permissionAssistant) {
try {
ResourceBundle bundle = I18n.getBundle(new Locale(permissionAssistant.getLanguage()));
MetaFile metaFile = permissionAssistant.getMetaFile();
File csvFile = MetaFiles.getPath(metaFile).toFile();
try (CSVReader csvReader =
new CSVReader(
new InputStreamReader(new FileInputStream(csvFile), StandardCharsets.UTF_8), ';')) {
String[] groupRow = csvReader.readNext();
if (groupRow == null || groupRow.length < 11) {
errorLog = I18n.get(IMessage.BAD_FILE);
}
String[] headerRow = csvReader.readNext();
if (headerRow == null) {
errorLog = I18n.get(IMessage.NO_HEADER);
}
if (!checkHeaderRow(Arrays.asList(headerRow), bundle)) {
errorLog = I18n.get(IMessage.BAD_HEADER) + " " + Arrays.asList(headerRow);
}
if (!errorLog.equals("")) {
return errorLog;
}
if (permissionAssistant.getTypeSelect() == PermissionAssistantRepository.TYPE_GROUPS) {
Map<String, Group> groupMap = checkBadGroups(groupRow);
processGroupCSV(
csvReader,
groupRow,
groupMap,
permissionAssistant.getMetaField(),
permissionAssistant.getFieldPermission());
saveGroups(groupMap);
} else if (permissionAssistant.getTypeSelect()
== PermissionAssistantRepository.TYPE_ROLES) {
Map<String, Role> roleMap = checkBadRoles(groupRow);
processRoleCSV(
csvReader,
groupRow,
roleMap,
permissionAssistant.getMetaField(),
permissionAssistant.getFieldPermission());
saveRoles(roleMap);
}
}
} catch (Exception e) {
LOG.error(e.getLocalizedMessage());
TraceBackService.trace(e);
errorLog += "\n" + String.format(I18n.get(IMessage.ERR_IMPORT_WITH_MSG), e.getMessage());
}
return errorLog;
}
@Transactional
public void saveGroups(Map<String, Group> groupMap) {
for (Group group : groupMap.values()) {
groupRepository.save(group);
}
}
@Transactional
public void saveRoles(Map<String, Role> roleMap) {
for (Role role : roleMap.values()) {
roleRepo.save(role);
}
}
private Map<String, Group> checkBadGroups(String[] groupRow) {
List<String> badGroups = new ArrayList<String>();
Map<String, Group> groupMap = new HashMap<String, Group>();
for (Integer glen = header.size() + 1; glen < groupRow.length; glen += groupHeader.size()) {
String groupName = groupRow[glen];
Group group = groupRepository.all().filter("self.code = ?1", groupName).fetchOne();
if (group == null) {
badGroups.add(groupName);
} else {
groupMap.put(groupName, group);
}
}
if (!badGroups.isEmpty()) {
errorLog += "\n" + String.format(I18n.get(IMessage.NO_GROUP), badGroups);
}
return groupMap;
}
private Map<String, Role> checkBadRoles(String[] roleRow) {
List<String> badroles = new ArrayList<String>();
Map<String, Role> roleMap = new HashMap<String, Role>();
for (Integer len = header.size() + 1; len < roleRow.length; len += groupHeader.size()) {
String roleName = roleRow[len];
Role role = roleRepo.all().filter("self.name = ?1", roleName).fetchOne();
if (roleName == null) {
badroles.add(roleName);
} else {
roleMap.put(roleName, role);
}
}
if (!badroles.isEmpty()) {
errorLog += "\n" + String.format(I18n.get(IMessage.NO_ROLE), badroles);
}
return roleMap;
}
private String checkObject(String objectName) {
MetaModel model = modelRepository.all().filter("self.fullName = ?1", objectName).fetchOne();
if (model == null) {
errorLog += "\n" + String.format(I18n.get(IMessage.NO_OBJECT), objectName);
return null;
}
return objectName;
}
private void processGroupCSV(
CSVReader csvReader,
String[] groupRow,
Map<String, Group> groupMap,
MetaField field,
Boolean fieldBoolean)
throws IOException {
Map<String, MetaPermission> metaPermDict = new HashMap<String, MetaPermission>();
String objectName = null;
String[] row = csvReader.readNext();
while (row != null) {
for (Integer groupIndex = header.size() + 1;
groupIndex < row.length;
groupIndex += groupHeader.size()) {
String groupName = groupRow[groupIndex];
if (!groupMap.containsKey(groupName)) {
continue;
}
String[] rowGroup = Arrays.copyOfRange(row, groupIndex, groupIndex + groupHeader.size());
if (!Strings.isNullOrEmpty(groupName) && !Strings.isNullOrEmpty(row[0])) {
objectName = checkObject(row[0]);
if (objectName == null) {
break;
}
if (fieldBoolean) {
metaPermDict.put(groupName, getMetaPermission(groupMap.get(groupName), objectName));
}
updatePermission(groupMap.get(groupName), objectName, field, rowGroup);
} else if (fieldBoolean && objectName != null && !Strings.isNullOrEmpty(row[1])) {
updateFieldPermission(metaPermDict.get(groupName), row[1], rowGroup);
}
}
row = csvReader.readNext();
}
}
private void processRoleCSV(
CSVReader csvReader,
String[] roleRow,
Map<String, Role> roleMap,
MetaField field,
Boolean fieldPermission)
throws IOException {
Map<String, MetaPermission> metaPermDict = new HashMap<String, MetaPermission>();
String objectName = null;
String[] row = csvReader.readNext();
while (row != null) {
for (Integer groupIndex = header.size() + 1;
groupIndex < row.length;
groupIndex += groupHeader.size()) {
String roleName = roleRow[groupIndex];
if (!roleMap.containsKey(roleName)) {
continue;
}
String[] rowGroup = Arrays.copyOfRange(row, groupIndex, groupIndex + groupHeader.size());
if (!Strings.isNullOrEmpty(roleName) && !Strings.isNullOrEmpty(row[0])) {
objectName = checkObject(row[0]);
if (objectName == null) {
break;
}
if (fieldPermission) {
metaPermDict.put(roleName, getMetaPermission(roleMap.get(roleName), objectName));
}
updatePermission(roleMap.get(roleName), objectName, field, rowGroup);
} else if (fieldPermission && objectName != null && !Strings.isNullOrEmpty(row[1])) {
updateFieldPermission(metaPermDict.get(roleName), row[1], rowGroup);
}
}
row = csvReader.readNext();
}
}
public MetaPermission getMetaPermission(Group group, String objectName) {
String[] objectNames = objectName.split("\\.");
String groupName = group.getCode();
String permName = getPermissionName(null, objectNames[objectNames.length - 1], group.getCode());
MetaPermission metaPermission =
metaPermissionRepository.all().filter("self.name = ?1", permName).fetchOne();
if (metaPermission == null) {
LOG.debug("Create metaPermission group: {}, object: {}", groupName, objectName);
metaPermission = new MetaPermission();
metaPermission.setName(permName);
metaPermission.setObject(objectName);
group.addMetaPermission(metaPermission);
}
return metaPermission;
}
public MetaPermission getMetaPermission(Role role, String objectName) {
String[] objectNames = objectName.split("\\.");
String roleName = role.getName();
String permName = getPermissionName(null, objectNames[objectNames.length - 1], roleName);
MetaPermission metaPermission =
metaPermissionRepository.all().filter("self.name = ?1", permName).fetchOne();
if (metaPermission == null) {
LOG.debug("Create metaPermission role: {}, object: {}", roleName, objectName);
metaPermission = new MetaPermission();
metaPermission.setName(permName);
metaPermission.setObject(objectName);
role.addMetaPermission(metaPermission);
}
return metaPermission;
}
public MetaPermission updateFieldPermission(
MetaPermission metaPermission, String field, String[] row) {
MetaPermissionRule permissionRule =
ruleRepository
.all()
.filter(
"self.field = ?1 and self.metaPermission.name = ?2",
field,
metaPermission.getName())
.fetchOne();
if (permissionRule == null) {
permissionRule = new MetaPermissionRule();
permissionRule.setMetaPermission(metaPermission);
permissionRule.setField(field);
}
permissionRule.setCanRead(row[0].equalsIgnoreCase("x"));
permissionRule.setCanWrite(row[1].equalsIgnoreCase("x"));
permissionRule.setCanExport(row[4].equalsIgnoreCase("x"));
permissionRule.setReadonlyIf(row[5]);
permissionRule.setHideIf(row[6]);
metaPermission.addRule(permissionRule);
return metaPermission;
}
public void updatePermission(Group group, String objectName, MetaField field, String[] row) {
String[] objectNames = objectName.split("\\.");
String permName =
getPermissionName(field, objectNames[objectNames.length - 1], group.getCode());
Permission permission =
permissionRepository.all().filter("self.name = ?1", permName).fetchOne();
boolean newPermission = false;
if (permission == null) {
newPermission = true;
permission = new Permission();
permission.setName(permName);
permission.setObject(objectName);
}
permission.setCanRead(row[0].equalsIgnoreCase("x"));
permission.setCanWrite(row[1].equalsIgnoreCase("x"));
permission.setCanCreate(row[2].equalsIgnoreCase("x"));
permission.setCanRemove(row[3].equalsIgnoreCase("x"));
permission.setCanExport(row[4].equalsIgnoreCase("x"));
if (newPermission) {
group.addPermission(permission);
}
}
public void updatePermission(Role role, String objectName, MetaField field, String[] row) {
String[] objectNames = objectName.split("\\.");
String permName = getPermissionName(field, objectNames[objectNames.length - 1], role.getName());
Permission permission =
permissionRepository.all().filter("self.name = ?1", permName).fetchOne();
boolean newPermission = false;
if (permission == null) {
newPermission = true;
permission = new Permission();
permission.setName(permName);
permission.setObject(objectName);
}
permission.setCanRead(row[0].equalsIgnoreCase("x"));
permission.setCanWrite(row[1].equalsIgnoreCase("x"));
permission.setCanCreate(row[2].equalsIgnoreCase("x"));
permission.setCanRemove(row[3].equalsIgnoreCase("x"));
permission.setCanExport(row[4].equalsIgnoreCase("x"));
permission.setCondition(row[5]);
permission.setConditionParams(row[6]);
if (newPermission) {
role.addPermission(permission);
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.auth.web;
import com.axelor.auth.db.IMessage;
import com.axelor.auth.db.PermissionAssistant;
import com.axelor.auth.db.repo.PermissionAssistantRepository;
import com.axelor.auth.service.PermissionAssistantService;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaField;
import com.axelor.meta.db.MetaModel;
import com.axelor.meta.db.repo.MetaModelRepository;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Singleton
public class PermissionAssistantController {
public void createFile(ActionRequest request, ActionResponse response) {
try {
Long permissionAssistantId = (Long) request.getContext().get("id");
Beans.get(PermissionAssistantService.class)
.createFile(Beans.get(PermissionAssistantRepository.class).find(permissionAssistantId));
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void importPermissions(ActionRequest request, ActionResponse response) {
try {
Long permissionAssistantId = (Long) request.getContext().get("id");
String errors =
Beans.get(PermissionAssistantService.class)
.importPermissions(
Beans.get(PermissionAssistantRepository.class).find(permissionAssistantId));
response.setValue("importDate", LocalDateTime.now());
response.setValue("log", errors);
if (errors.isEmpty()) {
response.setFlash(I18n.get(IMessage.IMPORT_OK));
} else {
response.setFlash(I18n.get(IMessage.ERR_IMPORT));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void fillObjects(ActionRequest request, ActionResponse response) {
try {
PermissionAssistant assistant = request.getContext().asType(PermissionAssistant.class);
MetaField metaField = assistant.getMetaField();
if (metaField != null
&& (assistant.getObjectSet() == null || assistant.getObjectSet().isEmpty())) {
List<MetaModel> models =
Beans.get(MetaModelRepository.class)
.all()
.filter(
"self.metaFields.relationship = 'ManyToOne'"
+ " and self.metaFields.typeName = ?1",
metaField.getTypeName())
.fetch();
Set<MetaModel> objectSet = new HashSet<>();
objectSet.addAll(models);
response.setValue("objectSet", objectSet);
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.csv.script;
import com.axelor.app.AppSettings;
import com.axelor.apps.base.db.App;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import java.io.File;
import java.nio.file.Path;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ImportApp {
private static final Logger LOG = LoggerFactory.getLogger(ImportApp.class);
@Inject private MetaFiles metaFiles;
public Object importApp(Object bean, Map<String, Object> values) {
assert bean instanceof App;
App app = (App) bean;
final Path path = (Path) values.get("__path__");
String fileName = (String) values.get("imagePath");
if (!Strings.isNullOrEmpty(fileName)) {
try {
final File image = path.resolve("img" + File.separator + fileName).toFile();
if (image.exists()) {
final MetaFile metaFile = metaFiles.upload(image);
app.setImage(metaFile);
}
} catch (Exception e) {
LOG.warn("Can't load image {} for app {}", fileName, app.getName());
}
}
if (app.getLanguageSelect() == null) {
String language = AppSettings.get().get("application.locale");
app.setLanguageSelect(language);
}
return app;
}
}

View File

@ -0,0 +1,401 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.meta.service;
import com.axelor.auth.db.Group;
import com.axelor.auth.db.IMessage;
import com.axelor.auth.db.Role;
import com.axelor.auth.db.repo.GroupRepository;
import com.axelor.auth.db.repo.RoleRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaGroupMenuAssistant;
import com.axelor.meta.db.MetaMenu;
import com.axelor.meta.db.repo.MetaGroupMenuAssistantRepository;
import com.axelor.meta.db.repo.MetaMenuRepository;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import org.apache.commons.io.output.FileWriterWithEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MetaGroupMenuAssistantService {
@Inject private MetaGroupMenuAssistantRepository menuAssistantRepository;
@Inject private GroupRepository groupRepository;
@Inject private MetaMenuRepository menuRepository;
@Inject private RoleRepository roleRepository;
@Inject private MetaFiles metaFiles;
private List<String> badGroups = new ArrayList<>();
private List<String> badRoles = new ArrayList<>();
private String errorLog = "";
private Set<MetaMenu> updatedMenus = Sets.newHashSet();
private ResourceBundle bundle;
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private String getFileName(MetaGroupMenuAssistant groupMenuAssistant) {
String userCode = groupMenuAssistant.getCreatedBy().getCode();
String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
String fileName = "GroupMenu" + "-" + userCode + "-" + dateString + ".csv";
return fileName;
}
private void setBundle(Locale locale) {
bundle = I18n.getBundle(locale);
}
private ResourceBundle getBundle() {
if (bundle == null) {
bundle = I18n.getBundle(new Locale("en"));
}
return bundle;
}
@Transactional(rollbackOn = {Exception.class})
public void createGroupMenuFile(MetaGroupMenuAssistant groupMenuAssistant) throws IOException {
setBundle(new Locale(groupMenuAssistant.getLanguage()));
File groupMenuFile = MetaFiles.createTempFile("MenuGroup", ".csv").toFile();
try {
List<String[]> rows = createHeader(groupMenuAssistant);
addMenuRows(groupMenuAssistant, rows);
addGroupAccess(rows);
CSVWriter csvWriter = new CSVWriter(new FileWriterWithEncoding(groupMenuFile, "utf-8"), ';');
csvWriter.writeAll(rows);
csvWriter.close();
groupMenuAssistant.setMetaFile(
metaFiles.upload(new FileInputStream(groupMenuFile), getFileName(groupMenuAssistant)));
menuAssistantRepository.save(groupMenuAssistant);
} catch (Exception e) {
TraceBackService.trace(e);
}
}
private List<String[]> createHeader(MetaGroupMenuAssistant groupMenuAssistant)
throws IOException {
MetaFile metaFile = groupMenuAssistant.getMetaFile();
List<String[]> rows = new ArrayList<String[]>();
if (metaFile != null) {
File csvFile = MetaFiles.getPath(metaFile).toFile();
logger.debug("File name: {}", csvFile.getAbsolutePath());
logger.debug("File length: {}", csvFile.length());
try (CSVReader csvReader =
new CSVReader(
new InputStreamReader(new FileInputStream(csvFile), StandardCharsets.UTF_8), ';')) {
rows = csvReader.readAll();
logger.debug("Rows size: {}", rows.size());
}
}
if (!rows.isEmpty()) {
rows.set(0, getGroupRow(rows.get(0), groupMenuAssistant));
} else {
rows.add(getGroupRow(null, groupMenuAssistant));
}
return rows;
}
private String[] getGroupRow(String[] row, MetaGroupMenuAssistant groupMenuAssistant)
throws IOException {
List<String> groupList = new ArrayList<String>();
if (row != null) {
groupList.addAll(Arrays.asList(row));
} else {
groupList.add(getBundle().getString("Name"));
groupList.add(getBundle().getString("Title"));
}
for (Group group : groupMenuAssistant.getGroupSet()) {
String code = group.getCode();
if (!groupList.contains(code)) {
groupList.add(code);
}
}
for (Role role : groupMenuAssistant.getRoleSet()) {
String name = role.getName();
if (!groupList.contains(name)) {
groupList.add(name);
}
}
return groupList.toArray(new String[groupList.size()]);
}
private void addMenuRows(MetaGroupMenuAssistant groupMenuAssistant, List<String[]> rows) {
String[] groupRow = rows.get(0);
rows.remove(0);
Set<String> names = new HashSet<>();
for (String[] line : rows) {
names.add(line[0]);
}
for (MetaMenu metaMenu : groupMenuAssistant.getMenuSet()) {
String name = metaMenu.getName();
if (names.contains(name)) {
continue;
}
String title = metaMenu.getTitle();
String translation = getBundle().getString(title);
if (!Strings.isNullOrEmpty(translation)) {
title = translation;
}
String[] menu = new String[groupRow.length];
menu[0] = name;
menu[1] = title;
rows.add(menu);
}
Collections.sort(
rows,
new Comparator<String[]>() {
@Override
public int compare(String[] first, String[] second) {
return first[0].compareTo(second[0]);
}
});
rows.add(0, groupRow);
}
private void addGroupAccess(List<String[]> rows) {
ListIterator<String[]> rowIter = rows.listIterator();
String[] header = rowIter.next();
while (rowIter.hasNext()) {
String[] row = rowIter.next();
MetaMenu menu =
menuRepository.all().filter("self.name = ?1", row[0]).order("-priority").fetchOne();
if (row.length < header.length) {
row = Arrays.copyOf(row, header.length);
rowIter.set(row);
}
for (int i = 2; i < header.length; i++) {
for (Group group : menu.getGroups()) {
if (header[i] != null && header[i].equals(group.getCode())) {
row[i] = "x";
}
}
for (Role role : menu.getRoles()) {
if (header[i] != null && header[i].equals(role.getName())) {
row[i] = "x";
}
}
}
}
}
private Map<String, Object> checkGroups(String[] groupRow) {
Map<String, Object> groupMap = new HashMap<String, Object>();
for (Integer glen = 2; glen < groupRow.length; glen++) {
Group group = groupRepository.all().filter("self.code = ?1", groupRow[glen]).fetchOne();
if (group == null) {
badGroups.add(groupRow[glen]);
} else {
groupMap.put(groupRow[glen], group);
}
}
return groupMap;
}
private Map<String, Role> checkRoles(String[] roleRow) {
Map<String, Role> roleMap = new HashMap<String, Role>();
for (Integer rlen = 2; rlen < roleRow.length; rlen++) {
Role role = roleRepository.all().filter("self.name = ?1", roleRow[rlen]).fetchOne();
if (role == null) {
badRoles.add(roleRow[rlen]);
} else {
roleMap.put(roleRow[rlen], role);
}
}
return roleMap;
}
public String importGroupMenu(MetaGroupMenuAssistant groupMenuAssistant) {
try {
setBundle(new Locale(groupMenuAssistant.getLanguage()));
MetaFile metaFile = groupMenuAssistant.getMetaFile();
File csvFile = MetaFiles.getPath(metaFile).toFile();
CSVReader csvReader =
new CSVReader(
new InputStreamReader(new FileInputStream(csvFile), StandardCharsets.UTF_8), ';');
String[] groupRow = csvReader.readNext();
if (groupRow == null || groupRow.length < 3) {
csvReader.close();
return I18n.get(IMessage.BAD_FILE);
}
Map<String, Object> groupMap = checkGroups(groupRow);
groupMap.putAll(checkRoles(groupRow));
badGroups.removeAll(groupMap.keySet());
badRoles.removeAll(groupMap.keySet());
if (!badGroups.isEmpty()) {
errorLog += "\n" + String.format(I18n.get(IMessage.NO_GROUP), badGroups);
}
if (!badRoles.isEmpty()) {
errorLog += "\n" + String.format(I18n.get(IMessage.NO_ROLE), badRoles);
}
Group admin = groupRepository.findByCode("admins");
for (String[] row : csvReader.readAll()) {
importMenus(row, groupRow, groupMap, admin);
}
csvReader.close();
saveMenus();
} catch (Exception e) {
TraceBackService.trace(e);
errorLog += "\n" + String.format(I18n.get(IMessage.ERR_IMPORT_WITH_MSG), e.getMessage());
}
return errorLog;
}
@Transactional
public void saveMenus() {
for (MetaMenu menu : updatedMenus) {
menuRepository.save(menu);
}
}
private void importMenus(
String[] row, String[] groupRow, Map<String, Object> groupMap, Group admin)
throws IOException {
List<MetaMenu> menus =
menuRepository.all().filter("self.name = ?1", row[0]).order("-priority").fetch();
if (menus.isEmpty()) {
errorLog += "\n" + String.format(I18n.get(IMessage.NO_MENU), row[0]);
return;
}
for (MetaMenu menu : menus) {
boolean noAccess = true;
for (Integer mIndex = 2; mIndex < row.length; mIndex++) {
String code = groupRow[mIndex];
Object object = groupMap.get(code);
Role role = null;
Group group = null;
if (object instanceof Group) {
group = (Group) object;
} else if (object instanceof Role) {
role = (Role) object;
}
if (row[mIndex].equalsIgnoreCase("x")) {
noAccess = false;
if (group != null) {
menu.addGroup(group);
}
if (role != null) {
menu.addRole(role);
}
updatedMenus.add(menu);
} else if (group != null && menu.getGroups().contains(group)) {
menu.removeGroup(group);
updatedMenus.add(menu);
} else if (role != null && menu.getRoles().contains(role)) {
menu.removeRole(role);
updatedMenus.add(menu);
}
}
if (noAccess && admin != null) {
menu.addGroup(admin);
}
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
package com.axelor.meta.web;
import com.axelor.auth.db.IMessage;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.repo.MetaGroupMenuAssistantRepository;
import com.axelor.meta.service.MetaGroupMenuAssistantService;
import com.axelor.rpc.ActionRequest;
import com.axelor.rpc.ActionResponse;
import com.google.inject.Singleton;
import java.time.LocalDateTime;
@Singleton
public class MetaGroupMenuAssistantController {
public void createGroupMenuFile(ActionRequest request, ActionResponse response) {
try {
Long groupMenuAssistantId = (Long) request.getContext().get("id");
Beans.get(MetaGroupMenuAssistantService.class)
.createGroupMenuFile(
Beans.get(MetaGroupMenuAssistantRepository.class).find(groupMenuAssistantId));
response.setReload(true);
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
public void importGroupMenu(ActionRequest request, ActionResponse response) {
try {
Long groupMenuAssistantId = (Long) request.getContext().get("id");
String errorLog =
Beans.get(MetaGroupMenuAssistantService.class)
.importGroupMenu(
Beans.get(MetaGroupMenuAssistantRepository.class).find(groupMenuAssistantId));
response.setValue("log", errorLog);
if (errorLog.isEmpty()) {
response.setFlash(I18n.get(IMessage.IMPORT_OK));
response.setValue("importDate", LocalDateTime.now());
} else {
response.setFlash(I18n.get(IMessage.ERR_IMPORT));
}
} catch (Exception e) {
TraceBackService.trace(response, e);
}
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
<input file="auth_permission.csv" separator=";" type="com.axelor.auth.db.Permission" search="self.name = :name" call="com.axelor.csv.script.ImportPermission:importPermissionToRole">
<bind to="canRead" eval="can_read == 'x' ? 'true' : 'false'"/>
<bind to="canWrite" eval="can_write == 'x' ? 'true' : 'false'"/>
<bind to="canCreate" eval="can_create == 'x' ? 'true' : 'false'"/>
<bind to="canRemove" eval="can_remove == 'x' ? 'true' : 'false'"/>
<bind to="canExport" eval="can_export == 'x' ? 'true' : 'false'"/>
</input>
<input file="meta_metaMenu.csv" separator=";" type="com.axelor.meta.db.MetaMenu" search="self.name = :name and self.module = 'axelor-admin'" update="true" />
</csv-inputs>

View File

@ -0,0 +1,6 @@
"name";"object";"can_read";"can_write";"can_create";"can_remove";"can_export";"condition";"conditionParams";"roleName"
"perm.auth.all";"com.axelor.auth.db.*";"x";"x";"x";"x";"x";;;"Admin"
"perm.meta.all";"com.axelor.meta.db.*";"x";"x";"x";"x";"x";;;"Admin"
"perm.mail.all";"com.axelor.mail.db.*";"x";"x";"x";"x";"x";;;"Admin"
"perm.dms.all";"com.axelor.dms.db.*";"x";"x";"x";"x";"x";;;"Admin"
"perm.team.all";"com.axelor.team.db.*";"x";"x";"x";"x";"x";;;"Admin"
1 name object can_read can_write can_create can_remove can_export condition conditionParams roleName
2 perm.auth.all com.axelor.auth.db.* x x x x x Admin
3 perm.meta.all com.axelor.meta.db.* x x x x x Admin
4 perm.mail.all com.axelor.mail.db.* x x x x x Admin
5 perm.dms.all com.axelor.dms.db.* x x x x x Admin
6 perm.team.all com.axelor.team.db.* x x x x x Admin

View File

@ -0,0 +1,7 @@
"name";"roles.name"
"admin-root";"Admin"
"admin-root-app-management";"Admin"
"admin-root-maintenance";"Admin"
"admin-root-object-data-config";"Admin"
"menu-auth-permission-assistant";"Admin"
"menu-meta-group-menu-assistant";"Admin"
1 name roles.name
2 admin-root Admin
3 admin-root-app-management Admin
4 admin-root-maintenance Admin
5 admin-root-object-data-config Admin
6 menu-auth-permission-assistant Admin
7 menu-meta-group-menu-assistant Admin

View File

@ -0,0 +1,15 @@
<?xml version="1.0" ?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db"/>
<entity name="AccessConfig" cacheable="true">
<string name="name" readonly="true"/>
<many-to-one name="app" ref="com.axelor.apps.base.db.App" title="App" />
<many-to-many name="roleSet" ref="com.axelor.auth.db.Role" title="Roles"/>
<unique-constraint columns="app,name"/>
</entity>
</domain-models>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db"/>
<entity name="App" lang="java" strategy="JOINED" cacheable="true">
<string name="name" title="Name" required="true" translatable="true"/>
<string name="code" title="Code" required="true" unique="true"/>
<string name="description" title="Description" large="true" translatable="true"/>
<string name="modules" title="Modules contains in the app" />
<many-to-many name="dependsOnSet" title="Depends on" ref="App"/>
<many-to-one name="image" ref="com.axelor.meta.db.MetaFile" title="Image"/>
<boolean name="initDataLoaded" title="Init data loaded" />
<boolean name="demoDataLoaded" title="Demo data loaded" />
<boolean name="isRolesImported" title="Roles imported" />
<boolean name="active" title="Installed" />
<integer name="sequence" title="Sequence" />
<integer name="installOrder" title="Install order" />
<string name="languageSelect" title="Language" selection="select.language" />
<one-to-many name="accessConfigList" ref="AccessConfig" title="Access config" mappedBy="app"/>
<finder-method name="findByCode" using="code" cacheable="true" />
<finder-method name="findByName" using="name" cacheable="true" />
</entity>
</domain-models>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db" />
<entity name="DataBackup">
<many-to-one name="backupMetaFile" ref="com.axelor.meta.db.MetaFile"
title="Backup File" />
<integer name="statusSelect" selection="data.backup.status.select"
title="Status" default="0" />
<many-to-one name="logMetaFile" ref="com.axelor.meta.db.MetaFile"
title="Log" />
<datetime name="backupDate" title="Backup On" />
<datetime name="restoreDate" title="Restored On" />
<integer name="fetchLimit" title="CSV Backup Fetch Limit"/>
<boolean name="isRelativeDate" title="Relative Date"/>
<boolean name="updateImportId" title="Update Import Id"/>
<extra-code>
<![CDATA[
// DATABACKUP STATUS
public static final int DATA_BACKUP_STATUS_DRAFT = 0;
public static final int DATA_BACKUP_STATUS_IN_PROGRESS = 1;
public static final int DATA_BACKUP_STATUS_CREATED = 2;
public static final int DATA_BACKUP_STATUS_RESTORE = 3;
public static final int DATA_BACKUP_STATUS_RESTORE_ERROR = 4;
]]>
</extra-code>
</entity>
</domain-models>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" ?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db"/>
<entity name="DataConfigLine" cacheable="true">
<![CDATA[
This object store the configuration lines for export of data
]]>
<many-to-one name="metaModel" title="Object" ref="com.axelor.meta.db.MetaModel"/>
<integer name="typeSelect" title="Query/path" selection="data.config.line.type.select" />
<string name="path" title="Query" />
<many-to-one name="metaFieldPath" ref="com.axelor.meta.db.MetaField" title="Path Field"/>
<many-to-many name="toExportMetaFieldSet" ref="com.axelor.meta.db.MetaField" title="Fields to export" />
<many-to-one name="objectDataConfig" ref="ObjectDataConfig" />
<integer name="resetPathSelect" title="Action to perform" selection="data.config.line.reset.path.select" />
<string name="record" title="Record" selection="object.data.config.model.select"/>
<long name="recordSelectId" />
<many-to-many name="toDeleteMetaFieldSet" ref="com.axelor.meta.db.MetaField" title="Fields to delete" />
<string name="tabName" title="Tab name" required="true"/>
<unique-constraint columns="objectDataConfig,tabName"/>
<extra-code>
<![CDATA[
public static final int TYPE_PATH = 0;
public static final int TYPE_QUERY = 1;
public static final int RESET_NONE = 0;
public static final int RESET_REPLACE = 1;
public static final int RESET_DELETE = 2;
]]>
</extra-code>
</entity>
</domain-models>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" ?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="meta" package="com.axelor.meta.db" />
<entity name="MetaGroupMenuAssistant" table="META_GROUP_MENU_ASSISTANT">
<many-to-many name="groupSet" ref="com.axelor.auth.db.Group" title="Groups"/>
<many-to-many name="roleSet" ref="com.axelor.auth.db.Role" title="Roles"/>
<many-to-many name="menuSet" ref="MetaMenu" title="Menus"/>
<many-to-one name="metaFile" ref="MetaFile" title="File" />
<datetime name="importDate" title="Import date"/>
<string name="language" title="Language" selection="select.language" default="en"/>
<string name="log" large="true" title="Log" />
</entity>
</domain-models>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="core" package="com.axelor.db" />
<entity name="Model">
<string name="importId" copy="false" hashKey="false" readonly="true" title="Import ID" unique="true" />
<string name="importOrigin" copy="false" hashKey="false" readonly="true" title="Imported from" />
</entity>
</domain-models>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" ?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db"/>
<entity name="ObjectDataConfig" cacheable="true">
<![CDATA[
This object store the configuration for export of data
]]>
<string name="title" title="Title" />
<string name="modelSelect" namecolumn="true" title="Model" selection="object.data.config.model.select" required="true"/>
<integer name="statusSelect" title="Status" selection="object.data.config.status.select" />
<one-to-many name="dataConfigLineList" ref="DataConfigLine" title="Lines" mappedBy="objectDataConfig"/>
<string name="comment" column="comment_val" title="Comment" large="true"/>
<extra-code>
<![CDATA[
// STATUS SELECT
public static final int STATUS_DRAFT = 1;
public static final int STATUS_VALIDATED = 2;
public static final int STATUS_CANCELLED = 3;
]]>
</extra-code>
</entity>
</domain-models>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" ?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db"/>
<entity name="ObjectDataConfigExport" cacheable="true">
<![CDATA[
This object store the detail of export data
]]>
<string name="modelSelect" title="Model" selection="object.data.config.model.select" required="true"/>
<long name="modelSelectId"/>
<string name="recordName" title="Record name"/>
<string name="exportFormatSelect" title="Export format" selection="object.data.export.format.select" />
<string name="langSelect" title="Language" selection="select.language" />
<many-to-one name="objectDataConfig" ref="com.axelor.apps.base.db.ObjectDataConfig" title="Object data config"/>
</entity>
</domain-models>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" ?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="auth" package="com.axelor.auth.db"/>
<entity name="PermissionAssistant" cacheable="true">
<![CDATA[
This object stores the permission assistant.
]]>
<datetime name="importDate" title="Import date" readonly="true" />
<integer name="typeSelect" title="Type" required="true"/>
<many-to-one name="metaField" title="User field" ref="com.axelor.meta.db.MetaField"/>
<many-to-one name="metaFile" ref="com.axelor.meta.db.MetaFile" title="File" />
<many-to-many name="roleSet" ref="Role" title="Roles"/>
<many-to-many name="groupSet" ref="Group" title="Groups"/>
<many-to-many name="objectSet" ref="com.axelor.meta.db.MetaModel" title="Objects"/>
<string name="language" title="Language" selection="select.language" default="en"/>
<string name="log" large="true" title="Log" />
<boolean name="fieldPermission" title="Field permission"/>
<extra-code>
<![CDATA[
public static final int TYPE_GROUPS = 1;
public static final int TYPE_ROLES = 2;
]]>
</extra-code>
</entity>
</domain-models>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" ?>
<domain-models xmlns="http://axelor.com/xml/ns/domain-models"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/domain-models http://axelor.com/xml/ns/domain-models/domain-models_5.2.xsd">
<module name="base" package="com.axelor.apps.base.db"/>
<entity name="UserAccessConfig" cacheable="true">
<many-to-one name="user" ref="com.axelor.auth.db.User" title="User" column="user_id"/>
<many-to-one name="app" ref="com.axelor.apps.base.db.App" title="App" />
<many-to-one name="accessConfig" ref="com.axelor.apps.base.db.AccessConfig" title="Access config" />
</entity>
</domain-models>

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config",,,
"Access config imported successfully",,,
"Action to perform",,,
"Anonymize",,,
"Anonymize data",,,
"App",,,
"Application Config",,,
"Apps",,,
"Apps installed successfully",,,
"Apps management",,,
"Apps refreshed successfully",,,
"Are you sure wants to anonymize data ?.",,,
"Are you sure wants to import demo data for '${name}'?",,,
"Are you sure wants to install '${name}'?",,,
"Are you sure wants to uninstall '${name}'?",,,
"Backup",,,
"Backup Created",,,
"Backup File",,,
"Backup On",,,
"Backup Restore Error",,,
"Backup Restored",,,
"Bad header row:",,,
"Bad import file",,,
"Bulk install",,,
"CSV",,,
"CSV Backup Fetch Limit",,,
"Cancelled",,,
"Code",,,
"Comment",,,
"Condition",,,
"ConditionParams",,,
"Configure",,,
"Create",,,
"Create Backup",,,
"Create file",,,
"Data",,,
"Data config line",,,
"Delete",,,
"Demo data loaded",,,
"Demo data loaded successfully",,,
"Depends on",,,
"Description",,,
"Details",,,
"Draft",,,
"Error in import. Please check log.",,,
"Error in import: %s. Please check the server log",,,
"Error in refreshing app",,,
"Excel",,,
"Export",,,
"Export file",,,
"Export format",,,
"Exports",,,
"Field",,,
"Field permission",,,
"Fields to delete",,,
"Fields to export",,,
"File",,,
"Group Menu Assistant",,,
"Groups",,,
"Groups not found: %s",,,
"Hide If",,,
"Image",,,
"Import",,,
"Import ID",,,
"Import access config",,,
"Import completed successfully",,,
"Import date",,,
"Import demo data",,,
"Import permissions",,,
"Import roles",,,
"Imported from",,,
"In Progress",,,
"Init data loaded",,,
"Install",,,
"Install order",,,
"Installed",,,
"Language",,,
"Lines",,,
"Log",,,
"Menu not found: %s",,,
"Menus",,,
"Meta Permissions",,,
"Model",,,
"Model select",,,
"Modules contains in the app",,,
"Name",,,
"No application language set. Please set 'application.locale' property.",,,
"No configuration required",,,
"No header row found",,,
"No record found for: %s",,,
"None",,,
"Object",,,
"Object data anonymize",,,
"Object data config",,,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s",,,
"Objects",,,
"Path",,,
"Path Field",,,
"Permission Assistant",,,
"Query",,,
"Query/path",,,
"Read",,,
"Readonly If",,,
"Record",,,
"Record name",,,
"Record select",,,
"Refresh Apps",,,
"Relative Date",,,
"Replace",,,
"Restore",,,
"Restore Backup",,,
"Restored On",,,
"Return to draft status",,,
"Roles",,,
"Roles imported",,,
"Roles imported successfully",,,
"Roles not found: %s",,,
"Select all",,,
"Sequence",,,
"Status",,,
"Tab name",,,
"Technical maintenance",,,
"This app is used by %s. Please deactivate them before continue.",,,
"Title",,,
"Type",,,
"Uninstall",,,
"Update Import Id",,,
"User",,,
"User access config",,,
"User field",,,
"Validated",,,
"Write",,,
"{{$fmt('name')}}",,,
1 key message comment context
2 Access config
3 Access config imported successfully
4 Action to perform
5 Anonymize
6 Anonymize data
7 App
8 Application Config
9 Apps
10 Apps installed successfully
11 Apps management
12 Apps refreshed successfully
13 Are you sure wants to anonymize data ?.
14 Are you sure wants to import demo data for '${name}'?
15 Are you sure wants to install '${name}'?
16 Are you sure wants to uninstall '${name}'?
17 Backup
18 Backup Created
19 Backup File
20 Backup On
21 Backup Restore Error
22 Backup Restored
23 Bad header row:
24 Bad import file
25 Bulk install
26 CSV
27 CSV Backup Fetch Limit
28 Cancelled
29 Code
30 Comment
31 Condition
32 ConditionParams
33 Configure
34 Create
35 Create Backup
36 Create file
37 Data
38 Data config line
39 Delete
40 Demo data loaded
41 Demo data loaded successfully
42 Depends on
43 Description
44 Details
45 Draft
46 Error in import. Please check log.
47 Error in import: %s. Please check the server log
48 Error in refreshing app
49 Excel
50 Export
51 Export file
52 Export format
53 Exports
54 Field
55 Field permission
56 Fields to delete
57 Fields to export
58 File
59 Group Menu Assistant
60 Groups
61 Groups not found: %s
62 Hide If
63 Image
64 Import
65 Import ID
66 Import access config
67 Import completed successfully
68 Import date
69 Import demo data
70 Import permissions
71 Import roles
72 Imported from
73 In Progress
74 Init data loaded
75 Install
76 Install order
77 Installed
78 Language
79 Lines
80 Log
81 Menu not found: %s
82 Menus
83 Meta Permissions
84 Model
85 Model select
86 Modules contains in the app
87 Name
88 No application language set. Please set 'application.locale' property.
89 No configuration required
90 No header row found
91 No record found for: %s
92 None
93 Object
94 Object data anonymize
95 Object data config
96 Object data config export
97 Object data config exports
98 Object not found: %s
99 Objects
100 Path
101 Path Field
102 Permission Assistant
103 Query
104 Query/path
105 Read
106 Readonly If
107 Record
108 Record name
109 Record select
110 Refresh Apps
111 Relative Date
112 Replace
113 Restore
114 Restore Backup
115 Restored On
116 Return to draft status
117 Roles
118 Roles imported
119 Roles imported successfully
120 Roles not found: %s
121 Select all
122 Sequence
123 Status
124 Tab name
125 Technical maintenance
126 This app is used by %s. Please deactivate them before continue.
127 Title
128 Type
129 Uninstall
130 Update Import Id
131 User
132 User access config
133 User field
134 Validated
135 Write
136 {{$fmt('name')}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config","Zugriff auf die Konfiguration",,
"Access config imported successfully","Zugriffskonfiguration erfolgreich importiert",,
"Action to perform",,,
"Anonymize",,,
"Anonymize data","Daten anonymisieren",,
"App","App",,
"Application Config","Anwendungskonfiguration",,
"Apps","Apps",,
"Apps installed successfully","Apps erfolgreich installiert",,
"Apps management","Apps-Verwaltung",,
"Apps refreshed successfully","Apps erfolgreich aktualisiert",,
"Are you sure wants to anonymize data ?.","Sind Sie sicher, dass Sie Daten anonymisieren möchten?.",,
"Are you sure wants to import demo data for '${name}'?","Sind Sie sicher, dass Sie Demo-Daten für '${name}' importieren möchten?",,
"Are you sure wants to install '${name}'?","Sind Sie sicher, dass Sie '${name}' installieren möchten?",,
"Are you sure wants to uninstall '${name}'?","Sind Sie sicher, dass Sie '${name}' deinstallieren möchten?",,
"Backup","Sicherung",,
"Backup Created","Sicherung erstellt",,
"Backup File","Sicherungsdatei",,
"Backup On","Sicherung ein",,
"Backup Restore Error","Fehler bei der Wiederherstellung des Backups",,
"Backup Restored","Sicherung wiederhergestellt",,
"Bad header row:","Schlechte Kopfzeile:",,
"Bad import file","Fehlerhafte Importdatei",,
"Bulk install","Masseninstallation",,
"CSV","CSV",,
"CSV Backup Fetch Limit","CSV-Backup Fetch-Limit für das Abrufen von CSV-Backups",,
"Cancelled",,,
"Code","Code",,
"Comment",,,
"Condition","Bedingung",,
"ConditionParams","ConditionParams",,
"Configure","Konfigurieren",,
"Create","Erstellen",,
"Create Backup","Backup erstellen",,
"Create file","Datei erstellen",,
"Data","Daten",,
"Data config line","Datenkonfigurationszeile",,
"Delete","Löschen",,
"Demo data loaded","Demo-Daten geladen",,
"Demo data loaded successfully","Demo-Daten erfolgreich geladen",,
"Depends on","Abhängig von",,
"Description","Beschreibung",,
"Details","Details",,
"Draft","Entwurf",,
"Error in import. Please check log.","Fehler beim Import. Bitte überprüfen Sie das Protokoll.",,
"Error in import: %s. Please check the server log","Fehler beim Import: %s. Bitte überprüfen Sie das Serverprotokoll.",,
"Error in refreshing app","Fehler bei der Aktualisierung der App",,
"Excel","Excel",,
"Export","Exportieren",,
"Export file","Exportdatei",,
"Export format","Exportformat",,
"Exports",,,
"Field","Feld",,
"Field permission","Feldberechtigung",,
"Fields to delete","Zu löschende Felder",,
"Fields to export",,,
"File","Datei",,
"Group Menu Assistant","Gruppenmenü-Assistent",,
"Groups","Gruppen",,
"Groups not found: %s","Gruppen nicht gefunden: %s",,
"Hide If","Ausblenden wenn",,
"Image","Bild",,
"Import","Importieren",,
"Import ID","Import-ID",,
"Import access config","Zugangskonfiguration importieren",,
"Import completed successfully","Import erfolgreich abgeschlossen",,
"Import date","Importdatum",,
"Import demo data","Demo-Daten importieren",,
"Import permissions","Importberechtigungen",,
"Import roles","Rollen importieren",,
"Imported from","Importiert aus",,
"In Progress","In Bearbeitung",,
"Init data loaded","Init-Daten geladen",,
"Install","Installieren",,
"Install order","Installationsreihenfolge",,
"Installed","Installiert",,
"Language","Sprache",,
"Lines","Linien",,
"Log","Protokoll",,
"Menu not found: %s","Menü nicht gefunden: %s",,
"Menus","Menüs",,
"Meta Permissions",,,
"Model","Modell",,
"Model select",,,
"Modules contains in the app","Module enthält in der App",,
"Name","Name",,
"No application language set. Please set 'application.locale' property.","Keine Anwendungssprache eingestellt. Bitte setzen Sie die Eigenschaft'application.locale'.",,
"No configuration required","Keine Konfiguration erforderlich",,
"No header row found","Keine Kopfzeile gefunden",,
"No record found for: %s","Kein Datensatz gefunden für: %s",,
"None","Keine",,
"Object","Objekt",,
"Object data anonymize",,,
"Object data config","Objektdaten-Konfiguration",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Objekt nicht gefunden: %s",,
"Objects","Objekte",,
"Path","Pfad",,
"Path Field",,,
"Permission Assistant","Berechtigungsassistent",,
"Query","Anfrage",,
"Query/path","Abfrage/Pfad",,
"Read","Lesen",,
"Readonly If","Nur lesbar wenn",,
"Record","Aufzeichnung",,
"Record name",,,
"Record select",,,
"Refresh Apps","Apps aktualisieren",,
"Relative Date",,,
"Replace","Ersetzen",,
"Restore","Wiederherstellen",,
"Restore Backup","Backup wiederherstellen",,
"Restored On","Wiederhergestellt am",,
"Return to draft status","Zurück zum Entwurfsstatus",,
"Roles","Rollen",,
"Roles imported","Importierte Rollen",,
"Roles imported successfully","Rollen erfolgreich importiert",,
"Roles not found: %s","Rollen nicht gefunden: %s",,
"Select all","Alle auswählen",,
"Sequence","Sequenz",,
"Status","Status",,
"Tab name","Registerkartenname",,
"Technical maintenance","Technische Wartung",,
"This app is used by %s. Please deactivate them before continue.","Diese App wird von %s verwendet. Bitte deaktivieren Sie diese, bevor Sie fortfahren.",,
"Title","Titel",,
"Type","Typ",,
"Uninstall","Deinstallation",,
"Update Import Id",,,
"User","Benutzer",,
"User access config","Benutzerzugriffskonfiguration",,
"User field","Benutzerfeld",,
"Validated",,,
"Write","Schreiben",,
"{{$fmt('name')}}","{{$fmt('name')}}}}",,
1 key message comment context
2 Access config Zugriff auf die Konfiguration
3 Access config imported successfully Zugriffskonfiguration erfolgreich importiert
4 Action to perform
5 Anonymize
6 Anonymize data Daten anonymisieren
7 App App
8 Application Config Anwendungskonfiguration
9 Apps Apps
10 Apps installed successfully Apps erfolgreich installiert
11 Apps management Apps-Verwaltung
12 Apps refreshed successfully Apps erfolgreich aktualisiert
13 Are you sure wants to anonymize data ?. Sind Sie sicher, dass Sie Daten anonymisieren möchten?.
14 Are you sure wants to import demo data for '${name}'? Sind Sie sicher, dass Sie Demo-Daten für '${name}' importieren möchten?
15 Are you sure wants to install '${name}'? Sind Sie sicher, dass Sie '${name}' installieren möchten?
16 Are you sure wants to uninstall '${name}'? Sind Sie sicher, dass Sie '${name}' deinstallieren möchten?
17 Backup Sicherung
18 Backup Created Sicherung erstellt
19 Backup File Sicherungsdatei
20 Backup On Sicherung ein
21 Backup Restore Error Fehler bei der Wiederherstellung des Backups
22 Backup Restored Sicherung wiederhergestellt
23 Bad header row: Schlechte Kopfzeile:
24 Bad import file Fehlerhafte Importdatei
25 Bulk install Masseninstallation
26 CSV CSV
27 CSV Backup Fetch Limit CSV-Backup Fetch-Limit für das Abrufen von CSV-Backups
28 Cancelled
29 Code Code
30 Comment
31 Condition Bedingung
32 ConditionParams ConditionParams
33 Configure Konfigurieren
34 Create Erstellen
35 Create Backup Backup erstellen
36 Create file Datei erstellen
37 Data Daten
38 Data config line Datenkonfigurationszeile
39 Delete Löschen
40 Demo data loaded Demo-Daten geladen
41 Demo data loaded successfully Demo-Daten erfolgreich geladen
42 Depends on Abhängig von
43 Description Beschreibung
44 Details Details
45 Draft Entwurf
46 Error in import. Please check log. Fehler beim Import. Bitte überprüfen Sie das Protokoll.
47 Error in import: %s. Please check the server log Fehler beim Import: %s. Bitte überprüfen Sie das Serverprotokoll.
48 Error in refreshing app Fehler bei der Aktualisierung der App
49 Excel Excel
50 Export Exportieren
51 Export file Exportdatei
52 Export format Exportformat
53 Exports
54 Field Feld
55 Field permission Feldberechtigung
56 Fields to delete Zu löschende Felder
57 Fields to export
58 File Datei
59 Group Menu Assistant Gruppenmenü-Assistent
60 Groups Gruppen
61 Groups not found: %s Gruppen nicht gefunden: %s
62 Hide If Ausblenden wenn
63 Image Bild
64 Import Importieren
65 Import ID Import-ID
66 Import access config Zugangskonfiguration importieren
67 Import completed successfully Import erfolgreich abgeschlossen
68 Import date Importdatum
69 Import demo data Demo-Daten importieren
70 Import permissions Importberechtigungen
71 Import roles Rollen importieren
72 Imported from Importiert aus
73 In Progress In Bearbeitung
74 Init data loaded Init-Daten geladen
75 Install Installieren
76 Install order Installationsreihenfolge
77 Installed Installiert
78 Language Sprache
79 Lines Linien
80 Log Protokoll
81 Menu not found: %s Menü nicht gefunden: %s
82 Menus Menüs
83 Meta Permissions
84 Model Modell
85 Model select
86 Modules contains in the app Module enthält in der App
87 Name Name
88 No application language set. Please set 'application.locale' property. Keine Anwendungssprache eingestellt. Bitte setzen Sie die Eigenschaft'application.locale'.
89 No configuration required Keine Konfiguration erforderlich
90 No header row found Keine Kopfzeile gefunden
91 No record found for: %s Kein Datensatz gefunden für: %s
92 None Keine
93 Object Objekt
94 Object data anonymize
95 Object data config Objektdaten-Konfiguration
96 Object data config export
97 Object data config exports
98 Object not found: %s Objekt nicht gefunden: %s
99 Objects Objekte
100 Path Pfad
101 Path Field
102 Permission Assistant Berechtigungsassistent
103 Query Anfrage
104 Query/path Abfrage/Pfad
105 Read Lesen
106 Readonly If Nur lesbar wenn
107 Record Aufzeichnung
108 Record name
109 Record select
110 Refresh Apps Apps aktualisieren
111 Relative Date
112 Replace Ersetzen
113 Restore Wiederherstellen
114 Restore Backup Backup wiederherstellen
115 Restored On Wiederhergestellt am
116 Return to draft status Zurück zum Entwurfsstatus
117 Roles Rollen
118 Roles imported Importierte Rollen
119 Roles imported successfully Rollen erfolgreich importiert
120 Roles not found: %s Rollen nicht gefunden: %s
121 Select all Alle auswählen
122 Sequence Sequenz
123 Status Status
124 Tab name Registerkartenname
125 Technical maintenance Technische Wartung
126 This app is used by %s. Please deactivate them before continue. Diese App wird von %s verwendet. Bitte deaktivieren Sie diese, bevor Sie fortfahren.
127 Title Titel
128 Type Typ
129 Uninstall Deinstallation
130 Update Import Id
131 User Benutzer
132 User access config Benutzerzugriffskonfiguration
133 User field Benutzerfeld
134 Validated
135 Write Schreiben
136 {{$fmt('name')}} {{$fmt('name')}}}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config",,,
"Access config imported successfully",,,
"Action to perform",,,
"Anonymize",,,
"Anonymize data",,,
"App",,,
"Application Config",,,
"Apps",,,
"Apps installed successfully",,,
"Apps management",,,
"Apps refreshed successfully",,,
"Are you sure wants to anonymize data ?.",,,
"Are you sure wants to import demo data for '${name}'?",,,
"Are you sure wants to install '${name}'?",,,
"Are you sure wants to uninstall '${name}'?",,,
"Backup",,,
"Backup Created",,,
"Backup File",,,
"Backup On",,,
"Backup Restore Error",,,
"Backup Restored",,,
"Bad header row:",,,
"Bad import file",,,
"Bulk install",,,
"CSV",,,
"CSV Backup Fetch Limit",,,
"Cancelled",,,
"Code",,,
"Comment",,,
"Condition",,,
"ConditionParams",,,
"Configure",,,
"Create",,,
"Create Backup",,,
"Create file",,,
"Data",,,
"Data config line",,,
"Delete",,,
"Demo data loaded",,,
"Demo data loaded successfully",,,
"Depends on",,,
"Description",,,
"Details",,,
"Draft",,,
"Error in import. Please check log.",,,
"Error in import: %s. Please check the server log",,,
"Error in refreshing app",,,
"Excel",,,
"Export",,,
"Export file",,,
"Export format",,,
"Exports",,,
"Field",,,
"Field permission",,,
"Fields to delete",,,
"Fields to export",,,
"File",,,
"Group Menu Assistant",,,
"Groups",,,
"Groups not found: %s",,,
"Hide If",,,
"Image",,,
"Import",,,
"Import ID",,,
"Import access config",,,
"Import completed successfully",,,
"Import date",,,
"Import demo data",,,
"Import permissions",,,
"Import roles",,,
"Imported from",,,
"In Progress",,,
"Init data loaded",,,
"Install",,,
"Install order",,,
"Installed",,,
"Language",,,
"Lines",,,
"Log",,,
"Menu not found: %s",,,
"Menus",,,
"Meta Permissions",,,
"Model",,,
"Model select",,,
"Modules contains in the app",,,
"Name",,,
"No application language set. Please set 'application.locale' property.",,,
"No configuration required",,,
"No header row found",,,
"No record found for: %s",,,
"None",,,
"Object",,,
"Object data anonymize",,,
"Object data config",,,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s",,,
"Objects",,,
"Path",,,
"Path Field",,,
"Permission Assistant",,,
"Query",,,
"Query/path",,,
"Read",,,
"Readonly If",,,
"Record",,,
"Record name",,,
"Record select",,,
"Refresh Apps",,,
"Relative Date",,,
"Replace",,,
"Restore",,,
"Restore Backup",,,
"Restored On",,,
"Return to draft status",,,
"Roles",,,
"Roles imported",,,
"Roles imported successfully",,,
"Roles not found: %s",,,
"Select all",,,
"Sequence",,,
"Status",,,
"Tab name",,,
"Technical maintenance",,,
"This app is used by %s. Please deactivate them before continue.",,,
"Title",,,
"Type",,,
"Uninstall",,,
"Update Import Id",,,
"User",,,
"User access config",,,
"User field",,,
"Validated",,,
"Write",,,
"{{$fmt('name')}}",,,
1 key message comment context
2 Access config
3 Access config imported successfully
4 Action to perform
5 Anonymize
6 Anonymize data
7 App
8 Application Config
9 Apps
10 Apps installed successfully
11 Apps management
12 Apps refreshed successfully
13 Are you sure wants to anonymize data ?.
14 Are you sure wants to import demo data for '${name}'?
15 Are you sure wants to install '${name}'?
16 Are you sure wants to uninstall '${name}'?
17 Backup
18 Backup Created
19 Backup File
20 Backup On
21 Backup Restore Error
22 Backup Restored
23 Bad header row:
24 Bad import file
25 Bulk install
26 CSV
27 CSV Backup Fetch Limit
28 Cancelled
29 Code
30 Comment
31 Condition
32 ConditionParams
33 Configure
34 Create
35 Create Backup
36 Create file
37 Data
38 Data config line
39 Delete
40 Demo data loaded
41 Demo data loaded successfully
42 Depends on
43 Description
44 Details
45 Draft
46 Error in import. Please check log.
47 Error in import: %s. Please check the server log
48 Error in refreshing app
49 Excel
50 Export
51 Export file
52 Export format
53 Exports
54 Field
55 Field permission
56 Fields to delete
57 Fields to export
58 File
59 Group Menu Assistant
60 Groups
61 Groups not found: %s
62 Hide If
63 Image
64 Import
65 Import ID
66 Import access config
67 Import completed successfully
68 Import date
69 Import demo data
70 Import permissions
71 Import roles
72 Imported from
73 In Progress
74 Init data loaded
75 Install
76 Install order
77 Installed
78 Language
79 Lines
80 Log
81 Menu not found: %s
82 Menus
83 Meta Permissions
84 Model
85 Model select
86 Modules contains in the app
87 Name
88 No application language set. Please set 'application.locale' property.
89 No configuration required
90 No header row found
91 No record found for: %s
92 None
93 Object
94 Object data anonymize
95 Object data config
96 Object data config export
97 Object data config exports
98 Object not found: %s
99 Objects
100 Path
101 Path Field
102 Permission Assistant
103 Query
104 Query/path
105 Read
106 Readonly If
107 Record
108 Record name
109 Record select
110 Refresh Apps
111 Relative Date
112 Replace
113 Restore
114 Restore Backup
115 Restored On
116 Return to draft status
117 Roles
118 Roles imported
119 Roles imported successfully
120 Roles not found: %s
121 Select all
122 Sequence
123 Status
124 Tab name
125 Technical maintenance
126 This app is used by %s. Please deactivate them before continue.
127 Title
128 Type
129 Uninstall
130 Update Import Id
131 User
132 User access config
133 User field
134 Validated
135 Write
136 {{$fmt('name')}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config","Acceso config",,
"Access config imported successfully","Configuración de acceso importada con éxito",,
"Action to perform",,,
"Anonymize",,,
"Anonymize data","Anonimizar datos",,
"App","Aplicación",,
"Application Config","Configuración de la aplicación",,
"Apps","Aplicaciones",,
"Apps installed successfully","Aplicaciones instaladas con éxito",,
"Apps management","Gestión de aplicaciones",,
"Apps refreshed successfully","Aplicaciones actualizadas con éxito",,
"Are you sure wants to anonymize data ?.","¿Está seguro de que quiere anonimizar los datos?",,
"Are you sure wants to import demo data for '${name}'?","¿Está seguro de que desea importar datos de demostración para '${name}'?",,
"Are you sure wants to install '${name}'?","¿Estás seguro de que quieres instalar '${name}'?",,
"Are you sure wants to uninstall '${name}'?","¿Estás seguro de que quieres desinstalar '${name}'?",,
"Backup","Copia de seguridad",,
"Backup Created","Copia de seguridad creada",,
"Backup File","Archivo de copia de seguridad",,
"Backup On","Copia de seguridad activada",,
"Backup Restore Error","Error de restauración de copia de seguridad",,
"Backup Restored","Copia de seguridad restaurada",,
"Bad header row:","Fila de encabezado incorrecta:",,
"Bad import file","Archivo de importación erróneo",,
"Bulk install","Instalación a granel",,
"CSV","CSV",,
"CSV Backup Fetch Limit","Límite de obtención de copias de seguridad CSV",,
"Cancelled",,,
"Code","Código",,
"Comment",,,
"Condition","Condición",,
"ConditionParams","ConditionParams",,
"Configure","Configurar",,
"Create","Crear",,
"Create Backup","Crear copia de seguridad",,
"Create file","Crear fichero",,
"Data","Datos",,
"Data config line","Línea de configuración de datos",,
"Delete","Borrar",,
"Demo data loaded","Datos de demostración cargados",,
"Demo data loaded successfully","Datos de demostración cargados con éxito",,
"Depends on","Depende de",,
"Description","Descripción",,
"Details","Detalles",,
"Draft","Proyecto de",,
"Error in import. Please check log.","Error en la importación. Por favor, compruebe el registro.",,
"Error in import: %s. Please check the server log","Error en la importación: %s. Por favor, compruebe el registro del servidor",,
"Error in refreshing app","Error en la actualización de la aplicación",,
"Excel","Excel",,
"Export","Exportación",,
"Export file","Fichero de exportación",,
"Export format","Formato de exportación",,
"Exports",,,
"Field","Campo",,
"Field permission","Permiso de campo",,
"Fields to delete","Campos a borrar",,
"Fields to export",,,
"File","Archivo",,
"Group Menu Assistant","Asistente de menú de grupo",,
"Groups","Grupos",,
"Groups not found: %s","Grupos no encontrados: %s",,
"Hide If","Ocultar si",,
"Image","Imagen",,
"Import","Importar",,
"Import ID","ID de importación",,
"Import access config","Configuración de acceso de importación",,
"Import completed successfully","Importación completada con éxito",,
"Import date","Fecha de importación",,
"Import demo data","Importar datos de demostración",,
"Import permissions","Permisos de importación",,
"Import roles","Roles de importación",,
"Imported from","Importado de",,
"In Progress","En progreso",,
"Init data loaded","Datos de inicio cargados",,
"Install","Instalar",,
"Install order","Orden de instalación",,
"Installed","Instalado",,
"Language","Idioma",,
"Lines","Líneas",,
"Log","Bitácora",,
"Menu not found: %s","Menú no encontrado: %s",,
"Menus","Menús",,
"Meta Permissions",,,
"Model","Modelo",,
"Model select",,,
"Modules contains in the app","Los módulos contienen en la aplicación",,
"Name","Nombre",,
"No application language set. Please set 'application.locale' property.","No se ha configurado ningún idioma de aplicación. Por favor, establezca la propiedad'application.locale'.",,
"No configuration required","No requiere configuración",,
"No header row found","No se ha encontrado ninguna línea de encabezado",,
"No record found for: %s","No se han encontrado registros de: %s",,
"None","Ninguno",,
"Object","Objeto",,
"Object data anonymize",,,
"Object data config","Datos del objeto config",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Objeto no encontrado: %s",,
"Objects","Objetos",,
"Path","Camino",,
"Path Field",,,
"Permission Assistant","Asistente de Permisos",,
"Query","Consulta",,
"Query/path","Consulta/ruta",,
"Read","Leer",,
"Readonly If","Sólo lectura si",,
"Record","Grabar",,
"Record name",,,
"Record select",,,
"Refresh Apps","Actualizar aplicaciones",,
"Relative Date",,,
"Replace","Reemplazar",,
"Restore","Restaurar",,
"Restore Backup","Restaurar copia de seguridad",,
"Restored On","Restaurado el",,
"Return to draft status","Volver al estado de borrador",,
"Roles","Roles",,
"Roles imported","Roles importados",,
"Roles imported successfully","Roles importados con éxito",,
"Roles not found: %s","Roles no encontrados: %s",,
"Select all","Seleccionar todo",,
"Sequence","Secuencia",,
"Status","Estado",,
"Tab name","Nombre de ficha",,
"Technical maintenance","Mantenimiento técnico",,
"This app is used by %s. Please deactivate them before continue.","Esta aplicación es utilizada por %s. Por favor, desactívelas antes de continuar.",,
"Title","Título",,
"Type","Tipo",,
"Uninstall","Desinstalar",,
"Update Import Id",,,
"User","Usuario",,
"User access config","Acceso de usuario config",,
"User field","Campo de usuario",,
"Validated",,,
"Write","Escribir",,
"{{$fmt('name')}}","{{$fmt('nombre')}}",,
1 key message comment context
2 Access config Acceso config
3 Access config imported successfully Configuración de acceso importada con éxito
4 Action to perform
5 Anonymize
6 Anonymize data Anonimizar datos
7 App Aplicación
8 Application Config Configuración de la aplicación
9 Apps Aplicaciones
10 Apps installed successfully Aplicaciones instaladas con éxito
11 Apps management Gestión de aplicaciones
12 Apps refreshed successfully Aplicaciones actualizadas con éxito
13 Are you sure wants to anonymize data ?. ¿Está seguro de que quiere anonimizar los datos?
14 Are you sure wants to import demo data for '${name}'? ¿Está seguro de que desea importar datos de demostración para '${name}'?
15 Are you sure wants to install '${name}'? ¿Estás seguro de que quieres instalar '${name}'?
16 Are you sure wants to uninstall '${name}'? ¿Estás seguro de que quieres desinstalar '${name}'?
17 Backup Copia de seguridad
18 Backup Created Copia de seguridad creada
19 Backup File Archivo de copia de seguridad
20 Backup On Copia de seguridad activada
21 Backup Restore Error Error de restauración de copia de seguridad
22 Backup Restored Copia de seguridad restaurada
23 Bad header row: Fila de encabezado incorrecta:
24 Bad import file Archivo de importación erróneo
25 Bulk install Instalación a granel
26 CSV CSV
27 CSV Backup Fetch Limit Límite de obtención de copias de seguridad CSV
28 Cancelled
29 Code Código
30 Comment
31 Condition Condición
32 ConditionParams ConditionParams
33 Configure Configurar
34 Create Crear
35 Create Backup Crear copia de seguridad
36 Create file Crear fichero
37 Data Datos
38 Data config line Línea de configuración de datos
39 Delete Borrar
40 Demo data loaded Datos de demostración cargados
41 Demo data loaded successfully Datos de demostración cargados con éxito
42 Depends on Depende de
43 Description Descripción
44 Details Detalles
45 Draft Proyecto de
46 Error in import. Please check log. Error en la importación. Por favor, compruebe el registro.
47 Error in import: %s. Please check the server log Error en la importación: %s. Por favor, compruebe el registro del servidor
48 Error in refreshing app Error en la actualización de la aplicación
49 Excel Excel
50 Export Exportación
51 Export file Fichero de exportación
52 Export format Formato de exportación
53 Exports
54 Field Campo
55 Field permission Permiso de campo
56 Fields to delete Campos a borrar
57 Fields to export
58 File Archivo
59 Group Menu Assistant Asistente de menú de grupo
60 Groups Grupos
61 Groups not found: %s Grupos no encontrados: %s
62 Hide If Ocultar si
63 Image Imagen
64 Import Importar
65 Import ID ID de importación
66 Import access config Configuración de acceso de importación
67 Import completed successfully Importación completada con éxito
68 Import date Fecha de importación
69 Import demo data Importar datos de demostración
70 Import permissions Permisos de importación
71 Import roles Roles de importación
72 Imported from Importado de
73 In Progress En progreso
74 Init data loaded Datos de inicio cargados
75 Install Instalar
76 Install order Orden de instalación
77 Installed Instalado
78 Language Idioma
79 Lines Líneas
80 Log Bitácora
81 Menu not found: %s Menú no encontrado: %s
82 Menus Menús
83 Meta Permissions
84 Model Modelo
85 Model select
86 Modules contains in the app Los módulos contienen en la aplicación
87 Name Nombre
88 No application language set. Please set 'application.locale' property. No se ha configurado ningún idioma de aplicación. Por favor, establezca la propiedad'application.locale'.
89 No configuration required No requiere configuración
90 No header row found No se ha encontrado ninguna línea de encabezado
91 No record found for: %s No se han encontrado registros de: %s
92 None Ninguno
93 Object Objeto
94 Object data anonymize
95 Object data config Datos del objeto config
96 Object data config export
97 Object data config exports
98 Object not found: %s Objeto no encontrado: %s
99 Objects Objetos
100 Path Camino
101 Path Field
102 Permission Assistant Asistente de Permisos
103 Query Consulta
104 Query/path Consulta/ruta
105 Read Leer
106 Readonly If Sólo lectura si
107 Record Grabar
108 Record name
109 Record select
110 Refresh Apps Actualizar aplicaciones
111 Relative Date
112 Replace Reemplazar
113 Restore Restaurar
114 Restore Backup Restaurar copia de seguridad
115 Restored On Restaurado el
116 Return to draft status Volver al estado de borrador
117 Roles Roles
118 Roles imported Roles importados
119 Roles imported successfully Roles importados con éxito
120 Roles not found: %s Roles no encontrados: %s
121 Select all Seleccionar todo
122 Sequence Secuencia
123 Status Estado
124 Tab name Nombre de ficha
125 Technical maintenance Mantenimiento técnico
126 This app is used by %s. Please deactivate them before continue. Esta aplicación es utilizada por %s. Por favor, desactívelas antes de continuar.
127 Title Título
128 Type Tipo
129 Uninstall Desinstalar
130 Update Import Id
131 User Usuario
132 User access config Acceso de usuario config
133 User field Campo de usuario
134 Validated
135 Write Escribir
136 {{$fmt('name')}} {{$fmt('nombre')}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config",,,
"Access config imported successfully",,,
"Action to perform","Action à effectuer",,
"Anonymize","Anonymiser",,
"Anonymize data","Anonymiser les données",,
"App",,,
"Application Config","Config applicative",,
"Apps",,,
"Apps installed successfully","Applications installées avec succès",,
"Apps management","Gestion des applications",,
"Apps refreshed successfully","Applications actualisées avec succès",,
"Are you sure wants to anonymize data ?.","Voulez-vous vraiment rendre anonyme les données ?",,
"Are you sure wants to import demo data for '${name}'?","Voulez-vous vraiment importer les données de démo '${name}'?",,
"Are you sure wants to install '${name}'?","Voulez-vous vraiment intaller '${name}'?",,
"Are you sure wants to uninstall '${name}'?","Voulez-vous vraiment désintaller '${name}'?",,
"Backup","Sauvegarde",,
"Backup Created","Sauvegarde créée",,
"Backup File","Fichier de sauvegarde",,
"Backup On","Sauvegardé le",,
"Backup Restore Error","Erreur lors de la restauration de sauvegarde",,
"Backup Restored","Sauvegarde restaurée",,
"Bad header row:","Ligne d'en-tête incorrecte",,
"Bad import file","Fichier d'import incorrect",,
"Bulk install","Installation en masse",,
"CSV",,,
"CSV Backup Fetch Limit",,,
"Cancelled",,,
"Code",,,
"Comment",,,
"Condition",,,
"ConditionParams",,,
"Configure","Configurer",,
"Create","Créer",,
"Create Backup","Créer une sauvegarde",,
"Create file","Créer fichier",,
"Data","Données",,
"Data config line","Ligne de configuration de donnée",,
"Delete","Supprimer",,
"Demo data loaded","Charger les données de démonstration",,
"Demo data loaded successfully","Données de démonstration chargées avec succès",,
"Depends on","Dépend de",,
"Description",,,
"Details","Détails",,
"Draft",,,
"Error in import. Please check log.","Erreur dans l'import. Veuillez verifier le journal.",,
"Error in import: %s. Please check the server log","Erreur dans l'import: %s. Veuillez verifier le journal du serveur",,
"Error in refreshing app","Erreur dans l'actualisation de l'application",,
"Excel",,,
"Export","Exporter",,
"Export file",,,
"Export format","Format d'export",,
"Exports",,,
"Field","Champ",,
"Field permission","Champ de permission",,
"Fields to delete","Champs à supprimer",,
"Fields to export","Champs à exporter",,
"File","Fichier",,
"Group Menu Assistant","Assistant gestion des menus par groupes",,
"Groups","Groupes",,
"Groups not found: %s","Groupe(s) inconnu(s) : %s",,
"Hide If","Cacher si",,
"Image",,,
"Import","Importer",,
"Import ID","ID dimport",,
"Import access config",,,
"Import completed successfully",,,
"Import date","Date d'import",,
"Import demo data","Importer les données de démonstration",,
"Import permissions","Importer les permissions",,
"Import roles",,,
"Imported from","Importé depuis",,
"In Progress",,,
"Init data loaded","Charger les données d'initialisation",,
"Install","Installer",,
"Install order",,,
"Installed",,,
"Language","Langue",,
"Lines",,,
"Log","Journal",,
"Menu not found: %s","Menu(s) inconnu(s) : %s",,
"Menus",,,
"Meta Permissions",,,
"Model",,,
"Model select",,,
"Modules contains in the app","Modules contenus dans l'application",,
"Name","Nom",,
"No application language set. Please set 'application.locale' property.","Langue d'application non configurée. Veuillez configurer la propriété 'application.locale'",,
"No configuration required","Pas de configuration requise",,
"No header row found","Aucune ligne d'en-tête reconnue",,
"No record found for: %s",,,
"None",,,
"Object","Objet",,
"Object data anonymize",,,
"Object data config","Configuration des objets de données",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Objet(s) inconnu(s) : %s",,
"Objects","Objets",,
"Path","Chemin",,
"Path Field",,,
"Permission Assistant","Assistant gestion des permissions",,
"Query","Requête",,
"Query/path","Requête/chemin",,
"Read","Lecture",,
"Readonly If","Lecture seule si",,
"Record","Enregistrement",,
"Record name",,,
"Record select",,,
"Refresh Apps","Actualiser les applications",,
"Relative Date",,,
"Replace",,,
"Restore","Restaurer",,
"Restore Backup","Restaurer une sauvegarde",,
"Restored On","Restauré le",,
"Return to draft status","Retourner à l'état brouillon",,
"Roles","Rôles",,
"Roles imported","Rôles importés",,
"Roles imported successfully","Rôle importés avec succès",,
"Roles not found: %s","Rôles non trouvés: %s",,
"Select all","Tout sélectionner",,
"Sequence","Séquence",,
"Status",,,
"Tab name","Nom de l'onglet",,
"Technical maintenance","Maintenance technique",,
"This app is used by %s. Please deactivate them before continue.","Cette application est utilisée par %s. Veuillez les désactiver avent de continuer",,
"Title","Titre",,
"Type",,,
"Uninstall",,,
"Update Import Id",,,
"User",,,
"User access config",,,
"User field","Champ utilisateur",,
"Validated",,,
"Write","Écriture",,
"{{$fmt('name')}}",,,
1 key message comment context
2 Access config
3 Access config imported successfully
4 Action to perform Action à effectuer
5 Anonymize Anonymiser
6 Anonymize data Anonymiser les données
7 App
8 Application Config Config applicative
9 Apps
10 Apps installed successfully Applications installées avec succès
11 Apps management Gestion des applications
12 Apps refreshed successfully Applications actualisées avec succès
13 Are you sure wants to anonymize data ?. Voulez-vous vraiment rendre anonyme les données ?
14 Are you sure wants to import demo data for '${name}'? Voulez-vous vraiment importer les données de démo '${name}'?
15 Are you sure wants to install '${name}'? Voulez-vous vraiment intaller '${name}'?
16 Are you sure wants to uninstall '${name}'? Voulez-vous vraiment désintaller '${name}'?
17 Backup Sauvegarde
18 Backup Created Sauvegarde créée
19 Backup File Fichier de sauvegarde
20 Backup On Sauvegardé le
21 Backup Restore Error Erreur lors de la restauration de sauvegarde
22 Backup Restored Sauvegarde restaurée
23 Bad header row: Ligne d'en-tête incorrecte
24 Bad import file Fichier d'import incorrect
25 Bulk install Installation en masse
26 CSV
27 CSV Backup Fetch Limit
28 Cancelled
29 Code
30 Comment
31 Condition
32 ConditionParams
33 Configure Configurer
34 Create Créer
35 Create Backup Créer une sauvegarde
36 Create file Créer fichier
37 Data Données
38 Data config line Ligne de configuration de donnée
39 Delete Supprimer
40 Demo data loaded Charger les données de démonstration
41 Demo data loaded successfully Données de démonstration chargées avec succès
42 Depends on Dépend de
43 Description
44 Details Détails
45 Draft
46 Error in import. Please check log. Erreur dans l'import. Veuillez verifier le journal.
47 Error in import: %s. Please check the server log Erreur dans l'import: %s. Veuillez verifier le journal du serveur
48 Error in refreshing app Erreur dans l'actualisation de l'application
49 Excel
50 Export Exporter
51 Export file
52 Export format Format d'export
53 Exports
54 Field Champ
55 Field permission Champ de permission
56 Fields to delete Champs à supprimer
57 Fields to export Champs à exporter
58 File Fichier
59 Group Menu Assistant Assistant gestion des menus par groupes
60 Groups Groupes
61 Groups not found: %s Groupe(s) inconnu(s) : %s
62 Hide If Cacher si
63 Image
64 Import Importer
65 Import ID ID d’import
66 Import access config
67 Import completed successfully
68 Import date Date d'import
69 Import demo data Importer les données de démonstration
70 Import permissions Importer les permissions
71 Import roles
72 Imported from Importé depuis
73 In Progress
74 Init data loaded Charger les données d'initialisation
75 Install Installer
76 Install order
77 Installed
78 Language Langue
79 Lines
80 Log Journal
81 Menu not found: %s Menu(s) inconnu(s) : %s
82 Menus
83 Meta Permissions
84 Model
85 Model select
86 Modules contains in the app Modules contenus dans l'application
87 Name Nom
88 No application language set. Please set 'application.locale' property. Langue d'application non configurée. Veuillez configurer la propriété 'application.locale'
89 No configuration required Pas de configuration requise
90 No header row found Aucune ligne d'en-tête reconnue
91 No record found for: %s
92 None
93 Object Objet
94 Object data anonymize
95 Object data config Configuration des objets de données
96 Object data config export
97 Object data config exports
98 Object not found: %s Objet(s) inconnu(s) : %s
99 Objects Objets
100 Path Chemin
101 Path Field
102 Permission Assistant Assistant gestion des permissions
103 Query Requête
104 Query/path Requête/chemin
105 Read Lecture
106 Readonly If Lecture seule si
107 Record Enregistrement
108 Record name
109 Record select
110 Refresh Apps Actualiser les applications
111 Relative Date
112 Replace
113 Restore Restaurer
114 Restore Backup Restaurer une sauvegarde
115 Restored On Restauré le
116 Return to draft status Retourner à l'état brouillon
117 Roles Rôles
118 Roles imported Rôles importés
119 Roles imported successfully Rôle importés avec succès
120 Roles not found: %s Rôles non trouvés: %s
121 Select all Tout sélectionner
122 Sequence Séquence
123 Status
124 Tab name Nom de l'onglet
125 Technical maintenance Maintenance technique
126 This app is used by %s. Please deactivate them before continue. Cette application est utilisée par %s. Veuillez les désactiver avent de continuer
127 Title Titre
128 Type
129 Uninstall
130 Update Import Id
131 User
132 User access config
133 User field Champ utilisateur
134 Validated
135 Write Écriture
136 {{$fmt('name')}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config","Configurazione degli accessi",,
"Access config imported successfully","Accesso alla configurazione importata con successo",,
"Action to perform",,,
"Anonymize",,,
"Anonymize data","Anonimizzare i dati",,
"App","App",,
"Application Config","Config dell'applicazione",,
"Apps","Applicazioni",,
"Apps installed successfully","Le applicazioni installate con successo",,
"Apps management","Gestione delle applicazioni",,
"Apps refreshed successfully","Le applicazioni aggiornate con successo",,
"Are you sure wants to anonymize data ?.","Sei sicuro di voler rendere anonimi i dati?",,
"Are you sure wants to import demo data for '${name}'?","Sei sicuro di voler importare i dati demo per '${name}'?",,
"Are you sure wants to install '${name}'?","Sei sicuro di voler installare '${name}'?",,
"Are you sure wants to uninstall '${name}'?","Sei sicuro di voler disinstallare '${name}'?",,
"Backup","Backup",,
"Backup Created","Backup creato",,
"Backup File","File di backup",,
"Backup On","Backup On",,
"Backup Restore Error","Errore di ripristino del backup",,
"Backup Restored","Backup ripristinato",,
"Bad header row:","Brutta fila di intestazioni:",,
"Bad import file","File di importazione difettoso",,
"Bulk install","Installazione all'ingrosso",,
"CSV","CSV",,
"CSV Backup Fetch Limit","Limite di recupero del backup CSV",,
"Cancelled",,,
"Code","Codice",,
"Comment",,,
"Condition","Condizione",,
"ConditionParams","CondizioneParigi",,
"Configure","Configurare",,
"Create","Creare",,
"Create Backup","Crea backup",,
"Create file","Crea file",,
"Data","Dati",,
"Data config line","Linea di configurazione dati",,
"Delete","Cancellare",,
"Demo data loaded","Dati demo caricati",,
"Demo data loaded successfully","Dati dimostrativi caricati con successo",,
"Depends on","Dipende",,
"Description","Descrizione",,
"Details","Dettagli",,
"Draft","Bozza",,
"Error in import. Please check log.","Errore nell'importazione. Per favore, controlla il registro.",,
"Error in import: %s. Please check the server log","Errore nell'importazione: %s. Controllare il log del server",,
"Error in refreshing app","Errore nell'applicazione rinfrescante",,
"Excel","Excel",,
"Export","Esportazione",,
"Export file","Esporta file",,
"Export format","Formato di esportazione",,
"Exports",,,
"Field","Campo",,
"Field permission","Permesso di campo",,
"Fields to delete","Campi da cancellare",,
"Fields to export",,,
"File","File",,
"Group Menu Assistant","Assistente menu di gruppo",,
"Groups","Gruppi",,
"Groups not found: %s","Gruppi non trovati: %s",,
"Hide If","Nascondi se",,
"Image","Immagine",,
"Import","Importazione",,
"Import ID","Importa ID",,
"Import access config","Importare la configurazione di accesso",,
"Import completed successfully","Importazione completata con successo",,
"Import date","Data d'importazione",,
"Import demo data","Importare i dati demo",,
"Import permissions","Autorizzazioni all'importazione",,
"Import roles","Ruoli di importazione",,
"Imported from","Importati da",,
"In Progress","In corso",,
"Init data loaded","Dati init caricati",,
"Install","Installare",,
"Install order","Ordine di installazione",,
"Installed","Installato",,
"Language","Lingua",,
"Lines","Linee",,
"Log","Tronco",,
"Menu not found: %s","Menu non trovato: %s",,
"Menus","Menu",,
"Meta Permissions",,,
"Model","Modello",,
"Model select",,,
"Modules contains in the app","I moduli contengono nell'applicazione",,
"Name","Nome",,
"No application language set. Please set 'application.locale' property.","Nessuna lingua di applicazione impostata. Impostare la proprietà 'application.locale'.",,
"No configuration required","Nessuna configurazione richiesta",,
"No header row found","Nessuna riga di intestazione trovata",,
"No record found for: %s","Nessun record trovato: %s",,
"None","Nessuna",,
"Object","Oggetto",,
"Object data anonymize",,,
"Object data config","Configurazione dei dati dell'oggetto",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Oggetto non trovato: %s",,
"Objects","Oggetti",,
"Path","Sentiero",,
"Path Field",,,
"Permission Assistant","Assistente di autorizzazione",,
"Query","Domanda",,
"Query/path","Domanda/percorso",,
"Read","Leggi",,
"Readonly If","Leggero Se",,
"Record","Registrare",,
"Record name",,,
"Record select",,,
"Refresh Apps","Aggiorna le applicazioni",,
"Relative Date",,,
"Replace","Sostituire",,
"Restore","Ripristinare",,
"Restore Backup","Ripristina backup",,
"Restored On","Ripristinato su",,
"Return to draft status","Ritorno allo stato di bozza",,
"Roles","Ruoli",,
"Roles imported","Ruoli importati",,
"Roles imported successfully","Ruoli importati con successo",,
"Roles not found: %s","Ruoli non trovati: %s",,
"Select all","Seleziona tutti",,
"Sequence","Sequenza",,
"Status","Stato",,
"Tab name","Nome della scheda",,
"Technical maintenance","Manutenzione tecnica",,
"This app is used by %s. Please deactivate them before continue.","Questa applicazione è usata da %s. Disattivarli prima di continuare.",,
"Title","Titolo",,
"Type","Tipo",,
"Uninstall","Disinstallare",,
"Update Import Id",,,
"User","Utente",,
"User access config","Configurazione dell'accesso utente",,
"User field","Campo utente",,
"Validated",,,
"Write","Scrivere",,
"{{$fmt('name')}}","{{$fmt('name')}}}}",,
1 key message comment context
2 Access config Configurazione degli accessi
3 Access config imported successfully Accesso alla configurazione importata con successo
4 Action to perform
5 Anonymize
6 Anonymize data Anonimizzare i dati
7 App App
8 Application Config Config dell'applicazione
9 Apps Applicazioni
10 Apps installed successfully Le applicazioni installate con successo
11 Apps management Gestione delle applicazioni
12 Apps refreshed successfully Le applicazioni aggiornate con successo
13 Are you sure wants to anonymize data ?. Sei sicuro di voler rendere anonimi i dati?
14 Are you sure wants to import demo data for '${name}'? Sei sicuro di voler importare i dati demo per '${name}'?
15 Are you sure wants to install '${name}'? Sei sicuro di voler installare '${name}'?
16 Are you sure wants to uninstall '${name}'? Sei sicuro di voler disinstallare '${name}'?
17 Backup Backup
18 Backup Created Backup creato
19 Backup File File di backup
20 Backup On Backup On
21 Backup Restore Error Errore di ripristino del backup
22 Backup Restored Backup ripristinato
23 Bad header row: Brutta fila di intestazioni:
24 Bad import file File di importazione difettoso
25 Bulk install Installazione all'ingrosso
26 CSV CSV
27 CSV Backup Fetch Limit Limite di recupero del backup CSV
28 Cancelled
29 Code Codice
30 Comment
31 Condition Condizione
32 ConditionParams CondizioneParigi
33 Configure Configurare
34 Create Creare
35 Create Backup Crea backup
36 Create file Crea file
37 Data Dati
38 Data config line Linea di configurazione dati
39 Delete Cancellare
40 Demo data loaded Dati demo caricati
41 Demo data loaded successfully Dati dimostrativi caricati con successo
42 Depends on Dipende
43 Description Descrizione
44 Details Dettagli
45 Draft Bozza
46 Error in import. Please check log. Errore nell'importazione. Per favore, controlla il registro.
47 Error in import: %s. Please check the server log Errore nell'importazione: %s. Controllare il log del server
48 Error in refreshing app Errore nell'applicazione rinfrescante
49 Excel Excel
50 Export Esportazione
51 Export file Esporta file
52 Export format Formato di esportazione
53 Exports
54 Field Campo
55 Field permission Permesso di campo
56 Fields to delete Campi da cancellare
57 Fields to export
58 File File
59 Group Menu Assistant Assistente menu di gruppo
60 Groups Gruppi
61 Groups not found: %s Gruppi non trovati: %s
62 Hide If Nascondi se
63 Image Immagine
64 Import Importazione
65 Import ID Importa ID
66 Import access config Importare la configurazione di accesso
67 Import completed successfully Importazione completata con successo
68 Import date Data d'importazione
69 Import demo data Importare i dati demo
70 Import permissions Autorizzazioni all'importazione
71 Import roles Ruoli di importazione
72 Imported from Importati da
73 In Progress In corso
74 Init data loaded Dati init caricati
75 Install Installare
76 Install order Ordine di installazione
77 Installed Installato
78 Language Lingua
79 Lines Linee
80 Log Tronco
81 Menu not found: %s Menu non trovato: %s
82 Menus Menu
83 Meta Permissions
84 Model Modello
85 Model select
86 Modules contains in the app I moduli contengono nell'applicazione
87 Name Nome
88 No application language set. Please set 'application.locale' property. Nessuna lingua di applicazione impostata. Impostare la proprietà 'application.locale'.
89 No configuration required Nessuna configurazione richiesta
90 No header row found Nessuna riga di intestazione trovata
91 No record found for: %s Nessun record trovato: %s
92 None Nessuna
93 Object Oggetto
94 Object data anonymize
95 Object data config Configurazione dei dati dell'oggetto
96 Object data config export
97 Object data config exports
98 Object not found: %s Oggetto non trovato: %s
99 Objects Oggetti
100 Path Sentiero
101 Path Field
102 Permission Assistant Assistente di autorizzazione
103 Query Domanda
104 Query/path Domanda/percorso
105 Read Leggi
106 Readonly If Leggero Se
107 Record Registrare
108 Record name
109 Record select
110 Refresh Apps Aggiorna le applicazioni
111 Relative Date
112 Replace Sostituire
113 Restore Ripristinare
114 Restore Backup Ripristina backup
115 Restored On Ripristinato su
116 Return to draft status Ritorno allo stato di bozza
117 Roles Ruoli
118 Roles imported Ruoli importati
119 Roles imported successfully Ruoli importati con successo
120 Roles not found: %s Ruoli non trovati: %s
121 Select all Seleziona tutti
122 Sequence Sequenza
123 Status Stato
124 Tab name Nome della scheda
125 Technical maintenance Manutenzione tecnica
126 This app is used by %s. Please deactivate them before continue. Questa applicazione è usata da %s. Disattivarli prima di continuare.
127 Title Titolo
128 Type Tipo
129 Uninstall Disinstallare
130 Update Import Id
131 User Utente
132 User access config Configurazione dell'accesso utente
133 User field Campo utente
134 Validated
135 Write Scrivere
136 {{$fmt('name')}} {{$fmt('name')}}}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config","Toegang configureren",,
"Access config imported successfully","Toegang tot geïmporteerde configuratie met succes",,
"Action to perform",,,
"Anonymize",,,
"Anonymize data","Gegevens anonimiseren",,
"App","App",,
"Application Config","Toepassingsconfiguratie",,
"Apps","Apps",,
"Apps installed successfully","Apps succesvol geïnstalleerd",,
"Apps management","Beheer van apps",,
"Apps refreshed successfully","Apps succesvol ververst",,
"Are you sure wants to anonymize data ?.","Weet u zeker dat u gegevens wilt anonimiseren?",,
"Are you sure wants to import demo data for '${name}'?","Weet u zeker dat u demodata wilt importeren voor '${naam}'?",,
"Are you sure wants to install '${name}'?","Weet u zeker dat u '${naam}' wilt installeren?",,
"Are you sure wants to uninstall '${name}'?","Weet u zeker dat u '${naam}' wilt verwijderen?",,
"Backup","Back-up",,
"Backup Created","Back-up gemaakt",,
"Backup File","Back-up bestand",,
"Backup On","Backup Aan",,
"Backup Restore Error","Back-up Fout herstellen",,
"Backup Restored","Back-up hersteld",,
"Bad header row:","Slechte header-rij:",,
"Bad import file","Slecht importbestand",,
"Bulk install","Bulk installeren",,
"CSV","CSV",,
"CSV Backup Fetch Limit","CSV-back-up limiet halen",,
"Cancelled",,,
"Code","Code",,
"Comment",,,
"Condition","Voorwaarde",,
"ConditionParams","ConditionParams",,
"Configure","Configureer",,
"Create","Creëer",,
"Create Backup","Back-up maken",,
"Create file","Bestand aanmaken",,
"Data","Gegevens",,
"Data config line","Gegevensconfiguratielijn",,
"Delete","Schrappen",,
"Demo data loaded","Demo-gegevens geladen",,
"Demo data loaded successfully","Demodata succesvol geladen",,
"Depends on","Afhankelijk van",,
"Description","Beschrijving",,
"Details","Details",,
"Draft","Ontwerp",,
"Error in import. Please check log.","Fout in de import. Controleer het logboek.",,
"Error in import: %s. Please check the server log","Fout in de invoer: %s. Controleer het serverlogboek",,
"Error in refreshing app","Fout in verfrissende app",,
"Excel","Excel",,
"Export","Exporteren",,
"Export file","Bestand exporteren",,
"Export format","Exportformaat",,
"Exports",,,
"Field","Veld",,
"Field permission","Veldtoestemming",,
"Fields to delete","Te verwijderen velden",,
"Fields to export",,,
"File","Bestand",,
"Group Menu Assistant","Assistent groepsmenu",,
"Groups","Groepen",,
"Groups not found: %s","Groepen niet gevonden: %s",,
"Hide If","Verberg als",,
"Image","Beeld",,
"Import","Importeren",,
"Import ID","ID importeren",,
"Import access config","Importeer toegang configureren",,
"Import completed successfully","Importeren succesvol afgerond",,
"Import date","Datum van invoer",,
"Import demo data","Demodata importeren",,
"Import permissions","Importvergunningen",,
"Import roles","Rollen importeren",,
"Imported from","Geïmporteerd van",,
"In Progress","In uitvoering",,
"Init data loaded","Init gegevens geladen",,
"Install","Installeren",,
"Install order","Bestelling installeren",,
"Installed","Geïnstalleerd",,
"Language","Taal",,
"Lines","Lijnen",,
"Log","Logboek",,
"Menu not found: %s","Menu niet gevonden: %s",,
"Menus","Menu's",,
"Meta Permissions",,,
"Model","Model",,
"Model select",,,
"Modules contains in the app","Modules bevat in de app",,
"Name","Naam",,
"No application language set. Please set 'application.locale' property.","Geen applicatietaal ingesteld. Stel a.u.b. de eigenschap 'applicatie.locale' in.",,
"No configuration required","Geen configuratie nodig",,
"No header row found","Geen kopregel gevonden",,
"No record found for: %s","Geen record gevonden voor: %s",,
"None","Geen",,
"Object","Voorwerp",,
"Object data anonymize",,,
"Object data config","Objectgegevens configureren",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Object niet gevonden: %s",,
"Objects","Voorwerpen",,
"Path","Pad",,
"Path Field",,,
"Permission Assistant","Toestemming Assistent",,
"Query","Vraag",,
"Query/path","Vraag/pad",,
"Read","Lezen",,
"Readonly If","Alleen lezen als",,
"Record","Verslag",,
"Record name",,,
"Record select",,,
"Refresh Apps","Apps verversen",,
"Relative Date",,,
"Replace","Vervangen",,
"Restore","Herstel",,
"Restore Backup","Back-up herstellen",,
"Restored On","Gerestaureerd op",,
"Return to draft status","Terug naar de ontwerpstatus",,
"Roles","Rollen",,
"Roles imported","Geïmporteerde rollen",,
"Roles imported successfully","Rollen succesvol geïmporteerd",,
"Roles not found: %s","Rollen niet gevonden: %s",,
"Select all","Selecteer alle",,
"Sequence","Volgorde",,
"Status","Status",,
"Tab name","Tabblad naam",,
"Technical maintenance","Technisch onderhoud",,
"This app is used by %s. Please deactivate them before continue.","Deze app wordt gebruikt door %s. Deactiveer ze voordat u verdergaat.",,
"Title","Titel",,
"Type","Type",,
"Uninstall","Verwijderen",,
"Update Import Id",,,
"User","Gebruiker",,
"User access config","Configuratie van de gebruikerstoegang",,
"User field","Gebruikersveld",,
"Validated",,,
"Write","Schrijf",,
"{{$fmt('name')}}","{{$fmt('naam')}}}",,
1 key message comment context
2 Access config Toegang configureren
3 Access config imported successfully Toegang tot geïmporteerde configuratie met succes
4 Action to perform
5 Anonymize
6 Anonymize data Gegevens anonimiseren
7 App App
8 Application Config Toepassingsconfiguratie
9 Apps Apps
10 Apps installed successfully Apps succesvol geïnstalleerd
11 Apps management Beheer van apps
12 Apps refreshed successfully Apps succesvol ververst
13 Are you sure wants to anonymize data ?. Weet u zeker dat u gegevens wilt anonimiseren?
14 Are you sure wants to import demo data for '${name}'? Weet u zeker dat u demodata wilt importeren voor '${naam}'?
15 Are you sure wants to install '${name}'? Weet u zeker dat u '${naam}' wilt installeren?
16 Are you sure wants to uninstall '${name}'? Weet u zeker dat u '${naam}' wilt verwijderen?
17 Backup Back-up
18 Backup Created Back-up gemaakt
19 Backup File Back-up bestand
20 Backup On Backup Aan
21 Backup Restore Error Back-up Fout herstellen
22 Backup Restored Back-up hersteld
23 Bad header row: Slechte header-rij:
24 Bad import file Slecht importbestand
25 Bulk install Bulk installeren
26 CSV CSV
27 CSV Backup Fetch Limit CSV-back-up limiet halen
28 Cancelled
29 Code Code
30 Comment
31 Condition Voorwaarde
32 ConditionParams ConditionParams
33 Configure Configureer
34 Create Creëer
35 Create Backup Back-up maken
36 Create file Bestand aanmaken
37 Data Gegevens
38 Data config line Gegevensconfiguratielijn
39 Delete Schrappen
40 Demo data loaded Demo-gegevens geladen
41 Demo data loaded successfully Demodata succesvol geladen
42 Depends on Afhankelijk van
43 Description Beschrijving
44 Details Details
45 Draft Ontwerp
46 Error in import. Please check log. Fout in de import. Controleer het logboek.
47 Error in import: %s. Please check the server log Fout in de invoer: %s. Controleer het serverlogboek
48 Error in refreshing app Fout in verfrissende app
49 Excel Excel
50 Export Exporteren
51 Export file Bestand exporteren
52 Export format Exportformaat
53 Exports
54 Field Veld
55 Field permission Veldtoestemming
56 Fields to delete Te verwijderen velden
57 Fields to export
58 File Bestand
59 Group Menu Assistant Assistent groepsmenu
60 Groups Groepen
61 Groups not found: %s Groepen niet gevonden: %s
62 Hide If Verberg als
63 Image Beeld
64 Import Importeren
65 Import ID ID importeren
66 Import access config Importeer toegang configureren
67 Import completed successfully Importeren succesvol afgerond
68 Import date Datum van invoer
69 Import demo data Demodata importeren
70 Import permissions Importvergunningen
71 Import roles Rollen importeren
72 Imported from Geïmporteerd van
73 In Progress In uitvoering
74 Init data loaded Init gegevens geladen
75 Install Installeren
76 Install order Bestelling installeren
77 Installed Geïnstalleerd
78 Language Taal
79 Lines Lijnen
80 Log Logboek
81 Menu not found: %s Menu niet gevonden: %s
82 Menus Menu's
83 Meta Permissions
84 Model Model
85 Model select
86 Modules contains in the app Modules bevat in de app
87 Name Naam
88 No application language set. Please set 'application.locale' property. Geen applicatietaal ingesteld. Stel a.u.b. de eigenschap 'applicatie.locale' in.
89 No configuration required Geen configuratie nodig
90 No header row found Geen kopregel gevonden
91 No record found for: %s Geen record gevonden voor: %s
92 None Geen
93 Object Voorwerp
94 Object data anonymize
95 Object data config Objectgegevens configureren
96 Object data config export
97 Object data config exports
98 Object not found: %s Object niet gevonden: %s
99 Objects Voorwerpen
100 Path Pad
101 Path Field
102 Permission Assistant Toestemming Assistent
103 Query Vraag
104 Query/path Vraag/pad
105 Read Lezen
106 Readonly If Alleen lezen als
107 Record Verslag
108 Record name
109 Record select
110 Refresh Apps Apps verversen
111 Relative Date
112 Replace Vervangen
113 Restore Herstel
114 Restore Backup Back-up herstellen
115 Restored On Gerestaureerd op
116 Return to draft status Terug naar de ontwerpstatus
117 Roles Rollen
118 Roles imported Geïmporteerde rollen
119 Roles imported successfully Rollen succesvol geïmporteerd
120 Roles not found: %s Rollen niet gevonden: %s
121 Select all Selecteer alle
122 Sequence Volgorde
123 Status Status
124 Tab name Tabblad naam
125 Technical maintenance Technisch onderhoud
126 This app is used by %s. Please deactivate them before continue. Deze app wordt gebruikt door %s. Deactiveer ze voordat u verdergaat.
127 Title Titel
128 Type Type
129 Uninstall Verwijderen
130 Update Import Id
131 User Gebruiker
132 User access config Configuratie van de gebruikerstoegang
133 User field Gebruikersveld
134 Validated
135 Write Schrijf
136 {{$fmt('name')}} {{$fmt('naam')}}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config","Konfiguracja dostępu",,
"Access config imported successfully","Konfiguracja dostępu zaimportowana pomyślnie",,
"Action to perform",,,
"Anonymize",,,
"Anonymize data","Anonimizacja danych",,
"App","Aplikacja",,
"Application Config","Konfiguracja aplikacji",,
"Apps","Aplikacje",,
"Apps installed successfully","Aplikacje zainstalowane pomyślnie",,
"Apps management","Zarządzanie aplikacjami",,
"Apps refreshed successfully","Aplikacje odświeżone pomyślnie",,
"Are you sure wants to anonymize data ?.","Czy na pewno chcesz anonimizować dane ?",,
"Are you sure wants to import demo data for '${name}'?","Czy na pewno chcesz zaimportować dane demo dla '${nazwa}'?",,
"Are you sure wants to install '${name}'?","Czy na pewno chcesz zainstalować '${nazwa}'?",,
"Are you sure wants to uninstall '${name}'?","Czy na pewno chcesz odinstalować '${nazwa}'?",,
"Backup","Kopia zapasowa",,
"Backup Created","Tworzenie kopii zapasowej",,
"Backup File","Plik kopii zapasowej",,
"Backup On","Kopia zapasowa Wł.",,
"Backup Restore Error","Błąd kopii zapasowej",,
"Backup Restored","Kopia zapasowa przywrócona",,
"Bad header row:","Zły rząd nagłówka:",,
"Bad import file","Zły plik importowy",,
"Bulk install","Instalacja masowa",,
"CSV","CSV",,
"CSV Backup Fetch Limit","CSV Backup Fetch Limit",,
"Cancelled",,,
"Code","Kod",,
"Comment",,,
"Condition","Warunek",,
"ConditionParams","ConditionParams",,
"Configure","Konfiguracja",,
"Create","Utworzyć",,
"Create Backup","Tworzenie kopii zapasowej",,
"Create file","Utwórz plik",,
"Data","Dane",,
"Data config line","Linia konfiguracji danych",,
"Delete","Skreślenie",,
"Demo data loaded","Załadowane dane demonstracyjne",,
"Demo data loaded successfully","Udane wczytanie danych demonstracyjnych",,
"Depends on","W zależności od",,
"Description","Opis",,
"Details","Szczegóły",,
"Draft","Wstępny projekt",,
"Error in import. Please check log.","Błąd w imporcie. Proszę sprawdzić dziennik.",,
"Error in import: %s. Please check the server log","Błąd w imporcie: %s. Proszę sprawdzić log serwera",,
"Error in refreshing app","Błąd w odświeżaniu aplikacji",,
"Excel","Excel",,
"Export","Eksportowanie",,
"Export file","Eksport pliku",,
"Export format","Format eksportu",,
"Exports",,,
"Field","Pole",,
"Field permission","Pozwolenie w terenie",,
"Fields to delete","Pola do usunięcia",,
"Fields to export",,,
"File","Pilnik",,
"Group Menu Assistant","Asystent menu grupy",,
"Groups","Grupy",,
"Groups not found: %s","Grupy nie znaleziono: %s",,
"Hide If","Ukryj Jeśli",,
"Image","Obrazek",,
"Import","Przywóz",,
"Import ID","Identyfikator przywozu",,
"Import access config","Konfiguracja dostępu do importu",,
"Import completed successfully","Przywóz ukończony pomyślnie",,
"Import date","Data przywozu",,
"Import demo data","Import danych demo",,
"Import permissions","Pozwolenia na przywóz",,
"Import roles","Role importowe",,
"Imported from","Przywożone z",,
"In Progress","W toku",,
"Init data loaded","Załadowane dane init",,
"Install","Zainstalować",,
"Install order","Zainstaluj zamówienie",,
"Installed","Zainstalowane",,
"Language","Język",,
"Lines","Linie",,
"Log","Dziennik",,
"Menu not found: %s","Menu nie znaleziono: %s",,
"Menus","Menu",,
"Meta Permissions",,,
"Model","Model",,
"Model select",,,
"Modules contains in the app","Moduły zawierają w aplikacji",,
"Name","Nazwa",,
"No application language set. Please set 'application.locale' property.","Brak zestawu języka aplikacji. Proszę ustawić 'application.locale' property.",,
"No configuration required","Nie jest wymagana żadna konfiguracja.",,
"No header row found","Nie znaleziono żadnego rzędu nagłówków",,
"No record found for: %s","Nie znaleziono żadnego rekordu: %s",,
"None","Brak.",,
"Object","Obiekt",,
"Object data anonymize",,,
"Object data config","Konfiguracja danych obiektowych",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Przedmiot nie znaleziono: %s",,
"Objects","Przedmioty",,
"Path","Ścieżka",,
"Path Field",,,
"Permission Assistant","Asystent ds. pozwoleń",,
"Query","Zapytanie",,
"Query/path","Kwerenda/ścieżka",,
"Read","Przeczytać",,
"Readonly If","Czytelnie Jeśli",,
"Record","Rekord",,
"Record name",,,
"Record select",,,
"Refresh Apps","Odśwież aplikacje",,
"Relative Date",,,
"Replace","Zastąpić miejsce",,
"Restore","Przywróćcie stan poprzedniego",,
"Restore Backup","Przywracanie kopii zapasowej",,
"Restored On","Restored On (Przywrócono)",,
"Return to draft status","Powrót do projektu statusu",,
"Roles","Role",,
"Roles imported","Role importowane",,
"Roles imported successfully","Role importowane z powodzeniem",,
"Roles not found: %s","Nie znaleziono ról: %s",,
"Select all","Wybierz wszystkie",,
"Sequence","Kolejność",,
"Status","Status",,
"Tab name","Nazwa zakładki",,
"Technical maintenance","Konserwacja techniczna",,
"This app is used by %s. Please deactivate them before continue.","Ta aplikacja jest używana przez %s. Proszę je wyłączyć przed kontynuacją.",,
"Title","Tytuł",,
"Type","Typ",,
"Uninstall","Odinstaluj",,
"Update Import Id",,,
"User","Użytkownik",,
"User access config","Konfiguracja dostępu użytkownika",,
"User field","Pole użytkownika",,
"Validated",,,
"Write","Napisać",,
"{{$fmt('name')}}","{{{$fmt('name')}}}}",,
1 key message comment context
2 Access config Konfiguracja dostępu
3 Access config imported successfully Konfiguracja dostępu zaimportowana pomyślnie
4 Action to perform
5 Anonymize
6 Anonymize data Anonimizacja danych
7 App Aplikacja
8 Application Config Konfiguracja aplikacji
9 Apps Aplikacje
10 Apps installed successfully Aplikacje zainstalowane pomyślnie
11 Apps management Zarządzanie aplikacjami
12 Apps refreshed successfully Aplikacje odświeżone pomyślnie
13 Are you sure wants to anonymize data ?. Czy na pewno chcesz anonimizować dane ?
14 Are you sure wants to import demo data for '${name}'? Czy na pewno chcesz zaimportować dane demo dla '${nazwa}'?
15 Are you sure wants to install '${name}'? Czy na pewno chcesz zainstalować '${nazwa}'?
16 Are you sure wants to uninstall '${name}'? Czy na pewno chcesz odinstalować '${nazwa}'?
17 Backup Kopia zapasowa
18 Backup Created Tworzenie kopii zapasowej
19 Backup File Plik kopii zapasowej
20 Backup On Kopia zapasowa Wł.
21 Backup Restore Error Błąd kopii zapasowej
22 Backup Restored Kopia zapasowa przywrócona
23 Bad header row: Zły rząd nagłówka:
24 Bad import file Zły plik importowy
25 Bulk install Instalacja masowa
26 CSV CSV
27 CSV Backup Fetch Limit CSV Backup Fetch Limit
28 Cancelled
29 Code Kod
30 Comment
31 Condition Warunek
32 ConditionParams ConditionParams
33 Configure Konfiguracja
34 Create Utworzyć
35 Create Backup Tworzenie kopii zapasowej
36 Create file Utwórz plik
37 Data Dane
38 Data config line Linia konfiguracji danych
39 Delete Skreślenie
40 Demo data loaded Załadowane dane demonstracyjne
41 Demo data loaded successfully Udane wczytanie danych demonstracyjnych
42 Depends on W zależności od
43 Description Opis
44 Details Szczegóły
45 Draft Wstępny projekt
46 Error in import. Please check log. Błąd w imporcie. Proszę sprawdzić dziennik.
47 Error in import: %s. Please check the server log Błąd w imporcie: %s. Proszę sprawdzić log serwera
48 Error in refreshing app Błąd w odświeżaniu aplikacji
49 Excel Excel
50 Export Eksportowanie
51 Export file Eksport pliku
52 Export format Format eksportu
53 Exports
54 Field Pole
55 Field permission Pozwolenie w terenie
56 Fields to delete Pola do usunięcia
57 Fields to export
58 File Pilnik
59 Group Menu Assistant Asystent menu grupy
60 Groups Grupy
61 Groups not found: %s Grupy nie znaleziono: %s
62 Hide If Ukryj Jeśli
63 Image Obrazek
64 Import Przywóz
65 Import ID Identyfikator przywozu
66 Import access config Konfiguracja dostępu do importu
67 Import completed successfully Przywóz ukończony pomyślnie
68 Import date Data przywozu
69 Import demo data Import danych demo
70 Import permissions Pozwolenia na przywóz
71 Import roles Role importowe
72 Imported from Przywożone z
73 In Progress W toku
74 Init data loaded Załadowane dane init
75 Install Zainstalować
76 Install order Zainstaluj zamówienie
77 Installed Zainstalowane
78 Language Język
79 Lines Linie
80 Log Dziennik
81 Menu not found: %s Menu nie znaleziono: %s
82 Menus Menu
83 Meta Permissions
84 Model Model
85 Model select
86 Modules contains in the app Moduły zawierają w aplikacji
87 Name Nazwa
88 No application language set. Please set 'application.locale' property. Brak zestawu języka aplikacji. Proszę ustawić 'application.locale' property.
89 No configuration required Nie jest wymagana żadna konfiguracja.
90 No header row found Nie znaleziono żadnego rzędu nagłówków
91 No record found for: %s Nie znaleziono żadnego rekordu: %s
92 None Brak.
93 Object Obiekt
94 Object data anonymize
95 Object data config Konfiguracja danych obiektowych
96 Object data config export
97 Object data config exports
98 Object not found: %s Przedmiot nie znaleziono: %s
99 Objects Przedmioty
100 Path Ścieżka
101 Path Field
102 Permission Assistant Asystent ds. pozwoleń
103 Query Zapytanie
104 Query/path Kwerenda/ścieżka
105 Read Przeczytać
106 Readonly If Czytelnie Jeśli
107 Record Rekord
108 Record name
109 Record select
110 Refresh Apps Odśwież aplikacje
111 Relative Date
112 Replace Zastąpić miejsce
113 Restore Przywróćcie stan poprzedniego
114 Restore Backup Przywracanie kopii zapasowej
115 Restored On Restored On (Przywrócono)
116 Return to draft status Powrót do projektu statusu
117 Roles Role
118 Roles imported Role importowane
119 Roles imported successfully Role importowane z powodzeniem
120 Roles not found: %s Nie znaleziono ról: %s
121 Select all Wybierz wszystkie
122 Sequence Kolejność
123 Status Status
124 Tab name Nazwa zakładki
125 Technical maintenance Konserwacja techniczna
126 This app is used by %s. Please deactivate them before continue. Ta aplikacja jest używana przez %s. Proszę je wyłączyć przed kontynuacją.
127 Title Tytuł
128 Type Typ
129 Uninstall Odinstaluj
130 Update Import Id
131 User Użytkownik
132 User access config Konfiguracja dostępu użytkownika
133 User field Pole użytkownika
134 Validated
135 Write Napisać
136 {{$fmt('name')}} {{{$fmt('name')}}}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config","Configuração de acesso",,
"Access config imported successfully","Acesso à configuração importada com sucesso",,
"Action to perform",,,
"Anonymize",,,
"Anonymize data","Anonimizar dados",,
"App","Aplicativo",,
"Application Config","Configuração da aplicação",,
"Apps","Aplicativos",,
"Apps installed successfully","Aplicativos instalados com sucesso",,
"Apps management","Gestão de aplicações",,
"Apps refreshed successfully","Aplicativos atualizados com sucesso",,
"Are you sure wants to anonymize data ?.","Tens a certeza que queres anonimizar os dados?",,
"Are you sure wants to import demo data for '${name}'?","Você tem certeza de que deseja importar dados de demonstração para '${nome}'?",,
"Are you sure wants to install '${name}'?","Tens a certeza que queres instalar ${nome}?",,
"Are you sure wants to uninstall '${name}'?","Tens a certeza que queres desinstalar o '${nome}'?",,
"Backup","Backup",,
"Backup Created","Backup criado",,
"Backup File","Arquivo de backup",,
"Backup On","Backup On",,
"Backup Restore Error","Erro de Restauração de Backup",,
"Backup Restored","Backup restaurado",,
"Bad header row:","Linha de cabeçalho ruim:",,
"Bad import file","Arquivo de importação ruim",,
"Bulk install","Instalação a granel",,
"CSV","CSV",,
"CSV Backup Fetch Limit","Limite de Busca de Apoio CSV",,
"Cancelled",,,
"Code","Código",,
"Comment",,,
"Condition","Condição",,
"ConditionParams","CondiçãoParams",,
"Configure","Configurar",,
"Create","Criar",,
"Create Backup","Criar backup",,
"Create file","Criar arquivo",,
"Data","Dados",,
"Data config line","Linha de configuração de dados",,
"Delete","Eliminar",,
"Demo data loaded","Dados de demonstração carregados",,
"Demo data loaded successfully","Dados de demonstração carregados com sucesso",,
"Depends on","Depende de",,
"Description","Descrição do produto",,
"Details","Detalhes",,
"Draft","Rascunho",,
"Error in import. Please check log.","Erro na importação. Por favor, verifique o registo.",,
"Error in import: %s. Please check the server log","Erro na importação: %s. Por favor, verifique o log do servidor",,
"Error in refreshing app","Erro na atualização do aplicativo",,
"Excel","Excel",,
"Export","Exportação",,
"Export file","Exportar arquivo",,
"Export format","Formato de exportação",,
"Exports",,,
"Field","Campo",,
"Field permission","Permissão de campo",,
"Fields to delete","Campos a eliminar",,
"Fields to export",,,
"File","Arquivo",,
"Group Menu Assistant","Assistente de Menu de Grupo",,
"Groups","Grupos",,
"Groups not found: %s","Grupos não encontrados: %s",,
"Hide If","Ocultar se",,
"Image","Imagem",,
"Import","Importação",,
"Import ID","Importar ID",,
"Import access config","Importar configuração de acesso",,
"Import completed successfully","Importação concluída com sucesso",,
"Import date","Data de importação",,
"Import demo data","Importar dados de demonstração",,
"Import permissions","Permissões de importação",,
"Import roles","Importar funções",,
"Imported from","Importado de",,
"In Progress","Em andamento",,
"Init data loaded","Dados de inicialização carregados",,
"Install","Instalar",,
"Install order","Instalar ordem",,
"Installed","Instalado",,
"Language","Idioma",,
"Lines","Linhas",,
"Log","Log",,
"Menu not found: %s","Menu não encontrado: %s",,
"Menus","Menus",,
"Meta Permissions",,,
"Model","Modelo",,
"Model select",,,
"Modules contains in the app","Módulos contém no aplicativo",,
"Name","Nome e Sobrenome",,
"No application language set. Please set 'application.locale' property.","Nenhuma linguagem de aplicação definida. Por favor, defina a propriedade 'application.locale'.",,
"No configuration required","Nenhuma configuração necessária",,
"No header row found","Nenhuma linha de cabeçalho encontrada",,
"No record found for: %s","Nenhum registo encontrado: %s",,
"None","Nenhum",,
"Object","Objeto",,
"Object data anonymize",,,
"Object data config","Configuração dos dados do objeto",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Objeto não encontrado: %s",,
"Objects","Objetos",,
"Path","Caminho",,
"Path Field",,,
"Permission Assistant","Assistente de permissão",,
"Query","Consulta",,
"Query/path","Consulta/caminho",,
"Read","Ler",,
"Readonly If","Apenas leitura Se",,
"Record","Registrar",,
"Record name",,,
"Record select",,,
"Refresh Apps","Atualizar aplicativos",,
"Relative Date",,,
"Replace","Substituir",,
"Restore","Restaurar",,
"Restore Backup","Restaurar backup",,
"Restored On","Restaurado em",,
"Return to draft status","Retornar ao status de esboço",,
"Roles","Funções",,
"Roles imported","Funções importadas",,
"Roles imported successfully","Funções importadas com sucesso",,
"Roles not found: %s","Funções não encontradas: %s",,
"Select all","Selecionar todos",,
"Sequence","Seqüência",,
"Status","Estado",,
"Tab name","Nome da guia",,
"Technical maintenance","Manutenção técnica",,
"This app is used by %s. Please deactivate them before continue.","Este aplicativo é usado por %s. Por favor, desactive-os antes de continuar.",,
"Title","Título",,
"Type","Tipo de",,
"Uninstall","Desinstalar",,
"Update Import Id",,,
"User","Usuário",,
"User access config","Configuração de acesso do usuário",,
"User field","Campo do usuário",,
"Validated",,,
"Write","Escrever",,
"{{$fmt('name')}}","{{$fmt('name')}}",,
1 key message comment context
2 Access config Configuração de acesso
3 Access config imported successfully Acesso à configuração importada com sucesso
4 Action to perform
5 Anonymize
6 Anonymize data Anonimizar dados
7 App Aplicativo
8 Application Config Configuração da aplicação
9 Apps Aplicativos
10 Apps installed successfully Aplicativos instalados com sucesso
11 Apps management Gestão de aplicações
12 Apps refreshed successfully Aplicativos atualizados com sucesso
13 Are you sure wants to anonymize data ?. Tens a certeza que queres anonimizar os dados?
14 Are you sure wants to import demo data for '${name}'? Você tem certeza de que deseja importar dados de demonstração para '${nome}'?
15 Are you sure wants to install '${name}'? Tens a certeza que queres instalar ${nome}?
16 Are you sure wants to uninstall '${name}'? Tens a certeza que queres desinstalar o '${nome}'?
17 Backup Backup
18 Backup Created Backup criado
19 Backup File Arquivo de backup
20 Backup On Backup On
21 Backup Restore Error Erro de Restauração de Backup
22 Backup Restored Backup restaurado
23 Bad header row: Linha de cabeçalho ruim:
24 Bad import file Arquivo de importação ruim
25 Bulk install Instalação a granel
26 CSV CSV
27 CSV Backup Fetch Limit Limite de Busca de Apoio CSV
28 Cancelled
29 Code Código
30 Comment
31 Condition Condição
32 ConditionParams CondiçãoParams
33 Configure Configurar
34 Create Criar
35 Create Backup Criar backup
36 Create file Criar arquivo
37 Data Dados
38 Data config line Linha de configuração de dados
39 Delete Eliminar
40 Demo data loaded Dados de demonstração carregados
41 Demo data loaded successfully Dados de demonstração carregados com sucesso
42 Depends on Depende de
43 Description Descrição do produto
44 Details Detalhes
45 Draft Rascunho
46 Error in import. Please check log. Erro na importação. Por favor, verifique o registo.
47 Error in import: %s. Please check the server log Erro na importação: %s. Por favor, verifique o log do servidor
48 Error in refreshing app Erro na atualização do aplicativo
49 Excel Excel
50 Export Exportação
51 Export file Exportar arquivo
52 Export format Formato de exportação
53 Exports
54 Field Campo
55 Field permission Permissão de campo
56 Fields to delete Campos a eliminar
57 Fields to export
58 File Arquivo
59 Group Menu Assistant Assistente de Menu de Grupo
60 Groups Grupos
61 Groups not found: %s Grupos não encontrados: %s
62 Hide If Ocultar se
63 Image Imagem
64 Import Importação
65 Import ID Importar ID
66 Import access config Importar configuração de acesso
67 Import completed successfully Importação concluída com sucesso
68 Import date Data de importação
69 Import demo data Importar dados de demonstração
70 Import permissions Permissões de importação
71 Import roles Importar funções
72 Imported from Importado de
73 In Progress Em andamento
74 Init data loaded Dados de inicialização carregados
75 Install Instalar
76 Install order Instalar ordem
77 Installed Instalado
78 Language Idioma
79 Lines Linhas
80 Log Log
81 Menu not found: %s Menu não encontrado: %s
82 Menus Menus
83 Meta Permissions
84 Model Modelo
85 Model select
86 Modules contains in the app Módulos contém no aplicativo
87 Name Nome e Sobrenome
88 No application language set. Please set 'application.locale' property. Nenhuma linguagem de aplicação definida. Por favor, defina a propriedade 'application.locale'.
89 No configuration required Nenhuma configuração necessária
90 No header row found Nenhuma linha de cabeçalho encontrada
91 No record found for: %s Nenhum registo encontrado: %s
92 None Nenhum
93 Object Objeto
94 Object data anonymize
95 Object data config Configuração dos dados do objeto
96 Object data config export
97 Object data config exports
98 Object not found: %s Objeto não encontrado: %s
99 Objects Objetos
100 Path Caminho
101 Path Field
102 Permission Assistant Assistente de permissão
103 Query Consulta
104 Query/path Consulta/caminho
105 Read Ler
106 Readonly If Apenas leitura Se
107 Record Registrar
108 Record name
109 Record select
110 Refresh Apps Atualizar aplicativos
111 Relative Date
112 Replace Substituir
113 Restore Restaurar
114 Restore Backup Restaurar backup
115 Restored On Restaurado em
116 Return to draft status Retornar ao status de esboço
117 Roles Funções
118 Roles imported Funções importadas
119 Roles imported successfully Funções importadas com sucesso
120 Roles not found: %s Funções não encontradas: %s
121 Select all Selecionar todos
122 Sequence Seqüência
123 Status Estado
124 Tab name Nome da guia
125 Technical maintenance Manutenção técnica
126 This app is used by %s. Please deactivate them before continue. Este aplicativo é usado por %s. Por favor, desactive-os antes de continuar.
127 Title Título
128 Type Tipo de
129 Uninstall Desinstalar
130 Update Import Id
131 User Usuário
132 User access config Configuração de acesso do usuário
133 User field Campo do usuário
134 Validated
135 Write Escrever
136 {{$fmt('name')}} {{$fmt('name')}}

View File

@ -0,0 +1,136 @@
"key","message","comment","context"
"Access config","Конфигурация доступа",,
"Access config imported successfully","Доступ к успешно импортированной конфигурации доступа",,
"Action to perform",,,
"Anonymize",,,
"Anonymize data","Анонимные данные",,
"App","Приложение",,
"Application Config","Настройка приложений",,
"Apps","Приложения",,
"Apps installed successfully","Успешно установленные приложения",,
"Apps management","Управление приложениями",,
"Apps refreshed successfully","Приложения успешно обновились",,
"Are you sure wants to anonymize data ?.","Вы уверены, что хотите обезличить данные?",,
"Are you sure wants to import demo data for '${name}'?","Вы уверены, что хотите импортировать демо-данные для '${имя}'?",,
"Are you sure wants to install '${name}'?","Ты уверен, что хочешь установить '${имя}'?",,
"Are you sure wants to uninstall '${name}'?","Ты уверен, что хочешь удалить '${имя}'?",,
"Backup","Резервное копирование",,
"Backup Created","Резервное копирование Создано",,
"Backup File","Резервный файл",,
"Backup On","Резервное копирование Вкл",,
"Backup Restore Error","Резервное копирование Восстановление Ошибка",,
"Backup Restored","Резервное копирование Восстановлено",,
"Bad header row:","Плохой заголовок:",,
"Bad import file","Плохой файл импорта",,
"Bulk install","Массовая установка",,
"CSV","РЕЗЮМЕ",,
"CSV Backup Fetch Limit","Лимит на резервное копирование CSV",,
"Cancelled",,,
"Code","Код",,
"Comment",,,
"Condition","Условие",,
"ConditionParams","УсловияПарамы",,
"Configure","Настроить",,
"Create","Создать",,
"Create Backup","Создать резервное копирование",,
"Create file","Создать файл",,
"Data","Данные",,
"Data config line","Строка конфигурирования данных",,
"Delete","Удалить",,
"Demo data loaded","Загружены демонстрационные данные",,
"Demo data loaded successfully","Успешная загрузка демонстрационных данных",,
"Depends on","Зависит от",,
"Description","Описание",,
"Details","Детали",,
"Draft","Проект",,
"Error in import. Please check log.","Ошибка при импорте. Пожалуйста, проверьте журнал.",,
"Error in import: %s. Please check the server log","Ошибка при импорте: %s. Пожалуйста, проверьте журнал сервера",,
"Error in refreshing app","Ошибка в обновляющемся приложении",,
"Excel","Excel",,
"Export","Экспорт",,
"Export file","Экспорт файла",,
"Export format","Формат экспорта",,
"Exports",,,
"Field","Поле",,
"Field permission","Разрешение на эксплуатацию",,
"Fields to delete","Поля для удаления",,
"Fields to export",,,
"File","Файл",,
"Group Menu Assistant","Помощник по меню в группе",,
"Groups","Группы",,
"Groups not found: %s","Не найденные группы: %s",,
"Hide If","Скрыть Если",,
"Image","Изображение",,
"Import","Импорт",,
"Import ID","Импорт ID",,
"Import access config","Импорт конфигурации доступа к импорту",,
"Import completed successfully","Успешно завершен импорт",,
"Import date","Дата импорта",,
"Import demo data","Импорт демонстрационных данных",,
"Import permissions","Разрешения на импорт",,
"Import roles","Важные роли",,
"Imported from","Импортировано из",,
"In Progress","В процессе",,
"Init data loaded","Загружены исходные данные",,
"Install","Установить",,
"Install order","Установка заказа",,
"Installed","Установленный",,
"Language","Язык",,
"Lines","Линии",,
"Log","Журнал",,
"Menu not found: %s","Меню не найдено: %s",,
"Menus","Меню",,
"Meta Permissions",,,
"Model","Модель",,
"Model select",,,
"Modules contains in the app","Модули, содержащиеся в приложении",,
"Name","Имя",,
"No application language set. Please set 'application.locale' property.","Язык приложения не установлен. Пожалуйста, установите свойство 'application.locale'.",,
"No configuration required","Конфигурация не требуется",,
"No header row found","Заголовок строки не найден",,
"No record found for: %s","Записи не найдено: %s",,
"None","Нет",,
"Object","Объект",,
"Object data anonymize",,,
"Object data config","Конфигурация данных объекта",,
"Object data config export",,,
"Object data config exports",,,
"Object not found: %s","Объект не найден: %s",,
"Objects","Объекты",,
"Path","Путь",,
"Path Field",,,
"Permission Assistant","Помощник по разрешениям",,
"Query","Запрос",,
"Query/path","Запрос/путь",,
"Read","Читать",,
"Readonly If","Только для чтения Если",,
"Record","Запись",,
"Record name",,,
"Record select",,,
"Refresh Apps","Обновить приложения",,
"Relative Date",,,
"Replace","Заменить",,
"Restore","Восстановить",,
"Restore Backup","Восстановить резервное копирование",,
"Restored On","Восстановлен вкл.",,
"Return to draft status","Возврат к статусу черновика",,
"Roles","Роли",,
"Roles imported","Импортированные роли",,
"Roles imported successfully","Роли успешно импортированы",,
"Roles not found: %s","Роли не найдены: %s",,
"Select all","Выберите все",,
"Sequence","Последовательность",,
"Status","Статус",,
"Tab name","Имя вкладки",,
"Technical maintenance","Техническое обслуживание",,
"This app is used by %s. Please deactivate them before continue.","Это приложение используется в %s. Пожалуйста, деактивируйте их перед продолжением.",,
"Title","Название",,
"Type","Тип",,
"Uninstall","Удалить",,
"Update Import Id",,,
"User","Пользователь",,
"User access config","Конфигурация доступа пользователя",,
"User field","Поле пользователя",,
"Validated",,,
"Write","Напиши",,
"{{$fmt('name')}}","{{$fmt('name')}}",,
1 key message comment context
2 Access config Конфигурация доступа
3 Access config imported successfully Доступ к успешно импортированной конфигурации доступа
4 Action to perform
5 Anonymize
6 Anonymize data Анонимные данные
7 App Приложение
8 Application Config Настройка приложений
9 Apps Приложения
10 Apps installed successfully Успешно установленные приложения
11 Apps management Управление приложениями
12 Apps refreshed successfully Приложения успешно обновились
13 Are you sure wants to anonymize data ?. Вы уверены, что хотите обезличить данные?
14 Are you sure wants to import demo data for '${name}'? Вы уверены, что хотите импортировать демо-данные для '${имя}'?
15 Are you sure wants to install '${name}'? Ты уверен, что хочешь установить '${имя}'?
16 Are you sure wants to uninstall '${name}'? Ты уверен, что хочешь удалить '${имя}'?
17 Backup Резервное копирование
18 Backup Created Резервное копирование Создано
19 Backup File Резервный файл
20 Backup On Резервное копирование Вкл
21 Backup Restore Error Резервное копирование Восстановление Ошибка
22 Backup Restored Резервное копирование Восстановлено
23 Bad header row: Плохой заголовок:
24 Bad import file Плохой файл импорта
25 Bulk install Массовая установка
26 CSV РЕЗЮМЕ
27 CSV Backup Fetch Limit Лимит на резервное копирование CSV
28 Cancelled
29 Code Код
30 Comment
31 Condition Условие
32 ConditionParams УсловияПарамы
33 Configure Настроить
34 Create Создать
35 Create Backup Создать резервное копирование
36 Create file Создать файл
37 Data Данные
38 Data config line Строка конфигурирования данных
39 Delete Удалить
40 Demo data loaded Загружены демонстрационные данные
41 Demo data loaded successfully Успешная загрузка демонстрационных данных
42 Depends on Зависит от
43 Description Описание
44 Details Детали
45 Draft Проект
46 Error in import. Please check log. Ошибка при импорте. Пожалуйста, проверьте журнал.
47 Error in import: %s. Please check the server log Ошибка при импорте: %s. Пожалуйста, проверьте журнал сервера
48 Error in refreshing app Ошибка в обновляющемся приложении
49 Excel Excel
50 Export Экспорт
51 Export file Экспорт файла
52 Export format Формат экспорта
53 Exports
54 Field Поле
55 Field permission Разрешение на эксплуатацию
56 Fields to delete Поля для удаления
57 Fields to export
58 File Файл
59 Group Menu Assistant Помощник по меню в группе
60 Groups Группы
61 Groups not found: %s Не найденные группы: %s
62 Hide If Скрыть Если
63 Image Изображение
64 Import Импорт
65 Import ID Импорт ID
66 Import access config Импорт конфигурации доступа к импорту
67 Import completed successfully Успешно завершен импорт
68 Import date Дата импорта
69 Import demo data Импорт демонстрационных данных
70 Import permissions Разрешения на импорт
71 Import roles Важные роли
72 Imported from Импортировано из
73 In Progress В процессе
74 Init data loaded Загружены исходные данные
75 Install Установить
76 Install order Установка заказа
77 Installed Установленный
78 Language Язык
79 Lines Линии
80 Log Журнал
81 Menu not found: %s Меню не найдено: %s
82 Menus Меню
83 Meta Permissions
84 Model Модель
85 Model select
86 Modules contains in the app Модули, содержащиеся в приложении
87 Name Имя
88 No application language set. Please set 'application.locale' property. Язык приложения не установлен. Пожалуйста, установите свойство 'application.locale'.
89 No configuration required Конфигурация не требуется
90 No header row found Заголовок строки не найден
91 No record found for: %s Записи не найдено: %s
92 None Нет
93 Object Объект
94 Object data anonymize
95 Object data config Конфигурация данных объекта
96 Object data config export
97 Object data config exports
98 Object not found: %s Объект не найден: %s
99 Objects Объекты
100 Path Путь
101 Path Field
102 Permission Assistant Помощник по разрешениям
103 Query Запрос
104 Query/path Запрос/путь
105 Read Читать
106 Readonly If Только для чтения Если
107 Record Запись
108 Record name
109 Record select
110 Refresh Apps Обновить приложения
111 Relative Date
112 Replace Заменить
113 Restore Восстановить
114 Restore Backup Восстановить резервное копирование
115 Restored On Восстановлен вкл.
116 Return to draft status Возврат к статусу черновика
117 Roles Роли
118 Roles imported Импортированные роли
119 Roles imported successfully Роли успешно импортированы
120 Roles not found: %s Роли не найдены: %s
121 Select all Выберите все
122 Sequence Последовательность
123 Status Статус
124 Tab name Имя вкладки
125 Technical maintenance Техническое обслуживание
126 This app is used by %s. Please deactivate them before continue. Это приложение используется в %s. Пожалуйста, деактивируйте их перед продолжением.
127 Title Название
128 Type Тип
129 Uninstall Удалить
130 Update Import Id
131 User Пользователь
132 User access config Конфигурация доступа пользователя
133 User field Поле пользователя
134 Validated
135 Write Напиши
136 {{$fmt('name')}} {{$fmt('name')}}

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<csv-inputs xmlns="http://axelor.com/xml/ns/data-import"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/data-import http://axelor.com/xml/ns/data-import/data-import_5.2.xsd">
<input file="base_permission.csv" separator=";" type="com.axelor.auth.db.Permission" search="self.name = :name" call="com.axelor.csv.script.ImportPermission:importPermissionToRole">
<bind to="canRead" eval="can_read == 'x' ? 'true' : 'false'"/>
<bind to="canWrite" eval="can_write == 'x' ? 'true' : 'false'"/>
<bind to="canCreate" eval="can_create == 'x' ? 'true' : 'false'"/>
<bind to="canRemove" eval="can_remove == 'x' ? 'true' : 'false'"/>
<bind to="canExport" eval="can_export == 'x' ? 'true' : 'false'"/>
</input>
</csv-inputs>

View File

@ -0,0 +1,16 @@
"name";"object";"can_read";"can_write";"can_create";"can_remove";"can_export";"condition";"conditionParams";"roleName"
"perm.auth.User.r";"com.axelor.auth.db.User";"x";;;;;;;"Base Read"
"perm.auth.User.rwc";"com.axelor.auth.db.User";"x";"x";"x";;;"self.id = ?";"__user__.id";"Base User"
"perm.auth.User.rwcde";"com.axelor.auth.db.User";"x";"x";"x";"x";"x";;;"Base Manager"
"perm.meta.r";"com.axelor.meta.db.*";"x";;;;;;;"Base Manager"
"perm.meta.r";"com.axelor.meta.db.*";"x";;;;;;;"Base User"
"perm.meta.r";"com.axelor.meta.db.*";"x";;;;;;;"Base Read"
"perm.mail.all";"com.axelor.mail.db.*";"x";"x";"x";"x";"x";;;"Base Manager"
"perm.mail.rwc";"com.axelor.mail.db.*";"x";"x";"x";;;;;"Base User"
"perm.mail.r";"com.axelor.mail.db.*";"x";;;;;;;"Base Read"
"perm.dms.all";"com.axelor.dms.db.*";"x";"x";"x";"x";"x";;;"Base Manager"
"perm.dms.rwc";"com.axelor.dms.db.*";"x";"x";"x";;;;;"Base User"
"perm.dms.r";"com.axelor.dms.db.*";"x";;;;;;;"Base Read"
"perm.team.all";"com.axelor.team.db.*";"x";"x";"x";"x";"x";;;"Base Manager"
"perm.team.rwc";"com.axelor.team.db.*";"x";"x";"x";;;;;"Base User"
"perm.team.r";"com.axelor.team.db.*";"x";;;;;;;"Base Read"
1 name object can_read can_write can_create can_remove can_export condition conditionParams roleName
2 perm.auth.User.r com.axelor.auth.db.User x Base Read
3 perm.auth.User.rwc com.axelor.auth.db.User x x x self.id = ? __user__.id Base User
4 perm.auth.User.rwcde com.axelor.auth.db.User x x x x x Base Manager
5 perm.meta.r com.axelor.meta.db.* x Base Manager
6 perm.meta.r com.axelor.meta.db.* x Base User
7 perm.meta.r com.axelor.meta.db.* x Base Read
8 perm.mail.all com.axelor.mail.db.* x x x x x Base Manager
9 perm.mail.rwc com.axelor.mail.db.* x x x Base User
10 perm.mail.r com.axelor.mail.db.* x Base Read
11 perm.dms.all com.axelor.dms.db.* x x x x x Base Manager
12 perm.dms.rwc com.axelor.dms.db.* x x x Base User
13 perm.dms.r com.axelor.dms.db.* x Base Read
14 perm.team.all com.axelor.team.db.* x x x x x Base Manager
15 perm.team.rwc com.axelor.team.db.* x x x Base User
16 perm.team.r com.axelor.team.db.* x Base Read

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid name="access-config-grid" title="Access config" model="com.axelor.apps.base.db.AccessConfig">
<field name="name" />
</grid>
<form name="access-config-form" title="Access config" model="com.axelor.apps.base.db.AccessConfig">
<panel name="mainPanel">
<field name="name" />
<field name="roleSet" colSpan="12"/>
</panel>
</form>
</object-views>

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid name="app-grid" title="Apps" model="com.axelor.apps.base.db.App" canDelete="false" canNew="false">
<field name="name" />
<field name="active" />
<field name="demoDataLoaded" />
</grid>
<form name="app-form" title="App" model="com.axelor.apps.base.db.App" canDelete="false" canNew="false" width="large">
<panel name="mainPanel">
<field name="image" widget="Image" colSpan="2"/>
<panel colSpan="6" name="infoPanel">
<field name="name" readonly="true" colSpan="12" />
<field name="languageSelect" selection-in="['en','fr']"/>
<field name="demoDataLoaded" readonly="true" if="com.axelor.app.AppSettings.get().get('application.mode') != 'prod'"/>
</panel>
<field name="description" colSpan="12" readonly="true"/>
<field name="initDataLoaded" hidden="true" />
<field name="code" hidden="true" />
<field name="active" hidden="true" />
<field name="isRolesImported" hidden="true" />
</panel>
<panel itemSpan="12" sidebar="true" name="actionPanel">
<button name="installBtn" title="Install" onClick="action-app-alert-install,save,action-app-method-install-app" showIf="!active" />
<button name="uninstallBtn" title="Uninstall" onClick="action-app-alert-un-install,action-app-method-uninstall-app" showIf="active" />
<button name="configureBtn" title="Configure" showIf="active" onClick="com.axelor.apps.base.web.AppController:configure" />
<button name="importDataDemoBtn" if="com.axelor.app.AppSettings.get().get('application.mode') != 'prod'" showIf="active &amp;&amp; !demoDataLoaded" onClick="action-app-alert-demo-import,save,action-method-app-import-data-demo" title="Import demo data" />
<button name="importRolesBtn" showIf="active &amp;&amp; !isRolesImported" onClick="save,action-app-method-import-roles" title="Import roles" />
</panel>
<panel-related name="dependsOnSetPanel" field="dependsOnSet" readonly="true" canSelect="false" canView="false" >
<field name="name" />
<field name="active"/>
</panel-related>
</form>
<form model="com.axelor.apps.base.db.App" title="Bulk install" name="bulk-install-app-form">
<panel name="mainPanel">
<button name="selectAllBtn" title="Select all" colSpan="4" onClick="action-app-bulk-install-select-all,action-app-bulk-install-apps-set-change"/>
<field type="many-to-many" name="appsSet" target="com.axelor.apps.base.db.App" colSpan="12" title="Apps" canNew="false" domain="self.active = false or self.demoDataLoaded = false" height="200" onChange="action-app-bulk-install-apps-set-change"/>
<field name="languageSelect" type="string" selection="select.language" title="Language" colSpan="4" selection-in="['en','fr']"/>
<field type="boolean" name="importDemoData" title="Import demo data" colSpan="3" if="com.axelor.app.AppSettings.get().get('application.mode') != 'prod'"/>
<button name="installBtn" title="Install" colSpan="4" onClick="action-app-method-bulk-install" hidden="true"/>
</panel>
</form>
<form model="com.axelor.apps.base.db.Wizard" title="Import access config" name="import-access-config-wizard-form">
<panel name="wizardPanel">
<field type="many-to-one" name="metaFile" title="File" widget="binary-link" target="com.axelor.meta.db.MetaFile"/>
<button name="importAccessBtn" title="Import" colSpan="4" onClick="action-app-method-import-access-config" readonlyIf="metaFile == null"/>
</panel>
</form>
<cards name="app-cards" model="com.axelor.apps.base.db.App" title="Apps" css="rect-image" orderBy="sequence" width="25%:200" >
<toolbar>
<button name="bulkInstallBtn" title="Bulk install" onClick="action-app-open-bulk-install-selector"/>
<button name="refreshAppBtn" title="Refresh Apps" onClick="action-app-method-refresh-app"/>
<button name="importRolesBtn" title="Import roles" onClick="action-app-method-import-all-roles" />
</toolbar>
<field name="name" />
<field name="code" x-bind="{{code|unaccent|uppercase}}" />
<field name="image" />
<field name="description"/>
<field name="active"/>
<field name="sequence"/>
<template><![CDATA[
<div>
<div class="span4 card-image">
<img ng-src="{{$image('image', 'content')}}" />
</div>
<div class="span8">
<strong x-translate>{{$fmt('name')}}</strong>
</div>
<div class="span12">
<a class="btn btn-small btn-info pull-right" href="" ng-show="!active" ui-action-click="action-app-alert-install,action-app-method-install-app" x-translate>Install</a>
<a class="btn btn-small btn-success pull-right" href="" ng-show="active" ui-action-click="com.axelor.apps.base.web.AppController:configure" x-translate>Configure</a>
<a class="btn btn-small btn-danger pull-right" href="" ng-show="active" ui-action-click="action-app-alert-un-install,action-app-method-uninstall-app" x-translate>Uninstall</a>
</div>
</div>
]]>
</template>
</cards>
<action-record name="action-app-bulk-install-select-all" model="com.axelor.apps.base.db.App">
<field name="appsSet" expr="eval:(__repo__(App).all().filter('self.active = false or self.demoDataLoaded = false').fetch()).toSet()"/>
</action-record>
<action-method name="action-method-app-import-data-demo">
<call class="com.axelor.apps.base.web.AppController" method="importDataDemo"/>
</action-method>
<action-method name="action-app-method-install-app">
<call class="com.axelor.apps.base.web.AppController" method="installApp" />
</action-method>
<action-method name="action-app-method-uninstall-app">
<call class="com.axelor.apps.base.web.AppController" method="uninstallApp" />
</action-method>
<action-method name="action-app-method-bulk-install" >
<call class="com.axelor.apps.base.web.AppController" method="bulkInstall" />
</action-method>
<action-method name="action-app-method-refresh-app">
<call class="com.axelor.apps.base.web.AppController" method="refreshApp"/>
</action-method>
<action-method name="action-app-method-import-roles">
<call class="com.axelor.apps.base.web.AppController" method="importRoles"/>
</action-method>
<action-method name="action-app-method-import-all-roles">
<call class="com.axelor.apps.base.web.AppController" method="importAllRoles"/>
</action-method>
<action-method name="action-app-method-generate-access-template">
<call class="com.axelor.apps.base.web.AppController" method="generateAccessTemplate"/>
</action-method>
<action-method name="action-app-method-import-access-config">
<call class="com.axelor.apps.base.web.AppController" method="importAccessConfig"/>
</action-method>
<action-view name="action-app-open-bulk-install-selector" title="Bulk install" model="com.axelor.apps.base.db.App">
<view type="form" name="bulk-install-app-form"/>
<view-param name="show-toolbar" value="false"/>
<view-param name="popup" value="true" />
<view-param name="popup-save" value="false" />
<view-param name="show-confirm" value="false" />
<view-param name="height" value="400" />
</action-view>
<action-view name="action-app-view-import-access-config-wizard" title="Import access config" model="com.axelor.apps.base.db.Wizard">
<view type="form" name="import-access-config-wizard-form"/>
<view-param name="show-toolbar" value="false"/>
<view-param name="popup" value="true" />
<view-param name="popup-save" value="false" />
<view-param name="show-confirm" value="false" />
</action-view>
<action-attrs name="action-app-on-load-attrs">
<attribute name="hidden" for="configureBtn" expr="eval:!__self__.active"/>
<attribute name="hidden" for="demoDataLoaded" expr="eval:!__self__.active"/>
<attribute name="hidden" for="importDataDemoBtn" expr="eval:!__self__.active || demoDataLoaded"/>
</action-attrs>
<action-attrs name="action-app-bulk-install-apps-set-change">
<attribute name="hidden" for="installBtn" expr="eval:appsSet == null || appsSet.empty"/>
</action-attrs>
<action-validate name="action-app-alert-install">
<alert message="Are you sure wants to install '${name}'?"/>
</action-validate>
<action-validate name="action-app-alert-un-install">
<alert message="Are you sure wants to uninstall '${name}'?"/>
</action-validate>
<action-validate name="action-app-alert-demo-import">
<alert message="Are you sure wants to import demo data for '${name}'?"/>
</action-validate>
</object-views>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid model="com.axelor.apps.base.db.DataBackup" title="Create Backup"
name="data-create-backup-grid">
<field name="statusSelect" />
<field name="backupMetaFile" widget="binary-link" />
<field name="backupDate" />
</grid>
<form model="com.axelor.apps.base.db.DataBackup" title="Create Backup"
name="data-create-backup-form" onNew="data.create.backup.load.today.date"
width="large">
<panel name="mainPanel">
<field name="statusSelect" readonly="true" widget="NavSelect"
selection-in="[0,1,2]" colSpan="12" />
<field name="fetchLimit" widget="Integer" required="true" min="1"
colSpan="4" />
<field name="isRelativeDate" widget="boolean-switch" onChange="action-data-backup-attrs-onchange-relativedate" colSpan="2"/>
<field name="updateImportId" widget="boolean-switch" showIf="isRelativeDate" colSpan="2"/>
<spacer colSpan="4"/>
<button-group name="mainBtnGroup" colSpan="3">
<button name="backToDraftBtn" title="Return to draft status"
showIf="backupMetaFile != null &amp;&amp; statusSelect == 1"
colSpan="4" onClick="data.create.backup.set.draft.status" />
<button name="createBtn" title="Create"
showIf="backupMetaFile == null &amp;&amp; statusSelect != 1"
colSpan="4" onClick="save,action.data.backup.call.createbackup,save" />
</button-group>
<spacer name="btnGrpSpacer" colSpan="12"/>
<field name="backupMetaFile" readonly="true" widget="binary-link"
colSpan="4" />
<field name="backupDate" readonly="true" colSpan="4" />
</panel>
</form>
<grid model="com.axelor.apps.base.db.DataBackup" title="Restore Backup"
name="data-restore-backup-grid">
<field name="statusSelect" />
<field name="backupMetaFile" widget="binary-link" />
<field name="restoreDate" />
</grid>
<form model="com.axelor.apps.base.db.DataBackup" title="Restore Backup"
name="data-restore-backup-form" width="mid"
onNew="data.restore.backup.load.today.date">
<panel>
<field name="statusSelect" selection-in="[0,1,3,4]" readonly="true"
widget="NavSelect" colSpan="12" />
<field name="backupMetaFile" required="true"
domain="self.fileName LIKE '%.zip'" colSpan="3" widget="binary-link"/>
<button name="restoreBtn" title="Restore"
showIf="backupMetaFile != null &amp;&amp; statusSelect != 2"
onClick="save,action.data.backup.call.restorebackup,save" colSpan="3" />
<spacer name="restoreBtnSpacer" />
<field name="restoreDate" readonly="true" colSpan="3" />
<field name="logMetaFile" readonly="true" widget="binary-link"
colSpan="3" />
</panel>
</form>
<action-record name="data.create.backup.load.today.date"
model="com.axelor.apps.base.db.DataBackup">
<field name="backupDate" expr="eval: __datetime__" />
<field name="fetchLimit" expr="100" />
</action-record>
<action-record name="data.restore.backup.load.today.date"
model="com.axelor.apps.base.db.DataBackup">
<field name="restoreDate" expr="eval: __datetime__" />
</action-record>
<action-record name="data.create.backup.set.draft.status"
model="com.axelor.apps.base.db.DataBackup">
<field name="statusSelect" expr="0" />
<field name="backupMetaFile" expr="" />
</action-record>
<action-method name="action.data.backup.call.createbackup">
<call class="com.axelor.apps.base.web.DataBackupController"
method="CreateBackUp" />
</action-method>
<action-method name="action.data.backup.call.restorebackup">
<call class="com.axelor.apps.base.web.DataBackupController"
method="RestoreBackUp" />
</action-method>
<action-attrs name="action-data-backup-attrs-onchange-relativedate">
<attribute name="value" for="updateImportId" expr="false" if="!isRelativeDate"/>
</action-attrs>
</object-views>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid name="data-config-line-grid" title="Data config line" model="com.axelor.apps.base.db.DataConfigLine">
<field name="metaModel" />
<field name="path" required="true" />
<field name="metaFieldPath"/>
</grid>
<form name="data-config-line-form" title="Data config line"
model="com.axelor.apps.base.db.DataConfigLine" width="large">
<panel name="mainPanel">
<field name="metaModel" canEdit="false" />
<field name="tabName" />
<field name="typeSelect"/>
<field name="metaFieldPath" hidden="true" showIf="typeSelect == 0" requiredIf="typeSelect == 0" onSelect="action-object-attrs-metaFieldPath-onselect"/>
<field name="path" hidden="true" showIf="typeSelect == 1" requiredIf="typeSelect == 1" colSpan="12"/>
<panel-related name="toExportMetaFieldSetPanel" field="toExportMetaFieldSet" canNew="false" canEdit="false" colSpan="12"
domain="self.metaModel = :metaModel and (self.relationship is null or self.relationship not in ('ManyToMany','OneToMany'))">
<field name="name" />
<field name="label" />
<field name="typeName" />
</panel-related>
<field name="resetPathSelect" showIf="typeSelect == 0" onChange="action-data-config-line-record-set-record-model"/>
<field name="record" widget="RefSelect" x-related="recordSelectId" selection-in="null" showIf="resetPathSelect == 1 &amp;&amp; typeSelect == 0"/>
<field name="recordSelectId" hidden="true" />
<panel-related name="toDeleteMetaFieldSetsPanel" field="toDeleteMetaFieldSet" canNew="false" canEdit="false" colSpan="12"
domain="self in (:toExportMetaFieldSet)">
<field name="name" />
<field name="label" />
<field name="typeName" />
</panel-related>
</panel>
</form>
<action-record name="action-data-config-line-record-set-record-model" model="com.axelor.apps.base.db.DataConfigLine">
<field name="record" expr="eval: _parent?.modelSelect" if="resetPathSelect == 1 &amp;&amp; typeSelect == 0"/>
<field name="recordSelectId" expr="0" if="resetPathSelect == 1 &amp;&amp; typeSelect == 0"/>
<field name="record" expr="eval: null" if="resetPathSelect != 1 || typeSelect != 0"/>
</action-record>
<action-attrs name="action-object-attrs-metaFieldPath-onselect">
<attribute name="domain" expr="eval:&quot; self.metaModel = :metaModel AND CONCAT(self.packageName ,'.',self.typeName) = '${_parent?.modelSelect}'&quot;" for="metaFieldPath"/>
</action-attrs>
</object-views>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<menuitem name="menu-auth-permission-assistant" parent="menu-auth" title="Permission Assistant"
action="action-auth-permission-assistant"/>
<action-view name="action-auth-permission-assistant" title="Permission Assistant"
model="com.axelor.auth.db.PermissionAssistant">
<view type="grid" name="permission-assistant-grid" />
<view type="form" name="permission-assistant-form" />
</action-view>
<menuitem name="menu-meta-group-menu-assistant" parent="menu-auth" title="Group Menu Assistant"
action="action-group-menu-assistant" />
<action-view name="action-group-menu-assistant" title="Group Menu Assistant"
model="com.axelor.meta.db.MetaGroupMenuAssistant">
<view type="grid" name="meta-group-menu-assistant-grid" />
<view type="form" name="meta-group-menu-assistant-form" />
</action-view>
<menuitem name="menu-meta-permission" parent="menu-auth" title="Meta Permissions"
action="action-meta-permission" />
<action-view name="action-meta-permission" title="Meta Permissions"
model="com.axelor.meta.db.MetaPermission">
<view type="grid" name="meta-permission-grid" />
<view type="form" name="meta-permission-form" />
</action-view>
<menuitem name="admin-root" title="Application Config" order="-60"
icon="fa-gear" icon-background="#31b4a9"/>
<menuitem name="admin-root-app-management" parent="admin-root" order="5"
title="Apps management" action="admin.root.app.management" />
<action-view name="admin.root.app.management" title="Apps management"
model="com.axelor.apps.base.db.App" >
<view type="cards" name="app-cards"/>
<view type="grid" name="app-grid"/>
<view type="form" name="app-form"/>
</action-view>
<menuitem name="admin-root-maintenance" parent="admin-root" if="__config__.app.isApp('base')"
title="Technical maintenance" order="45" />
<menuitem name="admin-root-object-data-config" parent="admin-root-maintenance"
title="Object data config" action="admin.root.object.data.config" order="10"/>
<action-view name="admin.root.object.data.config" title="Object data config"
model="com.axelor.apps.base.db.ObjectDataConfig" >
<view type="grid" name="object-data-config-grid"/>
<view type="form" name="object-data-config-form"/>
</action-view>
<menuitem name="admin-root-data-backup" parent="admin-root-maintenance" title="Backup" order="11"/>
<menuitem name="admin-root-data-create-backup" parent="admin-root-data-backup"
title="Create Backup" action="admin.root.data.create.backup"/>
<action-view name="admin.root.data.create.backup" title="Create Backup"
model="com.axelor.apps.base.db.DataBackup" >
<view type="grid" name="data-create-backup-grid"/>
<view type="form" name="data-create-backup-form"/>
<domain>self.backupDate != null</domain>
</action-view>
<menuitem name="admin-root-data-restore-backup" parent="admin-root-data-backup"
title="Restore Backup" action="admin.root.data.restore.backup"/>
<action-view name="admin.root.data.restore.backup" title="Restore Backup"
model="com.axelor.apps.base.db.DataBackup" >
<view type="grid" name="data-restore-backup-grid"/>
<view type="form" name="data-restore-backup-form"/>
<domain>self.restoreDate != null</domain>
</action-view>
</object-views>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid title="Group Menu Assistant" name="meta-group-menu-assistant-grid" model="com.axelor.meta.db.MetaGroupMenuAssistant">
<field name="metaFile" />
<field name="importDate" />
<field name="createdBy" />
<field name="updatedBy" />
</grid>
<form title="Group Menu Assistant" name="meta-group-menu-assistant-form" model="com.axelor.meta.db.MetaGroupMenuAssistant" width="large">
<panel name="mainPanel" >
<panel-related name="groupSetPanel" field="groupSet" colSpan="6" />
<panel-related name="roleSetPanel" field="roleSet" colSpan="6" />
<panel-related name="menuSetPanel" field="menuSet" grid-view="simple-meta-menu-grid" colSpan="12"/>
<field name="metaFile" colSpan="6" widget="binary-link" />
<field name="language" readonlyIf="metaFile"/>
<button name="createFileBtn" title="Create file" colSpan="3" onClick="save,action-meta-method-create-group-menu-file"/>
<button name="importFileBtn" title="Import" readonlyIf="metaFile == null" colSpan="3" onClick="save,action-meta-method-import-group-menu,save"/>
<panel name="detailsPanel" title="Details" colSpan="12">
<field name="importDate" colSpan="4"/>
<field name="createdBy" colSpan="4"/>
<field name="updatedBy" colSpan="4"/>
</panel>
<field name="log" colSpan="12"/>
</panel>
</form>
<action-method name="action-meta-method-create-group-menu-file">
<call class="com.axelor.meta.web.MetaGroupMenuAssistantController" method="createGroupMenuFile"/>
</action-method>
<action-method name="action-meta-method-import-group-menu">
<call class="com.axelor.meta.web.MetaGroupMenuAssistantController" method="importGroupMenu"/>
</action-method>
</object-views>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid name="object-data-config-grid" title="Object data config" model="com.axelor.apps.base.db.ObjectDataConfig">
<field name="title"/>
<field name="modelSelect"/>
</grid>
<form name="object-data-config-form" title="Object data config" model="com.axelor.apps.base.db.ObjectDataConfig"
onNew="action-object-data-config-record-on-onnew">
<panel name="overviewPanel">
<field name="statusSelect" showTitle="false" readonly="true" colSpan="12" widget="NavSelect"/>
<panel name="mainPanel" colSpan="12">
<field name="title"/>
<field name="modelSelect"/>
</panel>
</panel>
<panel name="contentPanel" colSpan="12">
<panel-related name="dataConfigLineListPanel" field="dataConfigLineList" colSpan="12" />
<field name="comment" widget="HTML" colSpan="12"/>
</panel>
<panel-dashlet name="exportByObject" title="Exports" action="action-object-data-config-dashlet-export-by-object" colSpan="12"/>
<panel name="actionPanel" sidebar="true">
<button name="exportBtn" title="Export" onClick="save,action-open-object-data-export-form"/>
<button name="anonymizeBtn" title="Anonymize" onClick="save,action-open-object-data-anonymize-wizard"/>
<button name="draftBtn" title="Draft" showIf="statusSelect == 3" onClick="action-object-data-config-record-set-status,save"/>
<button name="validatedBtn" title="Validated" showIf="statusSelect == 1" onClick="save,action-object-data-config-record-set-status,save"/>
<button name="cancelledBtn" title="Cancelled" showIf="statusSelect == 2" onClick="save,action-object-data-config-record-set-status,save"/>
</panel>
</form>
<action-record name="action-object-data-config-record-on-onnew" model="com.axelor.apps.base.db.ObjectDataConfig">
<field name="statusSelect" expr="1"/>
</action-record>
<action-record name="action-object-data-config-record-set-status" model="com.axelor.apps.base.db.ObjectDataConfig">
<field name="statusSelect" expr="1" if="statusSelect == 3"/>
<field name="statusSelect" expr="2" if="statusSelect == 1"/>
<field name="statusSelect" expr="3" if="statusSelect == 2"/>
</action-record>
<action-view name="action-open-object-data-export-form" title="Export" model="com.axelor.apps.base.db.ObjectDataConfigExport">
<view type="form" name="object-data-config-export-form" />
<view-param name="show-toolbar" value="false"/>
<view-param name="show-confirm" value="false"/>
<view-param name="popup" value="reload"/>
<view-param name="popup-save" value="false"/>
<context name="_objectDataConfig" expr="eval:__this__" />
</action-view>
<action-view name="action-open-object-data-anonymize-wizard" title="Anonymize" model="com.axelor.apps.base.db.Wizard">
<view type="form" name="object-data-anonymize-wizard-form" />
<view-param name="show-toolbar" value="false"/>
<view-param name="show-confirm" value="false"/>
<view-param name="popup" value="true"/>
<view-param name="popup-save" value="false"/>
<context name="_modelSelect" expr="eval:modelSelect"/>
<context name="_objectDataConfigId" expr="eval:id" />
</action-view>
<action-view name="action-object-data-config-dashlet-export-by-object" title="Exports" model="com.axelor.apps.base.db.ObjectDataConfigExport">
<view type="grid" name="object-data-config-export-grid"/>
<view type="form" name="object-data-config-export-form"/>
<domain>self.objectDataConfig.id = :id</domain>
<context name="id" expr="eval: id"/>
</action-view>
</object-views>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid name="object-data-config-export-grid" title="Object data config exports" model="com.axelor.apps.base.db.ObjectDataConfigExport">
<field name="recordName"/>
<field name="createdOn"/>
<field name="exportFormatSelect"/>
</grid>
<form name="object-data-config-export-form" title="Object data config export" model="com.axelor.apps.base.db.ObjectDataConfigExport"
onNew="action-object-data-config-export-record-on-onnew">
<panel name="mainPanel">
<field name="modelSelect" title="Model" widget="RefSelect" x-related="modelSelectId" selection-in="null" />
<field name="exportFormatSelect" title="Export format" selection="object.data.export.format.select" type="string"/>
<field name="langSelect" title="Language" selection="select.language" type="string" />
<spacer name="langSelectSpacer"/>
<button name="exportBtn" title="Export" onClick="save,action-object-data-export-method-export" readonlyIf="modelSelectId == 0" />
<field name="modelSelectId" hidden="true" />
<field name="objectDataConfig" hidden="true"/>
</panel>
</form>
<action-record name="action-object-data-config-export-record-on-onnew" model="com.axelor.apps.base.db.ObjectDataConfigExport">
<field name="modelSelect" expr="eval:_objectDataConfig?.modelSelect"/>
<field name="langSelect" expr="eval:__user__.language"/>
<field name="objectDataConfig" expr="eval:_objectDataConfig"/>
<field name="exportFormatSelect" expr="eval:'csv'"/>
</action-record>
<action-method name="action-object-data-export-method-export">
<call class="com.axelor.apps.base.web.ObjectDataExportController" method="export"/>
</action-method>
</object-views>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<form name="object-data-anonymize-wizard-form" title="Object data anonymize" model="com.axelor.apps.base.db.Wizard"
onNew="action-object-data-anonymize-wizard-defaults">
<panel name="mainPanel">
<field name="modelSelect" title="Model" widget="RefSelect" x-related="modelSelectId" selection="object.data.config.model.select" selection-in="null" type="string" required="true"/>
<button name="anonymizeBtn" title="Anonymize data" onClick="action-object-data-export-wizard-validate-anonymize,action-object-data-export-wizard-method-anonymyize" readonlyIf="modelSelectId == 0" />
<field name="modelSelectId" hidden="true" />
<field name="objectDataConfigId" hidden="true"/>
</panel>
</form>
<action-attrs name="action-object-data-anonymize-wizard-defaults">
<attribute name="value" for="modelSelect" expr="eval:_modelSelect"/>
<attribute name="value" for="objectDataConfigId" expr="eval:_objectDataConfigId" />
</action-attrs>
<action-validate name="action-object-data-export-wizard-validate-anonymize">
<alert message="Are you sure wants to anonymize data ?."/>
</action-validate>
<action-method name="action-object-data-export-wizard-method-anonymyize">
<call class="com.axelor.apps.base.web.ObjectDataExportController" method="anonymize"/>
</action-method>
</object-views>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid title="Permission Assistant" name="permission-assistant-grid" model="com.axelor.auth.db.PermissionAssistant">
<field name="metaFile" />
<field name="importDate" />
<field name="createdBy" />
<field name="updatedBy" />
</grid>
<form name="permission-assistant-form" title="Permission Assistant" model="com.axelor.auth.db.PermissionAssistant">
<panel name="mainPanel" >
<field name="typeSelect" selection="permission.assistant.type.select" onChange="action-attr-reset-all-set"/>
<panel-related name="roleSetPanel" field="roleSet" showIf="typeSelect == 2" colSpan="12"/>
<panel-related name="groupSetPanel" field="groupSet" showIf="typeSelect == 1" colSpan="12"/>
<panel-related name="objectSetPanel" field="objectSet" colSpan="12"/>
<field name="metaField" domain="self.metaModel.name = 'User' and self.relationship != null" onChange="action-permission-assistant-method-fill-objects"/>
<field name="fieldPermission" />
<field name="metaFile" colSpan="12" widget="binary-link" />
<field name="language" />
<button name="createFileBtn" title="Create file" colSpan="3" onClick="save,action-permission-method-create-file"/>
<button name="importPermissionsBtn" title="Import permissions" readonlyIf="metaFile == null" colSpan="3" onClick="save,action-permission-method-import-permissions,save"/>
<panel name="detailsPanel" title="Details" readonly="true" colSpan="12">
<field name="importDate" colSpan="4"/>
<field name="createdBy" colSpan="4"/>
<field name="updatedBy" colSpan="4"/>
</panel>
<field name="log" colSpan="12" readonly="true" />
</panel>
</form>
<action-method name="action-permission-method-create-file" model="com.axelor.auth.db.PermissionAssistant">
<call class="com.axelor.auth.web.PermissionAssistantController" method="createFile"/>
</action-method>
<action-method name="action-permission-method-import-permissions" model="com.axelor.auth.db.PermissionAssistant">
<call class="com.axelor.auth.web.PermissionAssistantController" method="importPermissions"/>
</action-method>
<action-method name="action-permission-assistant-method-fill-objects">
<call class="com.axelor.auth.web.PermissionAssistantController" method="fillObjects"/>
</action-method>
<action-attrs name="action-attr-reset-all-set">
<attribute name="value" for="groupSet" expr="eval: null" if="typeSelect == 2"/>
<attribute name="value" for="roleSet" expr="eval: null" if="typeSelect == 1"/>
</action-attrs>
</object-views>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<selection name="permission.assistant.type.select">
<option value="1">Groups</option>
<option value="2">Roles</option>
</selection>
<selection name="object.data.config.model.select">
<option value="com.axelor.auth.db.User">User</option>
</selection>
<selection name="object.data.config.status.select">
<option value="1">Draft</option>
<option value="2">Validated</option>
<option value="3">Cancelled</option>
</selection>
<selection name="object.data.export.format.select">
<option value="csv">CSV</option>
<option value="xlsx">Excel</option>
</selection>
<selection name="data.config.line.type.select">
<option value="0">Path</option>
<option value="1">Query</option>
</selection>
<selection name="data.config.line.reset.path.select">
<option value="0">None</option>
<option value="1">Replace</option>
<option value="2">Delete</option>
</selection>
<selection name="data.backup.status.select">
<option value="0">Draft</option>
<option value="1">In Progress</option>
<option value="2">Backup Created</option>
<option value="3">Backup Restored</option>
<option value="4">Backup Restore Error</option>
</selection>
</object-views>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object-views xmlns="http://axelor.com/xml/ns/object-views"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://axelor.com/xml/ns/object-views http://axelor.com/xml/ns/object-views/object-views_5.2.xsd">
<grid name="user-access-config-grid" title="User access config" model="com.axelor.apps.base.db.UserAccessConfig">
<field name="app" />
<field name="accessConfig" domain="self.app = :app" />
</grid>
<form name="user-access-config-form" title="User access config" model="com.axelor.apps.base.db.UserAccessConfig" editable="true">
<panel name="mainPanel">
<field name="app" />
<field name="accessConfig" domain="self.app = :app" />
</panel>
</form>
</object-views>

View File

@ -0,0 +1,20 @@
/**
* Axelor Business Solutions
*
* Copyright (C) 2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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/>.
*/
.user-preferences .form-view.bar-layout.mini-form {
max-width: 720px;
}