/* * File : ExportTaskDlg.java * Created : 24-nov-2015 16:37 * By : fbusquets * * JClic - Authoring and playing system for educational activities * * Copyright (C) 2000 - 2005 Francesc Busquets & Departament * d'Educacio de la Generalitat de Catalunya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details (see the LICENSE file). */ package edu.xtec.jclic.project; import edu.xtec.jclic.AuthorSettings; import edu.xtec.util.BrowserLauncher; import edu.xtec.util.Messages; import edu.xtec.util.Options; import edu.xtec.util.ResourceBridge; import edu.xtec.util.StrUtils; import edu.xtec.util.StreamIO; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.ListIterator; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.swing.JDialog; import javax.swing.JOptionPane; import org.json.JSONObject; /** * * @author Francesc Busquets (fbusquets@xtec.cat) * @version 24.11.15 */ public class ExportTaskDlg extends javax.swing.JPanel { ResourceBridge rb; Options options; edu.xtec.util.SwingWorker sw; File exportPath; static String indexHtml = "<!DOCTYPE html>\n" + "<html>\n" + " <head>\n" + " <meta charset=\"UTF-8\">\n" + " <title>%TITLE%</title>\n" + " <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n" + " <meta name=\"mobile-web-app-capable\" content=\"yes\">\n" + " <meta name=\"application-name\" content=\"%TITLE%\">\n" + " <link rel=\"shortcut icon\" href=\"favicon.ico\">\n" + " <link rel=\"icon\" sizes=\"16x16\" href=\"favicon.ico\">\n" + " <link rel=\"icon\" sizes=\"72x72\" href=\"icon-72.png\">\n" + " <link rel=\"icon\" sizes=\"192x192\" href=\"icon-192.png\">\n" + " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" + " <script type=\"text/javascript\" src=\"%JSCODEBASE%\"></script>\n" + " </head>\n" + " <body style=\"margin:0\">\n" + " <div class =\"JClic\" data-project=\"%MAINFILE%\"></div>\n" + " </body>\n" + "</html>"; static String imsmanifest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!-- Generated by JClic - https://projectestac.github.io/jclic -->\n" + "<manifest identifier=\"%SCORMID%\" version=\"1.0\" xmlns=\"http://www.imsproject.org/xsd/imscp_rootv1p1p2\" " + "xmlns:adlcp=\"http://www.adlnet.org/xsd/adlcp_rootv1p2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " + "xsi:schemaLocation= \"http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd\">\n" + " <metadata> \n" + " <schema>ADL SCORM</schema>\n" + " <schemaversion>1.2</schemaversion>\n" + " </metadata> \n" + " <organizations default=\"JClic\"> \n" + " <organization identifier=\"JClic\"> \n" + " <title>%SCORMTITLE%</title>\n" + " <item identifier=\"ITEM_%SCORMID%\" identifierref=\"RES_%SCORMID%\">\n" + " <title>%SCORMTITLE%</title>\n" + " </item>\n" + " </organization>\n" + " </organizations>\n" + " <resources>\n" + " <resource identifier=\"RES_%SCORMID%\" type=\"webcontent\" href=\"/index.html\" adlcp:scormtype=\"sco\"> \n" + "%FILETAGS% </resource>\n" + " </resources>\n" + "</manifest>"; /** * Creates new ExportTaskDlg * * @param rb */ public ExportTaskDlg(ResourceBridge rb) { this.rb = rb; options = rb.getOptions(); initComponents(); } PrintStream ps = new PrintStream(new ByteArrayOutputStream()) { @Override public synchronized void println(String s) { logArea.append(s + "\n"); logArea.setCaretPosition(logArea.getText().length()); } @Override public synchronized void print(String s) { logArea.append(s); logArea.setCaretPosition(logArea.getText().length()); } }; /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jScrollPane1 = new javax.swing.JScrollPane(); logArea = new javax.swing.JTextArea(); javax.swing.JPanel bottomPanel = new javax.swing.JPanel(); copyBtn = new javax.swing.JButton(); browserBtn = new javax.swing.JButton(); cancelBtn = new javax.swing.JButton(); setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); setLayout(new java.awt.BorderLayout(0, 10)); jScrollPane1.setPreferredSize(new java.awt.Dimension(600, 500)); logArea.setEditable(false); logArea.setLineWrap(true); logArea.setWrapStyleWord(true); jScrollPane1.setViewportView(logArea); add(jScrollPane1, java.awt.BorderLayout.CENTER); bottomPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 10, 5)); copyBtn.setText(options.getMsg("export_project_copyLog")); copyBtn.setEnabled(false); copyBtn.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { copyBtnActionPerformed(evt); } }); bottomPanel.add(copyBtn); browserBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/edu/xtec/resources/icons/html_doc.gif"))); // NOI18N browserBtn.setText(options.getMsg("export_project_launchBrowser")); browserBtn.setEnabled(false); browserBtn.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { browserBtnActionPerformed(evt); } }); bottomPanel.add(browserBtn); cancelBtn.setText(options.getMsg("CANCEL")); cancelBtn.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cancelBtnActionPerformed(evt); } }); bottomPanel.add(cancelBtn); add(bottomPanel, java.awt.BorderLayout.SOUTH); }// </editor-fold>//GEN-END:initComponents private void copyBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyBtnActionPerformed StringSelection stringSelection = new StringSelection(logArea.getText()); Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard(); clpbrd.setContents(stringSelection, null); }//GEN-LAST:event_copyBtnActionPerformed private void cancelBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelBtnActionPerformed if (sw != null) { ProjectFileUtils.interrupt = true; } else { JDialog myDlg = (JDialog) javax.swing.SwingUtilities.getAncestorOfClass(JDialog.class, this); if (myDlg != null) { myDlg.dispose(); } } }//GEN-LAST:event_cancelBtnActionPerformed private void browserBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browserBtnActionPerformed String url = exportPath + "/index.html"; BrowserLauncher.openURL(url); ps.println("File " + url + " opened in web browser."); }//GEN-LAST:event_browserBtnActionPerformed public static void doTask(ResourceBridge rb, AuthorSettings settings, Component parent, final String inputPath, final String outputPath, final String mainFileName, final JClicProject project, final boolean copyAll, final String scormFile) { final Messages msg = rb.getOptions().getMessages(); final ExportTaskDlg exportDlg = new ExportTaskDlg(rb); final AuthorSettings set = settings; JDialog dlg = new JDialog(JOptionPane.getFrameForComponent(parent), true); dlg.setTitle(msg.get("export_project_exporting")); dlg.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); dlg.getContentPane().setLayout(new BorderLayout(10, 10)); dlg.getContentPane().add(exportDlg, BorderLayout.CENTER); dlg.pack(); dlg.setLocationRelativeTo(parent); dlg.setLocation((parent.getWidth() - dlg.getWidth()) / 2, (parent.getHeight() - dlg.getHeight()) / 2); exportDlg.sw = new edu.xtec.util.SwingWorker() { @Override public Object construct() { try { ArrayList<String> fileList = new ArrayList<String>(); fileList.add("imsmanifest.xml"); if (!copyAll) { exportDlg.ps.println("Processing: " + inputPath); ProjectFileUtils.processSingleFile(inputPath, outputPath, fileList, exportDlg.ps); } else { exportDlg.ps.println("Processing all projects in: " + inputPath); ProjectFileUtils.processRootFolder(inputPath, outputPath, fileList, exportDlg.ps); } exportDlg.ps.println("Generating file " + outputPath + "/index.html"); FileOutputStream fos = new FileOutputStream(new File(outputPath, "index.html")); PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos, "UTF-8")); String s = indexHtml.replaceAll("%TITLE%", StrUtils.safeHtml(project.settings.title)); s = StrUtils.replace(s, "%MAINFILE%", mainFileName); s = StrUtils.replace(s, "%JSCODEBASE%", set.jsCodeBase); pw.print(s); pw.flush(); pw.close(); fileList.add("index.html"); JSONObject json = project.settings.toJSON(msg); json.put("mainFile", mainFileName); exportDlg.exportPath = new File(outputPath); String fn = "favicon.ico"; exportDlg.ps.println("Copying " + fn); StreamIO.writeStreamTo(getClass().getResourceAsStream("/edu/xtec/resources/icons/" + fn), new FileOutputStream(new File(outputPath, fn))); fileList.add(fn); fn = "icon-192.png"; exportDlg.ps.println("Copying " + fn); StreamIO.writeStreamTo(getClass().getResourceAsStream("/edu/xtec/resources/icons/" + fn), new FileOutputStream(new File(outputPath, fn))); fileList.add(fn); fn = "icon-72.png"; exportDlg.ps.println("Copying " + fn); StreamIO.writeStreamTo(getClass().getResourceAsStream("/edu/xtec/resources/icons/" + fn), new FileOutputStream(new File(outputPath, fn))); fileList.add(fn); exportDlg.ps.println("Copying project cover model"); StreamIO.writeStreamTo(getClass().getResourceAsStream("/edu/xtec/resources/icons/cover-base.jpg"), new FileOutputStream(new File(outputPath, "project-cover.jpg"))); fileList.add("project-cover.jpg"); json.put("cover", "project-cover.jpg"); exportDlg.ps.println("Copying project thumbnail model"); StreamIO.writeStreamTo(getClass().getResourceAsStream("/edu/xtec/resources/icons/thumb-base.jpg"), new FileOutputStream(new File(outputPath, "project-thumb.jpg"))); fileList.add("project-thumb.jpg"); json.put("thumbnail", "project-thumb.jpg"); exportDlg.ps.println("Generating project.json"); fileList.add("project.json"); // Order fileList and remove duplicate values Collections.sort(fileList); fileList = new ArrayList<String>(new LinkedHashSet<String>(fileList)); json.append("files", fileList); fos = new FileOutputStream(new File(outputPath, "project.json")); pw = new PrintWriter(new OutputStreamWriter(fos, "UTF-8")); pw.print(json.toString(2)); pw.flush(); pw.close(); exportDlg.ps.println("Generating imsmanifest.xml"); fos = new FileOutputStream(new File(outputPath, "imsmanifest.xml")); pw = new PrintWriter(new OutputStreamWriter(fos, "UTF-8")); s = imsmanifest.replaceAll("%SCORMTITLE%", StrUtils.safeHtml(project.settings.title)); s = StrUtils.replace(s, "%SCORMID%", "JClic-" + Integer.toHexString((int) (Math.random() * 0xEFFFFF + 0x100000)).toUpperCase()); StringBuilder sb = new StringBuilder(); ListIterator<String> it = fileList.listIterator(); while (it.hasNext()) { sb.append(" <file href=\"").append(it.next()).append("\"/>\n"); } s = StrUtils.replace(s, "%FILETAGS%", sb.toString()); pw.print(s); pw.flush(); pw.close(); if (scormFile != null) { final byte[] BUFFER = new byte[1024]; exportDlg.ps.println("Generating file \"" + scormFile + "\" with all content inside"); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(scormFile)); it = fileList.listIterator(); while (it.hasNext()) { String file = it.next(); FileInputStream in = new FileInputStream(new File(outputPath, file)); ZipEntry entry = new ZipEntry(file); out.putNextEntry(entry); int i; while ((i = in.read(BUFFER)) != -1) { out.write(BUFFER, 0, i); } out.closeEntry(); } out.close(); } exportDlg.ps.println("\n" + msg.get("export_project_finished") + " " + outputPath); exportDlg.ps.println("\n" + msg.get("export_project_notice")); if (scormFile != null) { exportDlg.ps.printf("\n" + msg.get("export_project_scorm_notice") + "\n\n", scormFile); } exportDlg.browserBtn.setEnabled(true); } catch (InterruptedException iex) { exportDlg.ps.println("\nWARNING: The process was interrupted! Contents of the output folder might be unsuitable."); } catch (Exception ex) { exportDlg.ps.println("\nERROR processing ZIP file: " + ex.getMessage()); } return null; } @Override public void finished() { exportDlg.cancelBtn.setText(msg.get("OK")); exportDlg.copyBtn.setEnabled(true); exportDlg.sw = null; } }; exportDlg.sw.start(); dlg.setVisible(true); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton browserBtn; private javax.swing.JButton cancelBtn; private javax.swing.JButton copyBtn; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTextArea logArea; // End of variables declaration//GEN-END:variables }