PK ?E.V V build.properties# change the play.path to where your play framework is installed
play.path=../../playPK ?H build.xml
PK ?zjo5 5 commands.py# Here you can create play commands that are specific to the module, and extend existing commands
MODULE = 'press'
# Commands that are specific to your module
COMMANDS = ['press:hello']
def execute(**kargs):
command = kargs.get("command")
app = kargs.get("app")
args = kargs.get("args")
env = kargs.get("env")
if command == "press:hello":
print "~ Hello"
# This will be executed before any command (new, run...)
def before(**kargs):
command = kargs.get("command")
app = kargs.get("app")
args = kargs.get("args")
env = kargs.get("env")
# This will be executed after any command (new, run...)
def after(**kargs):
command = kargs.get("command")
app = kargs.get("app")
args = kargs.get("args")
env = kargs.get("env")
if command == "new":
pass
PK @SR~ commands.pyc
QLNc @ s. d Z d g Z d Z d Z d Z d S( t presss press:helloc K sV | i d } | i d } | i d } | i d } | d j o d GHn d S( Nt commandt appt argst envs press:hellos ~ Hello( t get( t kargsR R R R ( ( s( /Users/dirk/dev/ponele/press/commands.pyt execute s
c K s@ | i d } | i d } | i d } | i d } d S( NR R R R ( R ( R R R R R ( ( s( /Users/dirk/dev/ponele/press/commands.pyt before s c K sQ | i d } | i d } | i d } | i d } | d j o n d S( NR R R R t new( R ( R R R R R ( ( s( /Users/dirk/dev/ponele/press/commands.pyt after s
N( t MODULEt COMMANDSR R R
( ( ( s( /Users/dirk/dev/ponele/press/commands.pyt s PK PA." " manifestversion=1.0.35
frameworkVersions=
PK ?%w app/play.plugins100:press.PluginPK kIA3E( app/controllers/press/Press.javapackage controllers.press;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import play.exceptions.UnexpectedException;
import play.mvc.Controller;
import press.CachingStrategy;
import press.PluginConfig;
import press.ScriptCompressedFileManager;
import press.ScriptCompressor;
import press.ScriptRequestHandler;
import press.StyleCompressedFileManager;
import press.StyleCompressor;
import press.StyleRequestHandler;
import press.io.CompressedFile;
import press.io.FileIO;
public class Press extends Controller {
public static final DateTimeFormatter httpDateTimeFormatter = DateTimeFormat
.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'");
public static void getCompressedJS(String key) {
key = FileIO.unescape(key);
CompressedFile compressedFile = new ScriptCompressedFileManager().getCompressedFile(key);
renderCompressedFile(compressedFile, "JavaScript");
}
public static void getCompressedCSS(String key) {
key = FileIO.unescape(key);
CompressedFile compressedFile = new StyleCompressedFileManager().getCompressedFile(key);
renderCompressedFile(compressedFile, "CSS");
}
private static void renderCompressedFile(CompressedFile compressedFile, String type) {
flash.keep();
if (compressedFile == null) {
renderBadResponse(type);
}
InputStream inputStream = compressedFile.inputStream();
// This seems to be buggy, so instead of passing the file length we
// reset the input stream and allow play to manually copy the bytes from
// the input stream to the response
// renderBinary(inputStream, compressedFile.name(),
// compressedFile.length());
try {
if (inputStream.markSupported()) {
inputStream.reset();
}
} catch (IOException e) {
throw new UnexpectedException(e);
}
// If the caching strategy is always, the timestamp is not part of the
// key. If we let the browser cache, then the browser will keep holding
// old copies, even after changing the files at the server and
// restarting the server, since the key will stay the same.
// If the caching strategy is never, we also don't want to cache at the
// browser, for obvious reasons.
// If the caching strategy is Change, then the modified timestamp is a
// part of the key, so if the file changes, the key in the html file
// will be modified, and the browser will request a new version. Each
// version can therefore be cached indefinitely.
if (PluginConfig.cache.equals(CachingStrategy.Change)) {
// Cache for a year
response.setHeader("Cache-Control", "max-age=" + 31536000);
response.setHeader("Expires", httpDateTimeFormatter.print(new DateTime().plusYears(1)));
if(!PluginConfig.p3pHeader.isEmpty()) {
response.setHeader("P3P", PluginConfig.p3pHeader);
}
}
renderBinary(inputStream, compressedFile.name());
}
public static void clearJSCache() {
if (!PluginConfig.cacheClearEnabled) {
forbidden();
}
int count = ScriptRequestHandler.clearCache();
renderText("Cleared " + count + " JS files from cache");
}
public static void clearCSSCache() {
if (!PluginConfig.cacheClearEnabled) {
forbidden();
}
int count = StyleRequestHandler.clearCache();
renderText("Cleared " + count + " CSS files from cache");
}
private static void renderBadResponse(String fileType) {
String response = "/*\n";
response += "The compressed " + fileType + " file could not be generated.\n";
response += "This can occur in two situations:\n";
response += "1. The time between when the page was rendered by the ";
response += "server and when the browser requested the compressed ";
response += "file was greater than the timeout. (The timeout is ";
response += "currently configured to be ";
response += PluginConfig.compressionKeyStorageTime + ")\n";
response += "2. There was an exception thrown while rendering the ";
response += "page.\n";
response += "*/";
renderBinaryResponse(response);
}
private static void renderBinaryResponse(String response) {
try {
renderBinary(new ByteArrayInputStream(response.getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
throw new UnexpectedException(e);
}
}
}
PK @,ؗ> app/press/CacheManager.javapackage press;
import java.io.File;
import java.util.List;
import java.util.Map;
import play.libs.Crypto;
import play.templates.JavaExtensions;
import press.io.CompressedFile;
import press.io.FileIO;
public class CacheManager {
public static boolean useCachedFile(CompressedFile file) {
String filePath = file.getFileKey();
if (file.exists() && useCache()) {
PressLogger.trace("Using existing compressed file %s", filePath);
return true;
}
if (!file.exists()) {
PressLogger.trace("Compressed file %s does not yet exist", filePath);
}
PressLogger.trace("Generating compressed file %s", filePath);
return false;
}
private static boolean useCache() {
PressLogger.trace("Caching strategy is %s", PluginConfig.cache);
if (PluginConfig.cache.equals(CachingStrategy.Never)) {
return false;
}
// If the caching strategy is Change, we can still use the cache,
// because we included the modification timestamp in the key name, so
// same key means that it is not modified.
return true;
}
/**
* The key is a hash of each component file's path and last modified timestamp
*/
public static String getCompressedFileKey(Map files, String extension) {
// [myfile.css, another.less] ->
// /path/to/compressed/dir/hashoffilenames.css
StringBuffer key = new StringBuffer();
for (String filePath : files.keySet()) {
key.append(filePath);
key.append(files.get(filePath));
}
return FileIO.lettersOnly(Crypto.passwordHash(key.toString())) + extension;
}
}
PK ?g app/press/CachingStrategy.javapackage press;
import java.util.Arrays;
import java.util.List;
import play.templates.JavaExtensions;
public enum CachingStrategy {
Always, Never, Change;
public static CachingStrategy parse(String name) {
String lcName = name.trim().toLowerCase();
for (CachingStrategy strategy : CachingStrategy.values()) {
if (strategy.toString().toLowerCase().equals(lcName)) {
return strategy;
}
}
String msg = "Could not parse caching strategy name from '" + name + "'. ";
List strategies = Arrays.asList(CachingStrategy.values());
msg += "Caching strategy must be one of " + JavaExtensions.join(strategies, ", ");
throw new RuntimeException(msg);
}
}
PK MAAoRv v $ app/press/CompressedFileManager.javapackage press;
import java.util.ArrayList;
import java.util.List;
import play.vfs.VirtualFile;
import press.io.CompressedFile;
public abstract class CompressedFileManager {
private PressFileWriter pressFileWriter;
private Compressor compressor;
public CompressedFileManager(Compressor compressor) {
this.compressor = compressor;
this.pressFileWriter = new PressFileWriter(compressor);
}
/**
* Get the compressed file with the given compression key
*/
public CompressedFile getCompressedFile(String key) {
List componentFiles = SourceFileManager.getSourceFiles(key);
// If there was nothing found for the given request key, return null.
// This shouldn't happen unless there was a very long delay between the
// template being rendered and the compressed file being requested
if (componentFiles == null) {
return null;
}
return getCompressedFile(componentFiles);
}
/**
* Get the compressed file for the given set of component files
*/
public CompressedFile getCompressedFile(List componentFiles) {
// First check if the compressor has a cached copy of the file
String key = compressor.getCompressedFileKey(componentFiles);
CompressedFile file = CompressedFile.create(key, getCompressedDir());
if (CacheManager.useCachedFile(file)) {
return file;
}
// If there is no cached file, generate one
return pressFileWriter.writeCompressedFile(componentFiles, file);
}
public abstract String getCompressedDir();
}
PK <{@= app/press/Compressor.javapackage press;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import play.PlayPlugin;
import play.libs.Crypto;
import play.templates.JavaExtensions;
import press.io.CompressedFile;
import press.io.FileIO;
public abstract class Compressor {
/**
* A key unique for the list of component files and their last modified date
*/
abstract public String getCompressedFileKey(List componentFiles);
abstract public void compress(File file, Writer out, boolean compress) throws IOException;
protected static int clearCache(String compressedDir, String extension) {
return CompressedFile.clearCache(compressedDir, extension);
}
}
PK ?GJ app/press/ConfigHelper.javapackage press;
import play.Play;
import play.exceptions.ConfigurationException;
public class ConfigHelper {
public static String getString(String configKey) {
return Play.configuration.getProperty(configKey, null);
}
public static String getString(String configKey, String defaultValue) {
String value = Play.configuration.getProperty(configKey);
if (value == null || value.length() == 0) {
return defaultValue;
}
return value;
}
public static Boolean getBoolean(String configKey) {
return getBoolean(configKey, null);
}
public static Boolean getBoolean(String configKey, Boolean defaultValue) {
String asStr = Play.configuration.getProperty(configKey);
if (asStr == null || asStr.length() == 0) {
return defaultValue;
}
if (asStr.equals("true") || asStr.equals("false")) {
return Boolean.parseBoolean(asStr);
}
throw new ConfigurationException(configKey + " must be either true or false");
}
public static Integer getInt(String configKey) {
return getInt(configKey, null);
}
public static Integer getInt(String configKey, Integer defaultValue) {
String asStr = Play.configuration.getProperty(configKey);
if (asStr == null || asStr.length() == 0) {
return defaultValue;
}
return Integer.parseInt(asStr);
}
}
PK ?w_y y % app/press/DuplicateFileException.javapackage press;
@SuppressWarnings("serial")
public class DuplicateFileException extends PressException {
public DuplicateFileException(String fileType, String fileName, String tagName) {
super(buildMessage(fileType, fileName, tagName));
}
private static String buildMessage(String fileType, String fileName, String tagName) {
String msg = "Attempt to add the same " + fileType + " file ";
msg += "to compression twice: '" + fileName + "'\n";
msg += "Check that you're not including the same file in two different ";
msg += tagName + " tags.";
return msg;
}
}
PK c|@T·T app/press/FileInfo.javapackage press;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import play.vfs.VirtualFile;
public class FileInfo implements Serializable {
boolean compress;
public File file;
public FileInfo(boolean compress, VirtualFile file) {
this.compress = compress;
// We store the File instead of a VirtualFile so that this class can be
// serialized
this.file = file == null ? null : file.getRealFile();
}
public long getLastModified() {
return file.lastModified();
}
public static List getFiles(List fileInfos) {
List files = new ArrayList();
for (FileInfo info : fileInfos) {
files.add(info.file);
}
return files;
}
public static Map getFileLastModifieds(List fileInfos) {
Map files = new HashMap();
for (FileInfo info : fileInfos) {
files.put(info.file.getAbsolutePath(), info.file.lastModified());
}
return files;
}
}
PK u@ê app/press/PlayLessEngine.javapackage press;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.mozilla.javascript.WrappedException;
import play.Logger;
import play.cache.Cache;
import com.asual.lesscss.LessEngine;
import com.asual.lesscss.LessException;
/**
* Copied and modified from
* https://github.com/lunatech-labs/play-module-less/blob
* /master/src/play/modules/less/PlayLessEngine.java LessEngine wrapper for Play
*/
public class PlayLessEngine {
LessEngine lessEngine;
static Pattern importPattern = Pattern.compile(".*@import\\s*\"(.*?)\".*");
PlayLessEngine() {
lessEngine = new LessEngine();
}
/**
* Get the CSS for this less file either from the cache, or compile it.
*/
public String get(File lessFile, boolean compress) {
String cacheKey = "less_" + lessFile.getPath() + latestModified(lessFile);
String css = cacheGet(cacheKey, String.class);
if (css == null) {
css = compile(lessFile, compress);
cacheSet(cacheKey, css);
}
return css;
}
/**
* Returns the latest of the last modified dates of this file and all files
* it imports
*/
public static long latestModified(File lessFile) {
long lastModified = lessFile.lastModified();
for (File imported : getAllImports(lessFile)) {
lastModified = Math.max(lastModified, imported.lastModified());
}
return lastModified;
}
/**
* Returns a set composed of the file itself, followed by all files that it
* imports, the files they import, etc
*/
public static Set getAllImports(File lessFile) {
Set imports = new HashSet();
getAllImports(lessFile, imports);
return imports;
}
protected static void getAllImports(File lessFile, Set imports) {
imports.add(lessFile);
for (File imported : getImportsFromCacheOrFile(lessFile)) {
if (!imports.contains(imported)) {
getAllImports(imported, imports);
}
}
}
protected static Set getImportsFromCacheOrFile(File lessFile) {
String cacheKey = "less_imports_" + lessFile.getPath() + lessFile.lastModified();
Set files = null;
cacheGet(cacheKey, Set.class);
if (files == null) {
try {
files = getImportsFromFile(lessFile);
cacheSet(cacheKey, files);
} catch (IOException e) {
Logger.error(e, "IOException trying to determine imports in LESS file");
files = new HashSet();
}
}
return files;
}
protected static Set getImportsFromFile(File lessFile) throws IOException {
if (!lessFile.exists()) {
return Collections.emptySet();
}
BufferedReader r = new BufferedReader(new FileReader(lessFile));
try {
Set files = new HashSet();
String line;
while ((line = r.readLine()) != null) {
Matcher m = importPattern.matcher(line);
while (m.find()) {
File file = new File(lessFile.getParentFile(), m.group(1));
if (!file.exists())
file = new File(lessFile.getParentFile(), m.group(1) + ".less");
files.add(file);
files.addAll(getImportsFromCacheOrFile(file));
}
}
return files;
} finally {
IOUtils.closeQuietly(r);
}
}
protected String compile(File lessFile, boolean compress) {
try {
String css = lessEngine.compile(lessFile, compress);
// There seems to be a bug whereby \n's are sometimes escaped
return css.replace("\\n", "\n");
} catch (LessException e) {
return handleException(lessFile, e);
}
}
public String handleException(File lessFile, LessException e) {
Logger.warn(e, "Less exception");
String filename = e.getFilename();
List extractList = e.getExtract();
String extract = null;
if (extractList != null) {
extract = extractList.toString();
}
// LessEngine reports the file as null when it's not an @imported file
if (filename == null) {
filename = lessFile.getName();
}
// Try to detect missing imports (flaky)
if (extract == null && e.getCause() instanceof WrappedException) {
WrappedException we = (WrappedException) e.getCause();
if (we.getCause() instanceof FileNotFoundException) {
FileNotFoundException fnfe = (FileNotFoundException) we.getCause();
extract = fnfe.getMessage();
}
}
return formatMessage(filename, e.getLine(), e.getColumn(), extract, e.getType());
}
public String formatMessage(String filename, int line, int column, String extract,
String errorType) {
return "body:before {display: block; color: #c00; white-space: pre; font-family: monospace; background: #FDD9E1; border-top: 1px solid pink; border-bottom: 1px solid pink; padding: 10px; content: \"[LESS ERROR] "
+ String.format("%s:%s: %s (%s)", filename, line, extract, errorType) + "\"; }";
}
private static T cacheGet(String key, Class clazz) {
try {
return Cache.get(key, clazz);
} catch (NullPointerException e) {
Logger.info("LESS module: Cache not initialized yet. Request to regular action required to initialize cache in DEV mode.");
return null;
}
}
private static void cacheSet(String key, Object value) {
try {
Cache.set(key, value);
} catch (NullPointerException e) {
Logger.info("LESS module: Cache not initialized yet. Request to regular action required to initialize cache in DEV mode.");
}
}
}PK vCAd app/press/Plugin.javapackage press;
import java.lang.reflect.Method;
import play.PlayPlugin;
public class Plugin extends PlayPlugin {
static ThreadLocal rqManager = new ThreadLocal();
@Override
public void onApplicationStart() {
// Read the config each time the application is restarted
PluginConfig.readConfig();
// Clear the asset cache
RequestManager.clearCache();
}
@Override
public void beforeActionInvocation(Method actionMethod) {
// Before each action, reinitialize variables
rqManager.set(new RequestManager());
}
/**
* Add a single JS file to compression
*/
public static String addSingleJS(String fileName) {
return rqManager.get().addSingleFile(RequestManager.RQ_TYPE_SCRIPT, fileName);
}
/**
* Add a single CSS file to compression
*/
public static String addSingleCSS(String fileName) {
return rqManager.get().addSingleFile(RequestManager.RQ_TYPE_STYLE, fileName);
}
/**
* Adds the given source file(s) to the JS compressor, returning the file
* signature to be output in HTML
*/
public static String addJS(String src, boolean packFile) {
return rqManager.get().addMultiFile(RequestManager.RQ_TYPE_SCRIPT, src, packFile);
}
/**
* Adds the given source file(s) to the CSS compressor, returning the file
* signature to be output in HTML
*/
public static String addCSS(String src, boolean packFile) {
return rqManager.get().addMultiFile(RequestManager.RQ_TYPE_STYLE, src, packFile);
}
/**
* Outputs the tag indicating where the compressed JS should be included.
*/
public static String compressedJSTag() {
return rqManager.get().compressedTag(RequestManager.RQ_TYPE_SCRIPT);
}
/**
* Outputs the tag indicating where the compressed CSS should be included.
*/
public static String compressedCSSTag() {
return rqManager.get().compressedTag(RequestManager.RQ_TYPE_STYLE);
}
@Override
public void afterActionInvocation() {
// When the action finishes, save the list of files added for
// compression
if (rqManager.get() != null) {
rqManager.get().saveFileList();
}
rqManager.set(null);
}
@Override
public void onInvocationException(Throwable e) {
if (rqManager.get() != null) {
rqManager.get().errorOccurred();
}
}
}
PK ]IAP" " app/press/PluginConfig.javapackage press;
import play.Play;
import play.Play.Mode;
public class PluginConfig {
/**
* Stores the default configuration for the plugin
*/
public static class DefaultConfig {
// Whether the plugin is enabled
public static final boolean enabled = (Play.mode == Mode.PROD);
// The caching strategy
public static final CachingStrategy cache = CachingStrategy.Change;
// Whether the cache can be cleared through the web interface
// Default is to be available in dev only
public static final boolean cacheClearEnabled = (Play.mode == Mode.DEV);
// Whether to use the file system or memory to store compressed files
public static final boolean inMemoryStorage = false;
// The amount of time that a compression key is stored for.
// This only needs to be as long as the time between when the action
// finishes and the browser requests the compressed javascript (usually
// less than a second)
public static final String compressionKeyStorageTime = "2mn";
// The maximum amount of time in milli-seconds allowed for compression
// to occur before a timeout exception is thrown.
public static final int maxCompressionTimeMillis = 60000;
// Indicates whether the code output by press is compatible with the
// HTML standard. For example HTML requires that a closing LINK tag MUST
// NOT be output, while XHTML requires that it MUST be output
public static final boolean htmlCompatible = false;
// The domain which is storing the content. Can be set for use with a CDN.
// Will be used to turn a relative URI into an absolute URL.
public static final String contentHostingDomain = "";
// The P3P header to be output. If empty, the header is not output.
public static final String p3pHeader = "";
public static class js {
// The directory where source javascript files are read from
public static final String srcDir = "/public/javascripts/";
// The directory where compressed javascript files are written to
public static final String compressedDir = "/public/javascripts/press/";
// Options for YUI JS compression
public static final int lineBreak = -1;
public static final boolean munge = true;
public static final boolean warn = false;
public static final boolean preserveAllSemiColons = false;
public static final boolean preserveStringLiterals = false;
}
public static class css {
// The directory where source css files are read from
public static final String srcDir = "/public/stylesheets/";
// The directory where compressed css files are written to
public static final String compressedDir = "/public/stylesheets/press/";
// Options for YUI CSS compression
public static final int lineBreak = -1;
}
}
public static boolean enabled;
public static CachingStrategy cache;
public static boolean cacheClearEnabled;
public static boolean inMemoryStorage;
public static String compressionKeyStorageTime;
public static int maxCompressionTimeMillis;
public static boolean htmlCompatible;
public static String contentHostingDomain;
public static String p3pHeader;
public static class js {
public static String srcDir = DefaultConfig.js.srcDir;
public static String compressedDir = DefaultConfig.js.compressedDir;
// YUI JS compression options
public static int lineBreak = DefaultConfig.js.lineBreak;
public static boolean munge = DefaultConfig.js.munge;
public static boolean warn = DefaultConfig.js.warn;
public static boolean preserveAllSemiColons = DefaultConfig.js.preserveAllSemiColons;
public static boolean preserveStringLiterals = DefaultConfig.js.preserveStringLiterals;
}
public static class css {
public static String srcDir = DefaultConfig.css.srcDir;
public static String compressedDir = DefaultConfig.css.compressedDir;
public static int lineBreak = DefaultConfig.css.lineBreak;
}
// Required to make the class loader happy
public static boolean isInMemoryStorage() {
return inMemoryStorage;
}
/**
* Reads from the config file into memory. If the config file doesn't exist
* or is deleted, uses the default values.
*/
public static void readConfig() {
PressLogger.trace("Loading Press plugin configuration");
// press options
enabled = ConfigHelper.getBoolean("press.enabled", DefaultConfig.enabled);
String cacheDefault = DefaultConfig.cache.toString();
cache = CachingStrategy.parse(ConfigHelper.getString("press.cache", cacheDefault));
cacheClearEnabled = ConfigHelper.getBoolean("press.cache.clearEnabled",
DefaultConfig.cacheClearEnabled);
inMemoryStorage = ConfigHelper.getBoolean("press.inMemoryStorage", DefaultConfig.inMemoryStorage);
compressionKeyStorageTime = ConfigHelper.getString("press.key.lifetime",
DefaultConfig.compressionKeyStorageTime);
maxCompressionTimeMillis = ConfigHelper.getInt("press.compression.maxTimeMillis",
DefaultConfig.maxCompressionTimeMillis);
htmlCompatible = ConfigHelper.getBoolean("press.htmlCompatible",
DefaultConfig.htmlCompatible);
contentHostingDomain = ConfigHelper.getString("press.contentHostingDomain",
DefaultConfig.contentHostingDomain);
p3pHeader = ConfigHelper.getString("press.p3pHeader", DefaultConfig.p3pHeader);
css.srcDir = ConfigHelper.getString("press.css.sourceDir", DefaultConfig.css.srcDir);
css.compressedDir = ConfigHelper.getString("press.css.outputDir",
DefaultConfig.css.compressedDir);
js.srcDir = ConfigHelper.getString("press.js.sourceDir", DefaultConfig.js.srcDir);
js.compressedDir = ConfigHelper.getString("press.js.outputDir",
DefaultConfig.js.compressedDir);
// YUI options
css.lineBreak = ConfigHelper.getInt("press.yui.css.lineBreak", DefaultConfig.css.lineBreak);
js.lineBreak = ConfigHelper.getInt("press.yui.js.lineBreak", DefaultConfig.js.lineBreak);
js.munge = ConfigHelper.getBoolean("press.yui.js.munge", DefaultConfig.js.munge);
js.warn = ConfigHelper.getBoolean("press.yui.js.warn", DefaultConfig.js.warn);
js.preserveAllSemiColons = ConfigHelper.getBoolean("press.yui.js.preserveAllSemiColons",
DefaultConfig.js.preserveAllSemiColons);
js.preserveStringLiterals = ConfigHelper.getBoolean("press.yui.js.preserveStringLiterals",
DefaultConfig.js.preserveStringLiterals);
// Add a trailing slash to directories, if necessary
css.srcDir = addTrailingSlash(css.srcDir);
css.compressedDir = addTrailingSlash(css.compressedDir);
js.srcDir = addTrailingSlash(js.srcDir);
js.compressedDir = addTrailingSlash(js.compressedDir);
// Log the newly loaded configuration
logConfig();
}
private static void logConfig() {
PressLogger.trace("enabled: %b", enabled);
PressLogger.trace("caching strategy: %s", cache);
PressLogger.trace("cache publicly clearable: %s", cacheClearEnabled);
PressLogger.trace("in memory storage: %s", inMemoryStorage);
PressLogger.trace("compression key storage time: %s", compressionKeyStorageTime);
PressLogger.trace("HTML compatible: %b", htmlCompatible);
PressLogger.trace("Content hosting domain: %s", contentHostingDomain);
PressLogger.trace("P3P header: %s", p3pHeader);
PressLogger.trace("css source directory: %s", css.srcDir);
PressLogger.trace("css compressed output directory: %s", css.compressedDir);
PressLogger.trace("js source directory: %s", js.srcDir);
PressLogger.trace("js compressed output directory: %s", js.compressedDir);
PressLogger.trace("YUI css line break: %d", css.lineBreak);
PressLogger.trace("YUI js line break: %d", js.lineBreak);
PressLogger.trace("YUI js munge: %s", js.munge);
PressLogger.trace("YUI js warn: %s", js.warn);
PressLogger.trace("YUI js preserve all semi colons: %s", js.preserveAllSemiColons);
PressLogger.trace("YUI js preserve string literals: %s", js.preserveStringLiterals);
}
public static String addTrailingSlash(String dir) {
if (dir.charAt(dir.length() - 1) != '/') {
return dir + '/';
}
return dir;
}
}
PK ?0 app/press/PressException.javapackage press;
@SuppressWarnings("serial")
public class PressException extends RuntimeException {
public PressException(Exception e) {
super(e);
}
public PressException(String msg) {
super(msg);
}
}
PK OAn app/press/PressFileWriter.javapackage press;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import play.exceptions.UnexpectedException;
import play.vfs.VirtualFile;
import press.io.CompressedFile;
import press.io.FileIO;
public class PressFileWriter {
static final String PRESS_SIGNATURE = "press-1.0";
static final String PATTERN_TEXT = "^/\\*" + PRESS_SIGNATURE + "\\*/$";
static final Pattern HEADER_PATTERN = Pattern.compile(PATTERN_TEXT);
private Compressor compressor;
public PressFileWriter(Compressor compressor) {
this.compressor = compressor;
}
/**
* Create a compressed archive from the given component files and write it
* to the given file.
*/
public CompressedFile writeCompressedFile(List componentFiles, CompressedFile file) {
long timeStart = System.currentTimeMillis();
// If the file is being written by another thread, startWrite() will
// block until it is complete and then return null
Writer writer = file.startWrite();
if (writer == null) {
PressLogger.trace("Compressed file was generated by another thread");
return file;
}
try {
writer.append(createFileHeader());
for (FileInfo componentFile : componentFiles) {
compress(componentFile, writer);
}
long timeAfter = System.currentTimeMillis();
PressLogger.trace("Time to compress files for '%s': %d milli-seconds",
FileIO.getFileNameFromPath(file.name()), (timeAfter - timeStart));
} catch (Exception e) {
throw new UnexpectedException(e);
} finally {
// Note that this flushes and closes the writer as well
file.close();
}
return file;
}
private void compress(FileInfo fileInfo, Writer out) throws Exception {
String fileName = fileInfo.file.getName();
if (fileInfo.compress) {
PressLogger.trace("Compressing %s", fileName);
} else {
PressLogger.trace("Adding already compressed file %s", fileName);
}
compressor.compress(fileInfo.file, out, fileInfo.compress);
}
public static String createFileHeader() {
return "/*" + PRESS_SIGNATURE + "*/\n";
}
public static boolean hasPressHeader(File file) {
try {
if (!file.exists()) {
return false;
}
BufferedReader reader = new BufferedReader(new FileReader(file));
String firstLine = reader.readLine();
Matcher matcher = HEADER_PATTERN.matcher(firstLine);
if (matcher.matches()) {
return true;
}
return false;
} catch (IOException e) {
return false;
}
}
}
PK ?3> app/press/PressLogger.javapackage press;
import play.Logger;
public class PressLogger {
public static void trace(String message, Object... args) {
Logger.trace("Press: " + message, args);
}
}
PK @Fb app/press/RequestHandler.javapackage press;
import java.util.HashMap;
import java.util.Map;
import play.mvc.Router;
import play.vfs.VirtualFile;
import press.io.FileIO;
public abstract class RequestHandler {
Map files = new HashMap();
abstract String getTag(String src);
abstract protected SourceFileManager getSourceManager();
abstract protected CompressedFileManager getCompressedFileManager();
abstract public String getCompressedUrl(String requestKey);
abstract public String getSingleFileCompressionKey(String fileName);
protected String getSingleFileCompressionKey(String fileName, SourceFileManager tmpManager) {
PressLogger.trace("Request to compress single file %s", fileName);
return tmpManager.addSingleFile(fileName, true);
}
public String getSrcDir() {
return getSourceManager().srcDir;
}
public VirtualFile checkFileExists(String fileName) {
return getSourceManager().checkFileExists(fileName);
}
public String add(String fileName, boolean packFile) {
return getSourceManager().add(fileName, packFile);
}
public void saveFileList() {
getSourceManager().saveFileList();
}
public String closeRequest() {
return getSourceManager().closeRequest();
}
protected void checkForDuplicates(String fileName) {
if (!files.containsKey(fileName)) {
files.put(fileName, true);
return;
}
SourceFileManager srcManager = getSourceManager();
throw new DuplicateFileException(srcManager.getFileType(), fileName,
srcManager.getTagName());
}
protected static String getCompressedUrl(String action, String requestKey) {
Map params = new HashMap();
params.put("key", FileIO.escape(requestKey));
return Router.reverse(action, params).url;
}
}
PK ђ@BK app/press/RequestManager.javapackage press;
import press.io.PressFileGlobber;
/**
* Manages the state of a single request to render the page
*/
public class RequestManager {
public static final boolean RQ_TYPE_SCRIPT = true;
public static final boolean RQ_TYPE_STYLE = !RQ_TYPE_SCRIPT;
private boolean errorOccurred = false;
private RequestHandler scriptRequestHandler = new ScriptRequestHandler();
private RequestHandler styleRequestHandler = new StyleRequestHandler();
private RequestHandler getRequestHandler(boolean rqType) {
return rqType == RQ_TYPE_SCRIPT ? scriptRequestHandler : styleRequestHandler;
}
public String addSingleFile(boolean rqType, String fileName) {
RequestHandler handler = getRequestHandler(rqType);
handler.checkFileExists(fileName);
String src = null;
if (performCompression()) {
src = handler.getCompressedUrl(handler.getSingleFileCompressionKey(fileName));
} else {
src = handler.getSrcDir() + fileName;
}
return handler.getTag(src);
}
public String addMultiFile(boolean rqType, String src, boolean packFile) {
RequestHandler handler = getRequestHandler(rqType);
String baseUrl = handler.getSrcDir();
String result = "";
for (String fileName : PressFileGlobber.getResolvedFiles(src, baseUrl)) {
handler.checkFileExists(fileName);
handler.checkForDuplicates(fileName);
if (performCompression()) {
result += handler.add(fileName, packFile) + "\n";
} else {
result += handler.getTag(baseUrl + fileName);
}
}
return result;
}
public String compressedTag(boolean rqType) {
RequestHandler handler = getRequestHandler(rqType);
if (performCompression()) {
String requestKey = handler.closeRequest();
return handler.getTag(handler.getCompressedUrl(requestKey));
}
return "";
}
public void saveFileList() {
if (!performCompression()) {
return;
}
scriptRequestHandler.saveFileList();
styleRequestHandler.saveFileList();
}
public void errorOccurred() {
errorOccurred = true;
}
private boolean performCompression() {
return PluginConfig.enabled && !errorOccurred;
}
public static void clearCache() {
ScriptRequestHandler.clearCache();
StyleRequestHandler.clearCache();
}
}
PK MAّy * app/press/ScriptCompressedFileManager.javapackage press;
public class ScriptCompressedFileManager extends CompressedFileManager {
public ScriptCompressedFileManager() {
super(new ScriptCompressor());
}
public String getCompressedDir() {
return PluginConfig.js.compressedDir;
}
}
PK KAZoH'[ [ app/press/ScriptCompressor.javapackage press;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import play.Logger;
import play.vfs.VirtualFile;
import press.io.CompressedFile;
import press.io.FileIO;
import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
public class ScriptCompressor extends Compressor {
public static final String EXTENSION = ".js";
public static int clearCache() {
return clearCache(PluginConfig.js.compressedDir, EXTENSION);
}
static class PressErrorReporter implements ErrorReporter {
private static final String PREFIX = "[YUI Compressor] ";
private static final String FORMAT_STRING = "%s:%d (char %d) %s";
String fileName;
public PressErrorReporter(String fileName) {
this.fileName = fileName;
}
public void warning(String message, String sourceName, int line, String lineSource,
int lineOffset) {
if (line < 0 || (line == 1 && lineOffset == 0)) {
Logger.warn(PREFIX + message);
} else {
Logger.warn(PREFIX + FORMAT_STRING, fileName, line, lineOffset, message);
}
}
public void error(String message, String sourceName, int line, String lineSource,
int lineOffset) {
if (line < 0 || (line == 1 && lineOffset == 0)) {
Logger.error(PREFIX + message);
} else {
Logger.error(PREFIX + FORMAT_STRING, fileName, line, lineOffset, message);
}
}
public EvaluatorException runtimeError(String message, String sourceName, int line,
String lineSource, int lineOffset) {
error(message, sourceName, line, lineSource, lineOffset);
return new EvaluatorException(message);
}
}
@Override
public void compress(File sourceFile, Writer out, boolean compress) throws IOException {
if (!compress) {
FileIO.write(FileIO.getReader(sourceFile), out);
return;
}
ErrorReporter errorReporter = new PressErrorReporter(sourceFile.getName());
Reader in = FileIO.getReader(sourceFile);
JavaScriptCompressor compressor = new JavaScriptCompressor(in, errorReporter);
compressor.compress(out, PluginConfig.js.lineBreak, PluginConfig.js.munge,
PluginConfig.js.warn, PluginConfig.js.preserveAllSemiColons,
PluginConfig.js.preserveStringLiterals);
}
@Override
public String getCompressedFileKey(List componentFiles) {
Map files = FileInfo.getFileLastModifieds(componentFiles);
return CacheManager.getCompressedFileKey(files, EXTENSION);
}
}
PK ǒ@u u # app/press/ScriptRequestHandler.javapackage press;
public class ScriptRequestHandler extends RequestHandler {
private SourceFileManager srcManager = new ScriptSourceFileManager();
private CompressedFileManager compressManager = new ScriptCompressedFileManager();
@Override
public String getCompressedUrl(String requestKey) {
return getCompressedUrl("press.Press.getCompressedJS", requestKey);
}
@Override
public String getTag(String src) {
return "\n";
}
@Override
protected SourceFileManager getSourceManager() {
return srcManager;
}
@Override
protected CompressedFileManager getCompressedFileManager() {
return compressManager;
}
public static int clearCache() {
return ScriptCompressor.clearCache();
}
@Override
public String getSingleFileCompressionKey(String fileName) {
return super.getSingleFileCompressionKey(fileName, new ScriptSourceFileManager());
}
}
PK j@*- - &