Automatic compilation of LESS CSS files in the theme

git-svn-id: https://svn.libreccm.org/ccm/trunk@4994 8810af33-2d31-482b-a856-94f89814c4df
master
jensp 2017-09-08 17:05:52 +00:00
parent 06487845bb
commit 522ab912e5
2 changed files with 170 additions and 58 deletions

Binary file not shown.

View File

@ -11,15 +11,18 @@
* implied. See the License for the specific language governing * implied. See the License for the specific language governing
* rights and limitations under the License. * rights and limitations under the License.
* *
*/ */
package com.arsdigita.themedirector.util; package com.arsdigita.themedirector.util;
import com.arsdigita.themedirector.Theme; import com.arsdigita.themedirector.Theme;
import com.arsdigita.themedirector.ThemeFile; import com.arsdigita.themedirector.ThemeFile;
import com.arsdigita.themedirector.ThemeFileCollection; import com.arsdigita.themedirector.ThemeFileCollection;
import com.arsdigita.util.Assert; import com.arsdigita.util.Assert;
import com.arsdigita.util.UncheckedWrapperException;
import com.inet.lib.less.Less;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
@ -28,7 +31,16 @@ import java.io.IOException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.InputStreamReader;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/** /**
* This is a utility class that is able to take a theme and, when necessary, * This is a utility class that is able to take a theme and, when necessary,
@ -40,39 +52,44 @@ import java.io.FileNotFoundException;
*/ */
public class ThemeFileUtil { public class ThemeFileUtil {
private static Logger s_log = private static Logger s_log = Logger.getLogger(ThemeFileUtil.class);
Logger.getLogger(ThemeFileUtil.class);
/** /**
* this copies the files from the file system to the database. * this copies the files from the file system to the database.
* *
* @param currentFile The directory to search recursively for files * @param currentFile The directory to search recursively for files
* to put in the database or the single file to add to the db. * to put in the database or the single file to
* add to the db.
* @param currentTheme The current theme that is being operated on * @param currentTheme The current theme that is being operated on
* @param serverSpecificPath The absolute path of the root * @param serverSpecificPath The absolute path of the root file. This
* file. This string is removed from the absolute path of the current * string is removed from the absolute path of
* file to get the filePath property of the ThemeFile * the current file to get the filePath property
* of the ThemeFile
* @param themeFiles A Map of ThemeFiles with the key being the * @param themeFiles A Map of ThemeFiles with the key being the
* filePath. This is used to look up files that have already been * filePath. This is used to look up files that
* pulled from the database so that the code does not have to * have already been pulled from the database so
* check the db once for every file. * that the code does not have to check the db
* @param overwriteNewerFiles If this is true then it insert everything * once for every file.
* in to the database. If this is false, it only writes the file to the * @param overwriteNewerFiles If this is true then it insert everything in
* database if the file on the file system is newer than the one in * to the database. If this is false, it only
* the database. * writes the file to the database if the file on
* @param fileType The type of file this is. * the file system is newer than the one in the
* ThemeFile.LIVE and ThemeFile.DRAFT are the two allowed values. * database.
* @param fileType The type of file this is. ThemeFile.LIVE and
* ThemeFile.DRAFT are the two allowed values.
* *
*/ */
public static void updateDatabaseFiles(File currentFile, Theme currentTheme, public static void updateDatabaseFiles(File currentFile, Theme currentTheme,
String serverSpecificPath, String serverSpecificPath,
boolean overwriteNewerFiles, boolean overwriteNewerFiles,
String fileType) { String fileType) {
Assert.isTrue(ThemeFile.LIVE.equals(fileType) || Assert.isTrue(ThemeFile.LIVE.equals(fileType) || ThemeFile.DRAFT.equals(
ThemeFile.DRAFT.equals(fileType)); fileType));
HashMap themeFiles = new HashMap();
ThemeFileCollection collection = null; prepareThemeDirectory(serverSpecificPath);
Map<String, ThemeFile> themeFiles = new HashMap<>();
ThemeFileCollection collection;
if (ThemeFile.LIVE.equals(fileType)) { if (ThemeFile.LIVE.equals(fileType)) {
collection = currentTheme.getPublishedThemeFiles(); collection = currentTheme.getPublishedThemeFiles();
} else { } else {
@ -85,30 +102,123 @@ public class ThemeFileUtil {
themeFiles.put(file.getFilePath(), file); themeFiles.put(file.getFilePath(), file);
} }
} }
updateDatabaseFiles(currentFile, currentTheme, serverSpecificPath, updateDatabaseFiles(currentFile, currentTheme, serverSpecificPath,
themeFiles, overwriteNewerFiles, fileType); themeFiles, overwriteNewerFiles, fileType);
} }
/**
* Prepares the theme directory. At the moment only compiles LESS CSS files.
* All files which do not end with {@code *.less}. Also files ending with
* {@code *.inc.less}. are ignored. These files are interpreted as include
* files that are included into another LESS file.
*
* @param themePath
*/
private static void prepareThemeDirectory(final String themePath) {
final Path root = Paths.get(themePath);
if (!Files.isDirectory(root)) {
return;
}
try {
Files
.newDirectoryStream(root)
.forEach(path -> prepareThemeFile(path));
} catch (IOException ex) {
throw new UncheckedWrapperException(ex);
}
}
private static void prepareThemeFile(final Path path) {
if (Files.isDirectory(path)) {
try {
Files
.newDirectoryStream(path)
.forEach(current -> prepareThemeFile(current));
} catch (IOException ex) {
throw new UncheckedWrapperException(ex);
}
} else {
final String fileName = path.toString();
if (fileName.toLowerCase().endsWith(".less")
&& !fileName.toLowerCase().endsWith(".inc.less")) {
try {
final String result = Less.compile(path.toFile(), false);
final String outputPath = String.format(
"%s.css",
fileName.substring(0, fileName.length() - ".less"
.length()));
final Path output = Paths.get(outputPath);
Files.write(output, result.getBytes());
} catch (IOException ex) {
throw new UncheckedWrapperException(ex);
}
// s_log.debug(String.format("Compiling %s to CSS...", fileName));
// final ScriptEngine scriptEngine = new ScriptEngineManager()
// .getEngineByName("JavaScript");
// try {
// scriptEngine.eval("var global = this;\n"
// + "var window = this;\n"
// + "var process = {env:{}};\n" + "\n"
// + "var console = {};\n"
// + "console.debug = print;\n"
// + "console.log = print;\n"
// + "console.warn = print;\n"
// + "console.error = print;");
// scriptEngine
// .eval(new InputStreamReader(ThemeFileUtil.class
// .getResourceAsStream(
// "/com/arsdigita/themedirector/util/less.min.js")));
// } catch (ScriptException ex) {
// throw new UncheckedWrapperException(ex);
// }
// final Invocable invocable = (Invocable) scriptEngine;
// final String lesscss;
// try {
// lesscss = new String(Files.readAllBytes(path));
// } catch (IOException ex) {
// throw new UncheckedWrapperException(ex);
// }
//
// try {
// final Object result = invocable.invokeFunction("render",
// lesscss);
// s_log.debug(result);
// } catch (NoSuchMethodException | ScriptException ex) {
// throw new UncheckedWrapperException(ex);
// }
}
}
}
/** /**
* this copies the files to the database * this copies the files to the database
* *
* @param currentFile The directory to search recursively for files * @param currentFile The directory to search recursively for files
* to put in the database or the single file to add to the db. * to put in the database or the single file to
* add to the db.
* @param currentTheme The current theme that is being operated on * @param currentTheme The current theme that is being operated on
* @param serverSpecificPath The absolute path of the root * @param serverSpecificPath The absolute path of the root file. This
* file. This string is removed from the absolute path of the current * string is removed from the absolute path of
* file to get the filePath property of the ThemeFile * the current file to get the filePath property
* of the ThemeFile
* @param themeFiles A Map of ThemeFiles with the key being the * @param themeFiles A Map of ThemeFiles with the key being the
* filePath. This is used to look up files that have already been * filePath. This is used to look up files that
* pulled from the database so that the code does not have to * have already been pulled from the database so
* check the db once for every file. * that the code does not have to check the db
* @param overwriteNewerFiles If this is true then it insert everything * once for every file.
* in to the database. If this is false, it only writes the file to the * @param overwriteNewerFiles If this is true then it insert everything in
* database if the file on the file system is newer than the one in * to the database. If this is false, it only
* the database. * writes the file to the database if the file on
* @param fileType The type of file this is. * the file system is newer than the one in the
* ThemeFile.LIVE and ThemeFile.DRAFT are the two allowed values. * database.
* @param fileType The type of file this is. ThemeFile.LIVE and
* ThemeFile.DRAFT are the two allowed values.
* *
*/ */
private static void updateDatabaseFiles(File currentFile, private static void updateDatabaseFiles(File currentFile,
@ -131,15 +241,16 @@ public class ThemeFileUtil {
String fullFilePath = currentFile.getAbsolutePath(); String fullFilePath = currentFile.getAbsolutePath();
String filePath = null; String filePath = null;
int beginIndex = fullFilePath.indexOf(serverSpecificPath); int beginIndex = fullFilePath.indexOf(serverSpecificPath);
if (beginIndex > -1 && if (beginIndex > -1 && fullFilePath.length()
fullFilePath.length() > serverSpecificPath.length()) { > serverSpecificPath.length()) {
filePath = fullFilePath.substring filePath = fullFilePath.substring(beginIndex
(beginIndex + serverSpecificPath.length() + 1); + serverSpecificPath
.length() + 1);
} else { } else {
filePath = fullFilePath; filePath = fullFilePath;
} }
ThemeFile themeFile = (ThemeFile)themeFiles.get(filePath); ThemeFile themeFile = (ThemeFile) themeFiles.get(filePath);
if (!overwriteNewerFiles && themeFile != null) { if (!overwriteNewerFiles && themeFile != null) {
// make sure the currentFile is newer than the // make sure the currentFile is newer than the
// file in the db. // file in the db.
@ -176,8 +287,8 @@ public class ThemeFileUtil {
} }
themeFile.setVersion(fileType); themeFile.setVersion(fileType);
themeFile.setContent(content); themeFile.setContent(content);
themeFile.setLastModifiedDate themeFile.setLastModifiedDate(new Date(currentFile
(new Date(currentFile.lastModified())); .lastModified()));
// we save to help with stack trace issues and so that // we save to help with stack trace issues and so that
// we don't have too many build up. // we don't have too many build up.
@ -185,14 +296,15 @@ public class ThemeFileUtil {
in.close(); in.close();
os.close(); os.close();
} catch (FileNotFoundException fnfe) { } catch (FileNotFoundException fnfe) {
s_log.error("Error opening file reader for " + s_log.error("Error opening file reader for " + currentFile
currentFile.getAbsolutePath(), fnfe); .getAbsolutePath(), fnfe);
} catch (IOException ex) { } catch (IOException ex) {
s_log.error("Error working with either the input or " + s_log.error("Error working with either the input or "
" output stream for " + + " output stream for " + currentFile
currentFile.getAbsolutePath(), ex); .getAbsolutePath(), ex);
} }
} }
} }
} }
} }