/**
* Axelor Business Solutions
*
* Copyright (C) 2016 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.administration;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.axelor.app.AppSettings;
import com.axelor.apps.base.service.user.UserService;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.auth.db.Group;
import com.axelor.auth.db.repo.GroupRepository;
import com.axelor.meta.db.repo.MetaFileRepository;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaAction;
import com.axelor.meta.db.MetaFile;
import com.axelor.meta.db.MetaMenu;
import com.axelor.meta.db.repo.MetaMenuRepository;
import com.axelor.meta.db.MetaTranslation;
import com.axelor.meta.db.repo.MetaTranslationRepository;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.axelor.inject.Beans;
public class ExportDbObjectService {
@Inject
UserService uis;
private static final Logger log = LoggerFactory.getLogger(ExportDbObjectService.class);
public static String[] fieldAttrs = new String[]{"name","type","title"};
public String[] csvHeaders = new String[]{"object","module","name","type","title_en","title_fr","help_en","help_fr","url" };
private List<String[]> fieldDataList = new ArrayList<String[]>();
private List<String> objectList = new ArrayList<String>();
private Map<String,Object> objectMap = new HashMap<String, Object>();
private Group group = null;
@Transactional
public MetaFile exportObject() {
// group = AuthUtils.getUser().getGroup();
group = Beans.get(GroupRepository.class).all().filter("self.code = 'admins'").fetchOne();
try {
log.debug("Attachment dir: {}",AppSettings.get().get("file.upload.dir"));
String uploadDir = AppSettings.get().get("file.upload.dir");
if(uploadDir == null || !new File(uploadDir).exists()) { return null; }
String appSrc = AppSettings.get().get("application.src");
log.debug("Module dir: {}",appSrc);
if(appSrc == null){ return null;}
File moduleDir = new File(appSrc);
if(!moduleDir.exists()){ return null; }
MetaFile metaFile = new MetaFile();
String fileName = "ExportObject-"+DateTime.now().toString("yyyMMddHHmmSS")+".csv";
metaFile.setFileName(fileName);
metaFile.setFilePath(fileName);
metaFile = Beans.get(MetaFileRepository.class).save(metaFile);
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
updateObjectMap(Arrays.asList(moduleDir.listFiles()), saxParserFactory.newSAXParser(), new XmlHandler());
writeObjects(MetaFiles.getPath(metaFile).toFile());
return metaFile;
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
return null;
}
private void writeObjects(File objectFile) {
try {
List<? extends MetaMenu> menuList = Beans.get(MetaMenuRepository.class).all().filter("self.parent = null AND self.left = true AND ?1 MEMBER OF self.groups",group).order("-priority").order("id").fetch();
log.debug("Total root menus: {}",menuList.size());
generateMenuGraph(menuList);
CsvTool.csvWriter(objectFile.getParent(), objectFile.getName(), ';', csvHeaders, fieldDataList);
} catch (IOException e) {
e.printStackTrace();
}
}
private void generateMenuGraph(List<? extends MetaMenu> menuList) {
//log.debug("Checking menu list: {}",menuList);
for(MetaMenu menu : menuList) {
String model = menu.getAction() != null ? menu.getAction().getModel() : null;
//log.debug("Action model: ",model);
if(model != null && !objectList.contains(model)) {
updateFieldData(menu.getAction());
}
//List<? extends MetaMenu> childList = MetaMenu.all().filter("self.parent = ?1 AND self.left = true AND ?2 MEMBER OF self.groups", menu,group).order("-priority").order("id").fetch();
List<? extends MetaMenu> childList = Beans.get(MetaMenuRepository.class).all().filter("self.parent = ?1 AND self.left = true",menu).order("-priority").order("id").fetch();
generateMenuGraph(childList);
}
}
@SuppressWarnings("unchecked")
private void updateFieldData(MetaAction action) {
String[] objectName = action.getModel().split("\\.");
String objName = objectName[objectName.length-1];
Map<String,Object> moduleMap = (Map<String, Object>)objectMap.get(objName);
if(moduleMap == null){return;}
boolean addObject = true;
MetaTranslationRepository metaTranslationRepo = Beans.get(MetaTranslationRepository.class);
//log.debug("Adding object: {}",objName);
for(Entry<String, Object> module : moduleMap.entrySet()){
boolean addModule = true;
for(Map<String,String> field : (List<Map<String,String>>)module.getValue()){
String[] fields = new String[csvHeaders.length];
fields[0] = "";
if(addObject) {
fields[0] = action.getModel();
fields[8] = getActionUrl(action);
}
fields[1] = "";
if(addModule) { fields[1] = module.getKey(); }
fields[2] = field.get("name");
fields[3] = field.get("type");
fields[4] = field.get("title");
MetaTranslation mts = metaTranslationRepo.findByKey(field.get("title"), "fr");
if(mts != null) { fields[5] = mts.getMessage(); }
mts = metaTranslationRepo.findByKey("help:"+objName+"."+field.get("name"), "en");
if(mts != null) { fields[6] = mts.getMessage().replace(";", "\n"); }
mts = metaTranslationRepo.findByKey("help:"+objName+"."+field.get("name"), "fr");
if(mts != null) { fields[7] = mts.getMessage().replace(";", "\n"); }
fieldDataList.add(fields);
addObject = false;
addModule = false;
}
}
objectList.add(action.getModel());
}
private String getActionUrl(MetaAction action) {
String url = AppSettings.get().getBaseURL()+"#/ds";
String viewType = getActionViewType(action.getXml());
if(viewType.equals("grid")){
url = url+"/"+action.getName()+"/list/1";
}
else if(viewType.equals("form")) {
url = url+"/"+action.getName()+"/edit";
}
else if(viewType.equals("calendar")) {
url = url+"/"+action.getName()+"/calendar";
}
return url;
}
public static String getActionViewType(String xml) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true); // never forget this!
DocumentBuilder builder;
try {
builder = domFactory.newDocumentBuilder();
File tempXml = File.createTempFile("Temp", "xml");
FileWriter fw = new FileWriter(tempXml);
fw.write(xml);
fw.close();
Document doc = builder.parse(new FileInputStream(tempXml));
Node child = doc.getFirstChild();
NodeList chs = child.getChildNodes();
for(Integer i=0; i < chs.getLength(); i++){
if(chs.item(i).getNodeName().equals("view")) {
NamedNodeMap attributes = chs.item(i).getAttributes();
return attributes.getNamedItem("type").getNodeValue();
}
}
return "";
} catch (Exception e) {
log.error(e.getMessage());
}
return null;
}
@SuppressWarnings("unchecked")
private void updateObjectMap(List<File> modules, SAXParser parser, XmlHandler xmlHandler) throws SAXException, IOException {
for(File module : modules){
String modulePath = module.getAbsolutePath();
File modelDir = new File(modulePath+"/src/main/resources/domains/");
if(!modelDir.exists()){ continue;}
//log.debug("Module : {}",modelDir.getAbsolutePath());
for(File objectFile : modelDir.listFiles()){
//log.debug("Parsing domain : {}",objectFile.getName());
String objectName = objectFile.getName().split("\\.")[0];
parser.parse(new InputSource(new FileInputStream(objectFile)), xmlHandler);
Map<String,Object> moduleMap = (Map<String, Object>) objectMap.get(objectName);
if(moduleMap == null) { moduleMap = new HashMap<String,Object>();}
moduleMap.put(module.getName(),updateObjectModel(xmlHandler.fieldList, objectName, module.getName()));
objectMap.put(objectName,moduleMap);
}
}
}
private Object updateObjectModel(List<Map<String, String>> fieldList, String objectName, String moduleName) {
for(Map<String,String> field : fieldList) {
field.put("module", moduleName);
field.put("object", objectName);
}
return fieldList;
}
}
class XmlHandler extends DefaultHandler {
public List<Map<String,String>> fieldList;
private boolean isObject = false;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
switch(qName){
case "entity": {
isObject = true;
fieldList = new ArrayList<Map<String,String>>();
break;
}
default: {
if(isObject){
Map<String,String> fieldMap = new HashMap<String,String>();
fieldMap.put("type", qName);
for(String fieldAttr : Arrays.asList(ExportDbObjectService.fieldAttrs)){
if(attributes.getValue(fieldAttr) != null){
fieldMap.put(fieldAttr, attributes.getValue(fieldAttr));
}
}
fieldList.add(fieldMap);
}
}
}
super.startElement(uri, localName, qName, attributes);
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if(qName.equals("entity")){
isObject = false;
}
super.endElement(uri, localName, qName);
}
}