Navigation Menu

Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ldoguin committed Jul 6, 2012
1 parent d0fd4bf commit e800989
Show file tree
Hide file tree
Showing 9 changed files with 540 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .gitignore
@@ -0,0 +1,23 @@
# common ignores
.classpath
.project
.settings
.idea
bin
target
*~
*~bak
*-bak
*.bak
*-new
*.rej
*.orig
.DS_Store
.pydevproject
*.pyc
*.iml
*.ipr
*.iws
*.gpd.*
*cpJar*
dependency-tree.log
73 changes: 73 additions & 0 deletions pom.xml
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.nuxeo.ecm.platform</groupId>
<artifactId>nuxeo-features-parent</artifactId>
<version>5.6-SNAPSHOT</version>
</parent>
<groupId>org.nuxeo.preview.search</groupId>
<artifactId>nuxeo-preview-highlight-search</artifactId>
<name>nuxeo-preview-highlight-search</name>
<description/>
<dependencies>
<dependency>
<groupId>org.nuxeo.common</groupId>
<artifactId>nuxeo-common</artifactId>
</dependency>
<dependency>
<groupId>org.nuxeo.runtime</groupId>
<artifactId>nuxeo-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.nuxeo.ecm.core</groupId>
<artifactId>nuxeo-core</artifactId>
</dependency>
<dependency>
<groupId>org.nuxeo.ecm.core</groupId>
<artifactId>nuxeo-core-api</artifactId>
</dependency>
<dependency>
<groupId>org.nuxeo.ecm.core</groupId>
<artifactId>nuxeo-core-query</artifactId>
</dependency>
<dependency>
<groupId>org.nuxeo.ecm.core</groupId>
<artifactId>nuxeo-core-schema</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi-core</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<dependency>
<groupId>org.nuxeo.ecm.platform</groupId>
<artifactId>nuxeo-platform-preview</artifactId>
</dependency>
<dependency>
<groupId>org.nuxeo.ecm.platform</groupId>
<artifactId>nuxeo-platform-web-common</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<additionalProjectnatures>
<projectnature>org.nuxeo.ide.NuxeoNature</projectnature>
</additionalProjectnatures>
<classpathContainers>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
<classpathContainer>org.nuxeo.ide.SDK_CONTAINER</classpathContainer>
<classpathContainer>org.nuxeo.ide.SDK_TEST_CONTAINER</classpathContainer>
</classpathContainers>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,170 @@
/*
* (C) Copyright 2006-2012 Nuxeo SAS (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library 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
* Lesser General Public License for more details.
*
* Contributors:
* ldoguin
*/
package org.nuxeo.preview.search.highlight;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.impl.blob.ByteArrayBlob;
import org.nuxeo.ecm.platform.preview.adapter.BlobPostProcessor;
import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper;

/**
* @author <a href="mailto:ldoguin@nuxeo.com">Laurent Doguin</a>
*
*/
public class SearchKeywordBlobPostProcessor implements BlobPostProcessor {

private static final Log log = LogFactory.getLog(SearchKeywordBlobPostProcessor.class);

protected static final int BUFFER_SIZE = 4096 * 16;

protected static final String SEARCH_KEYWORDS_CSS = "<link href='"
+ VirtualHostHelper.getContextPathProperty()
+ "/css/SearchKeywords.css' rel=\"stylesheet\" type=\"text/css\" />";

protected static final String SEARCH_KEYWORDS_JS = "<script type=\"text/javascript\" src='"
+ VirtualHostHelper.getContextPathProperty()
+ "/scripts/SearchKeywords.js'></script>";

protected static final String JQUERY_JS = "<script type=\"text/javascript\" src='"
+ "http://code.jquery.com/jquery-1.7.2.min.js'></script>";

protected static final String INIT_SEARCHBOX_JS = "<script type=\"text/javascript\" >initSearchBox('"
+ VirtualHostHelper.getContextPathProperty() + "');</script>";

protected Pattern headPattern = Pattern.compile("(.*)(<head>)(.*)",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

protected Pattern htmlPattern = Pattern.compile("(.*)(<html>)(.*)",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

protected Pattern bodyPattern = Pattern.compile(
"(.*)(<body(?:\"[^\"]*\"['\"]*|'[^']*'['\"]*|[^'\">])+>)(.*)",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

protected Pattern endBodyPattern = Pattern.compile("(.*)(</body>)(.*)",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

protected Pattern charsetPattern = Pattern.compile(
"(.*) charset=(.*?)\"(.*)", Pattern.CASE_INSENSITIVE
| Pattern.DOTALL);

@Override
public Blob process(Blob blob) {
String mimetype = blob.getMimeType();
if (mimetype == null || !mimetype.startsWith("text/")) {
// blob does not carry HTML payload hence there is no need to try to
// inject HTML metadata
return blob;
}
try {
String encoding = null;
if (blob.getEncoding() == null) {
Matcher m = charsetPattern.matcher(blob.getString());
if (m.matches()) {
encoding = m.group(2);
}
} else {
encoding = blob.getEncoding();
}

String blobAsString = getBlobAsString(blob, encoding);
String processedBlob = addSearchKeyWordScript(blobAsString);
processedBlob = addInitSearchBoxJS(processedBlob);

byte[] bytes = encoding == null ? processedBlob.getBytes()
: processedBlob.getBytes(encoding);
blob = new ByteArrayBlob(bytes, blob.getMimeType(), encoding);
} catch (IOException e) {
log.debug("Unable to process Blob", e);
}
return blob;
}

protected String getBlobAsString(Blob blob, String encoding)
throws IOException {
if (encoding == null) {
return blob.getString();
}
Reader reader = new InputStreamReader(blob.getStream(), encoding);
return readString(reader);
}

protected String addSearchKeyWordScript(String blob) {
StringBuilder sb = new StringBuilder();
Matcher m = headPattern.matcher(blob);
if (m.matches()) {
sb.append(m.group(1));
sb.append(m.group(2));
sb.append(SEARCH_KEYWORDS_CSS);
sb.append(JQUERY_JS);
sb.append(SEARCH_KEYWORDS_JS);
sb.append(m.group(3));
} else {
log.debug("Unable to inject Annotation module javascript");
sb.append(blob);
}
return sb.toString();
}

protected String addInitSearchBoxJS(String blob) {
StringBuilder sb = new StringBuilder();
Matcher m = endBodyPattern.matcher(blob);
if (m.matches()) {
sb.append(m.group(1));
sb.append(INIT_SEARCHBOX_JS);
sb.append(m.group(2));
sb.append(m.group(3));
} else {
m = bodyPattern.matcher(blob);
if (m.matches()) {
sb.append(m.group(1));
sb.append(m.group(2));
sb.append(INIT_SEARCHBOX_JS);
sb.append(m.group(3));
} else {
log.debug("Unable to inject Annotation module javascript");
sb.append(blob);
}
}
return sb.toString();
}

public static String readString(Reader reader) throws IOException {
StringBuilder sb = new StringBuilder(BUFFER_SIZE);
try {
char[] buffer = new char[BUFFER_SIZE];
int read;
while ((read = reader.read(buffer, 0, BUFFER_SIZE)) != -1) {
sb.append(buffer, 0, read);
}
} finally {
if (reader != null) {
reader.close();
}
}
return sb.toString();
}

}
9 changes: 9 additions & 0 deletions src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
Manifest-Version: 1.0
Bundle-Vendor: Nuxeo
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
Bundle-Name: nuxeo-preview-highlight-search
Bundle-ManifestVersion: 2
Bundle-SymbolicName: nuxeo-preview-highlight-search
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Nuxeo-Component: OSGI-INF/preview-adapter-contrib.xml
13 changes: 13 additions & 0 deletions src/main/resources/OSGI-INF/deployment-fragment.xml
@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<fragment version="1">

<require>org.nuxeo.ecm.annotations.client</require>

<install>
<!-- unzip the war template -->
<unzip from="${bundle.fileName}" to="/" prefix="web">
<include>web/nuxeo.war/**</include>
</unzip>
</install>

</fragment>
10 changes: 10 additions & 0 deletions src/main/resources/OSGI-INF/preview-adapter-contrib.xml
@@ -0,0 +1,10 @@
<component name="org.nuxeo.preview.highlight.contrib">
<require>org.nuxeo.ecm.platform.annotations.preview.adapter.contrib</require>
<extension
target="org.nuxeo.ecm.platform.preview.adapter.PreviewAdapterManagerComponent"
point="blobPostProcessor">
<blobPostProcessor
class="org.nuxeo.preview.search.highlight.SearchKeywordBlobPostProcessor" />
</extension>

</component>
32 changes: 32 additions & 0 deletions src/main/resources/web/nuxeo.war/css/SearchKeywords.css
@@ -0,0 +1,32 @@
.highlight {
background-color: yellow;
}
.highlight-selected {
background-color: green;
}

#floating-box {
background-color: #fff;
border: 3px double #62B8E5;
border-radius: 5px 0 0 0;
bottom: 0;
box-shadow: -1px -1px 2px #DDDDDD;
float: right;
padding: 0.5em;
position: absolute;
right: 0;
z-index: 10;
}
input [type="text"] {
border: 1px solid #bbb;
border-radius: 5px;
box-shadow: 1px 1px 2px #E0E0E0 inset;
color: #666;
display: inline-block;
font-size: 1em;
line-height: 18px;
margin: .3em;
padding: .3em;
}
input[type="image"] {margin: -0.3em 0.3em;}
input[type="button"] { margin-right: 1em;}

0 comments on commit e800989

Please sign in to comment.