Commit 991bee01 authored by Bert Leunis's avatar Bert Leunis

Merge branch 'master' into bugfix/ESSENTIALS-760

parents 2dd72e1f cae538b3
This diff is collapsed.
This diff is collapsed.
......@@ -8,53 +8,9 @@ Please use the Hippo Essentials feedback form to inform us if you encounter any
suggestions for improvements.
```
# Prerequisites
* Java 8
* Maven 3.x
* Git (http://git-scm.com)
* NodeJS (http://nodejs.org/) 0.10+
* Node Package Manager (http://npmjs.org)
* Grunt (http://gruntjs.org)
* Bower (http://bower.io)
Grunt and Bower can be installed with Node Package Manager:
```shell
sudo npm install -g grunt-cli bower
```
If Bower fails to download zipped dependencies, make sure it uses decompress-zip >= 0.0.4.
(e.g. check the file /usr/lib/node_modules/bower/node_modules/decompress-zip/package.json,
and reinstall Bower if decompress-zip is too old).
## Windows specific preparation instructions
You can automate the installation of NodeJS and NPM using [Chocolatey package manager]
(https://chocolatey.org).
The package for Node.js can be installed using (this will also install NPM):
C:\> choco install nodejs.install
__Note:__ Due to a [bug] (http://jira.codehaus.org/browse/MEXEC-137) in the exec-maven-plugin the Maven build of the
API module will fail as the exec-maven-plugin is unable to find the __grunt__ and __bower__ commands. To fix this, do
the following:
* open folder `C:\Users\USER\AppData\Roaming\npm`
* copy __grunt.cmd__ to __grunt.bat__
* copy __bower.cmd__ to __bower.bat__
More information can be found [here] (http://stackoverflow.com/questions/22393785/exec-maven-plugin-says-cannot-run-specified-program-even-though-it-is-on-the-pa/22395703#22395703).
Installation of Git is also possible using Chocolatey:
C:\> choco install git.install
# Getting Started
## SVN checkout
## Code checkout
To get started with the Hippo Essentials, checkout the code. You have two options to check out
the project. The example commands below use the potentially unstable trunk snapshot. Consider
......@@ -62,17 +18,17 @@ using a tag instead.
### Read-only
```shell
svn co http://svn.onehippo.org/repos/hippo/hippo-cms7/essentials/trunk essentials
git clone git@code.onehippo.org:cms-community/hippo-essentials.git
```
### Read-write (you'll need Hippo SVN account for this)
### Read-write (you'll need Hippo GIT account for this)
```shell
svn co https://svn.onehippo.org/repos/hippo/hippo-cms7/essentials/trunk essentials
git clone git@code.onehippo.org:cms-community/hippo-essentials.git
```
### Build the essentials components:
```shell
cd essentials
cd hippo-essentials
mvn clean install
```
......@@ -83,8 +39,8 @@ mvn clean && mvn validate -Ppedantic
### Create and install archetype locally:
```shell
svn co http://svn.onehippo.org/repos/hippo/hippo-cms7/archetype/trunk/ archetype
cd archetype
git clone git@code.onehippo.org:cms-community/hippo-project-archetype.git
cd hippo-project-archetype
mvn clean install
```
......@@ -107,7 +63,7 @@ mvn -P cargo.run -Drepo.path=storage
The following URLs are available from this project:
* CMS at http://localhost:8080/cms
* Website at http://localhost:8080/site
* Website at http://localhost:8080/site
* Essentials dashboard at http://localhost:8080/essentials
Logs are located in `target/tomcat8x/logs`
......@@ -161,6 +117,6 @@ or permanently in your project's file
##Copyright and license
Copyright 2013-2015 Hippo B.V.
Distributed under the [Apache 2.0 license](http://svn.onehippo.org/repos/hippo/hippo-cms7/essentials/trunk/LICENSE).
Copyright 2013-2016 Hippo B.V.
Distributed under the [Apache 2.0 license](https://code.onehippo.org/cms-community/hippo-essentials/blob/master/LICENSE).
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2014-2015 Hippo B.V. (http://www.onehippo.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onehippo.cms7</groupId>
<artifactId>hippo-essentials-components</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<name>Hippo Essentials CMS Component Library</name>
<description>Hippo Essentials CMS Component Library</description>
<artifactId>hippo-essentials-components-cms</artifactId>
<properties>
<jsoup.version>1.7.1</jsoup.version>
<rome.version>0.9</rome.version>
</properties>
<dependencies>
<dependency>
<!-- jsoup HTML parser library @ http://jsoup.org/ -->
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>org.onehippo.cms7</groupId>
<artifactId>hippo-repository-connector</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>${rome.version}</version>
</dependency>
</dependencies>
<build>
<defaultGoal>package</defaultGoal>
</build>
</project>
/*
* Copyright 2015 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onehippo.cms7.essentials.components.cms.blog;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.apache.commons.lang.ArrayUtils;
import org.hippoecm.repository.util.JcrUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BlogImporterConfiguration {
public static final Logger log = LoggerFactory.getLogger(BlogImporterConfiguration.class);
private static final String PROP_CRONEXPRESSION = "cronExpression";
private static final String PROP_ACTIVE = "active";
private static final String PROP_RUNNOW = "runInstantly";
private final String cronExpression;
private final Boolean active;
private final Boolean runNow;
private final String blogBasePath;
private final String authorsBasePath;
private final String projectNamespace;
private final List<String> urls;
private final List<String> authors;
public BlogImporterConfiguration(Node moduleConfigNode) throws RepositoryException {
cronExpression = JcrUtils.getStringProperty(moduleConfigNode, PROP_CRONEXPRESSION, null);
active = JcrUtils.getBooleanProperty(moduleConfigNode, PROP_ACTIVE, Boolean.FALSE);
runNow = JcrUtils.getBooleanProperty(moduleConfigNode, PROP_RUNNOW, Boolean.FALSE);
projectNamespace = JcrUtils.getStringProperty(moduleConfigNode, BlogImporterJob.PROJECT_NAMESPACE, null);
blogBasePath = JcrUtils.getStringProperty(moduleConfigNode, BlogImporterJob.BLOGS_BASE_PATH, null);
authorsBasePath = JcrUtils.getStringProperty(moduleConfigNode, BlogImporterJob.AUTHORS_BASE_PATH, null);
urls = Arrays.asList(JcrUtils.getMultipleStringProperty(moduleConfigNode, BlogImporterJob.URLS, ArrayUtils.EMPTY_STRING_ARRAY));
authors = Arrays.asList(JcrUtils.getMultipleStringProperty(moduleConfigNode, BlogImporterJob.AUTHORS, ArrayUtils.EMPTY_STRING_ARRAY));
if (authors.size() != urls.size()) {
log.error("Authors and URL size mismatch, no blogs will be imported.");
authors.clear();
urls.clear();
}
}
public String getCronExpression() {
return cronExpression;
}
public boolean isActive() {
return active;
}
public boolean isRunNow() {
return runNow;
}
public String getBlogBasePath() {
return blogBasePath;
}
public String getAuthorsBasePath() {
return authorsBasePath;
}
public String getProjectNamespace() {
return projectNamespace;
}
public List<String> getUrls() {
return Collections.unmodifiableList(urls);
}
public List<String> getAuthors() {
return Collections.unmodifiableList(authors);
}
}
/*
* Copyright 2014 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onehippo.cms7.essentials.components.cms.blog;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.onehippo.cms7.essentials.components.cms.handlers.HandlerUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
/**
* The BlogUpdater derives the bloggers' names from the linked Author document and stores them in the blog's
* AuthorNames meta data field. That field is/will be used for faceted navigation.
*/
public final class BlogUpdater {
private static final Logger log = LoggerFactory.getLogger(BlogUpdater.class);
private BlogUpdater() {
}
/**
* Indicate if the document variant represented by node should be handled.
*
* @param node JCR node to consider
* @return true if the node is interesting, false otherwise.
* @throws javax.jcr.RepositoryException
*/
public static boolean wants(final Node node, final String documentType) throws RepositoryException {
// check if namespace is registered namespace, skip otherwise:
if(Strings.isNullOrEmpty(documentType) || documentType.indexOf(':')==-1){
return false;
}
final Iterable<String> iterable = Splitter.on(':').omitEmptyStrings().trimResults().split(documentType);
final Iterator<String> iterator = iterable.iterator();
if(iterator.hasNext()){
final String namespacePrefix = iterator.next();
if(!namespacePrefixExists(node.getSession(), namespacePrefix)){
return false;
}
}
return node.getPrimaryNodeType().isNodeType(documentType);
}
public static boolean namespacePrefixExists(final Session session, final String prefix) {
try {
final NamespaceRegistry namespaceRegistry = session.getWorkspace().getNamespaceRegistry();
// Check whether a URI is mapped for the prefix
final String p = namespaceRegistry.getURI(prefix);
return !Strings.isNullOrEmpty(p);
} catch (NamespaceException e) {
// expected:
return false;
} catch (RepositoryException e) {
log.error("Error while determining namespace check.", e);
}
return false;
}
/**
* Handle the Save event for a blogpost document.
*
* @param blogpost JCR node representing the blogpost
* @return indication whether or not changes need to be saved.
* @throws javax.jcr.RepositoryException
*/
public static boolean handleSaved(final Node blogpost, final String projectNamespace) throws RepositoryException {
// Delete the old property
final String authorNamesProp = projectNamespace + ":authornames";
if (blogpost.hasProperty(authorNamesProp)) {
blogpost.getProperty(authorNamesProp).remove();
}
// Construct the new property
final NodeIterator authorMirrors = blogpost.getNodes(projectNamespace + ":authors");
final Collection<String> authorNames = new ArrayList<String>();
// TODO mm check this (was :title???)
final String authorProperty = projectNamespace + ":fullname";
while (authorMirrors.hasNext()) {
final Node mirror = authorMirrors.nextNode();
final Node author = HandlerUtils.getReferencedVariant(mirror, "published");
if (author != null) {
if (author.hasProperty(authorProperty)) {
final Property property = author.getProperty(authorProperty);
authorNames.add(property.getString());
} else {
log.warn("Author node:[{}] has no property:[{}]", author.getPath(), authorProperty);
}
} else {
log.warn("Author property couldn't be updated because referenced author node is not published yet: {}", mirror.getPath());
}
}
if (authorNames.size() > 0) {
blogpost.setProperty(authorNamesProp, authorNames.toArray(new String[authorNames.size()]));
}
return true;
}
}
/*
* Copyright 2014-2015 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onehippo.cms7.essentials.components.cms.handlers;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.onehippo.cms7.essentials.components.cms.blog.BlogUpdater;
import org.onehippo.cms7.event.HippoEvent;
import org.onehippo.cms7.event.HippoEventConstants;
import org.onehippo.cms7.services.eventbus.Subscribe;
import org.onehippo.repository.events.HippoWorkflowEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
/**
* @version "$Id$"
*/
public class AuthorFieldHandler implements WorkflowEventHandler {
private static Logger log = LoggerFactory.getLogger(AuthorFieldHandler.class);
// Regrettably, the CMS doesn't expose below string in an API...
private static final String METHOD_NAME_SAVE = "commitEditableInstance";
private final Session session;
private final String projectNamespacePrefix;
public AuthorFieldHandler(final String projectNamespacePrefix, final Session session) {
this.projectNamespacePrefix = projectNamespacePrefix;
this.session = session;
}
/**
* Dispatch the event per workflow action.
*
* @param event the event.
*/
@Override
@Subscribe
public void handle(final HippoEvent<?> event) {
if (Strings.isNullOrEmpty(projectNamespacePrefix)) {
return;
}
if (HippoEventConstants.CATEGORY_WORKFLOW.equals(event.category())) {
HippoWorkflowEvent<?> wfEvent = new HippoWorkflowEvent(event);
if (METHOD_NAME_SAVE.equals(wfEvent.action())) {
dispatchSaveEvent(wfEvent, session);
}
}
}
/**
* Dispatch a "save" event.
* Derive the unpublished variant of the document the save event pertains to, and check who wants it.
*
* @param event the event.
* @param session the JCR session
*/
@SuppressWarnings("HippoHstCallNodeRefreshInspection")
private void dispatchSaveEvent(HippoWorkflowEvent<?> event, final Session session) {
final String handleUuid = event.subjectId();
try {
final Node handle = session.getNodeByIdentifier(handleUuid);
final Node variant = HandlerUtils.getVariant(handle, "unpublished");
if (variant != null && BlogUpdater.wants(variant, projectNamespacePrefix + ":blogpost")) {
if (BlogUpdater.handleSaved(variant, projectNamespacePrefix)) {
session.save();
}
}
} catch (RepositoryException ex) {
log.debug("Failed to process node for handle UUID '" + handleUuid + "'.", ex);
try {
session.refresh(false);
} catch (RepositoryException e) {
log.error("Error refreshing session", e);
}
}
}
}
/*
* Copyright 2014 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onehippo.cms7.essentials.components.cms.handlers;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
/**
* @version "$Id$"
*/
public final class HandlerUtils {
private HandlerUtils() {
}
/**
* @param handle JCR node representing a handle
* @param state desired state of the variant
* @return JCR node representing that variant, or null.
* @throws javax.jcr.RepositoryException
*/
public static Node getVariant(final Node handle, final String state) throws RepositoryException {
final NodeIterator variants = handle.getNodes(handle.getName());
while (variants.hasNext()) {
final Node variant = variants.nextNode();
if (variant.hasProperty("hippostd:state") && variant.getProperty("hippostd:state").getString().equals(state)) {
return variant;
}
}
return null;
}
/**
* Helper function to derive a certain document variant, given a hippo:mirror node.
* Optimally, the CMS or repository would provide this functionality.
*
* @param mirror repository node of type hippo:mirror
* @param state desired state of the variant
* @return JCR node representing that variant, or null.
* @throws javax.jcr.RepositoryException
*/
public static Node getReferencedVariant(final Node mirror, final String state) throws RepositoryException {
final Session session = mirror.getSession();
final String rootUuid = session.getRootNode().getIdentifier();
final String uuid = mirror.getProperty("hippo:docbase").getString();
Node variant = null;
if (!rootUuid.equals(uuid)) {
final Node authorHandle = session.getNodeByIdentifier(uuid);
variant = getVariant(authorHandle, state);
}
return variant;
}
}
/*
* Copyright 2014 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onehippo.cms7.essentials.components.cms.handlers;