697 lines
25 KiB
Java
697 lines
25 KiB
Java
/*
|
|
* Copyright (C) 2010 Peter Boy <pboy@barkhof.uni-bremen.de> All Rights Reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
package com.arsdigita.cms.contentsection;
|
|
|
|
import com.arsdigita.categorization.Category;
|
|
import com.arsdigita.categorization.RootCategoryCollection;
|
|
import com.arsdigita.cms.ContentSection;
|
|
import com.arsdigita.cms.ContentType;
|
|
import com.arsdigita.cms.ContentTypeLifecycleDefinition;
|
|
import com.arsdigita.cms.ContentTypeWorkflowTemplate;
|
|
import com.arsdigita.cms.SecurityManager;
|
|
import com.arsdigita.cms.Template;
|
|
import com.arsdigita.cms.TemplateManagerFactory;
|
|
import com.arsdigita.cms.lifecycle.LifecycleDefinition;
|
|
import com.arsdigita.cms.lifecycle.PhaseDefinition;
|
|
import com.arsdigita.cms.util.GlobalizationUtil;
|
|
import com.arsdigita.cms.workflow.CMSTask;
|
|
import com.arsdigita.cms.workflow.CMSTaskType;
|
|
import com.arsdigita.domain.DataObjectNotFoundException;
|
|
// import com.arsdigita.domain.DomainObject;
|
|
import com.arsdigita.initializer.InitializationException;
|
|
// import com.arsdigita.kernel.ACSObjectInstantiator;
|
|
import com.arsdigita.kernel.Party;
|
|
import com.arsdigita.kernel.PartyCollection;
|
|
import com.arsdigita.kernel.Role;
|
|
import com.arsdigita.kernel.permissions.PermissionService;
|
|
import com.arsdigita.kernel.permissions.PrivilegeDescriptor;
|
|
// import com.arsdigita.persistence.DataObject;
|
|
// import com.arsdigita.runtime.AbstractConfig;
|
|
import com.arsdigita.cms.LoaderConfig;
|
|
import com.arsdigita.util.Assert;
|
|
import com.arsdigita.util.UncheckedWrapperException;
|
|
// import com.arsdigita.web.ApplicationSetup;
|
|
import com.arsdigita.workflow.simple.WorkflowTemplate;
|
|
import com.arsdigita.xml.XML;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.util.Date;
|
|
import java.util.EmptyStackException;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
// import java.util.NoSuchElementException;
|
|
import java.util.Stack;
|
|
|
|
import org.apache.log4j.Logger;
|
|
import org.xml.sax.Attributes;
|
|
import org.xml.sax.helpers.DefaultHandler;
|
|
|
|
/**
|
|
* Helper class to setup (configure) a newly created content section instance.
|
|
* It handles those properties of a content section, which are to be persisted
|
|
* into the database.
|
|
*
|
|
* @author Peter Boy (pboy@barkhof.uni-bremen.de)
|
|
* @author Jon Orris (jorris@redhat.com)
|
|
* @version $Id: $
|
|
*/
|
|
public final class ContentSectionSetup {
|
|
|
|
private static Logger s_log = Logger.getLogger(ContentSectionSetup.class);
|
|
|
|
private final static String STYLESHEET = "/packages/content-section/xsl/cms.xsl";
|
|
private HashMap m_tasks = new HashMap();
|
|
private LifecycleDefinition m_lcd;
|
|
private WorkflowTemplate m_wf;
|
|
final ContentSection m_section;
|
|
|
|
// Load main CMS configuration file
|
|
private static final LoaderConfig s_conf = new LoaderConfig();
|
|
|
|
|
|
/**
|
|
* Constructor. Using this constructor the content section has to be
|
|
* already created using ContentSection.create(name)
|
|
*
|
|
* @param section name of a content section previously created by
|
|
* ContentSection.create
|
|
*/
|
|
public ContentSectionSetup(ContentSection section) {
|
|
Assert.exists(section, ContentSection.class);
|
|
m_section = section;
|
|
}
|
|
|
|
|
|
/**
|
|
* Wrapper class to create and configure a content section instance
|
|
* in one step.
|
|
*
|
|
*/
|
|
public static void setupContentSectionAppInstance(String name,
|
|
List staffGroup,
|
|
Boolean isPubliclyViewable,
|
|
String itemResolverClassName,
|
|
String templateResolverClassName,
|
|
List sectionContentTypes,
|
|
Boolean useSectionCategories,
|
|
List categoryFileList
|
|
) {
|
|
s_log.info("Creating content section on /" + name);
|
|
|
|
ContentSection section = ContentSection.create(name);
|
|
ContentSectionSetup setup = new ContentSectionSetup(section);
|
|
|
|
// Setup the access controls
|
|
setup.registerRoles(staffGroup);
|
|
setup.registerViewers(isPubliclyViewable);
|
|
setup.registerPublicationCycles();
|
|
setup.registerWorkflowTemplates();
|
|
setup.registerResolvers( itemResolverClassName,templateResolverClassName );
|
|
|
|
// setup.registerContentTypes((List)m_conf.getParameter(TYPES));
|
|
setup.registerContentTypes(sectionContentTypes);
|
|
|
|
// section specific categories, usually not used.
|
|
if (useSectionCategories) {
|
|
// Iterator files = ((List) m_conf.getParameter(CATEGORIES)).iterator();
|
|
// Iterator files = s_conf.getCategoryFileList().iterator();
|
|
Iterator files = categoryFileList.iterator();
|
|
while ( files.hasNext() ) {
|
|
setup.registerCategories((String) files.next());
|
|
}
|
|
}
|
|
setup.registerAlerts();
|
|
|
|
section.save();
|
|
|
|
// return section;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Steps through a list of roles which are part of a staff group and
|
|
* delegates processing of each role.
|
|
*
|
|
* @param roles
|
|
*/
|
|
public void registerRoles(List roles) {
|
|
|
|
Iterator i = roles.iterator();
|
|
while (i.hasNext()) {
|
|
List role = (List)i.next();
|
|
|
|
String name = (String)role.get(0);
|
|
String desc = (String)role.get(1);
|
|
List privileges = (List)role.get(2);
|
|
String task = (role.size() > 3 ? (String)role.get(3) : null);
|
|
|
|
s_log.info("Creating role " + name);
|
|
|
|
|
|
Role group = registerRole(
|
|
name,
|
|
desc,
|
|
privileges);
|
|
|
|
if (task != null)
|
|
m_tasks.put(task, group);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Takes the characteristics of a role and persists them in the database.
|
|
*
|
|
* @param name Name of the role
|
|
* @param desc Description of role
|
|
* @param privileges set (list) of privileges associated with the role
|
|
* @return
|
|
*/
|
|
private Role registerRole(String name, String desc, List privileges) {
|
|
|
|
Role role = m_section.getStaffGroup().createRole(name);
|
|
role.setDescription(desc);
|
|
role.save();
|
|
|
|
Iterator i = privileges.iterator();
|
|
while (i.hasNext()) {
|
|
String priv = (String)i.next();
|
|
s_log.info("Granting privilege cms_" + priv);
|
|
|
|
role.grantPermission(m_section,
|
|
PrivilegeDescriptor.get("cms_" + priv));
|
|
|
|
if (priv.equals(SecurityManager.CATEGORY_ADMIN) ||
|
|
priv.equals(SecurityManager.CATEGORIZE_ITEMS)) {
|
|
RootCategoryCollection coll = Category.getRootCategories(m_section);
|
|
while (coll.next()) {
|
|
if (priv.equals(SecurityManager.CATEGORY_ADMIN)) {
|
|
role.grantPermission(coll.getCategory(),
|
|
PrivilegeDescriptor.ADMIN);
|
|
} else {
|
|
role.grantPermission(coll.getCategory(),
|
|
Category.MAP_DESCRIPTOR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return role;
|
|
}
|
|
|
|
/**
|
|
* Creates a public user role and adds permission to read published pages.
|
|
*
|
|
* @param pub Boolean wether published pages are accessible by the public
|
|
* (i.e. without login).
|
|
*/
|
|
public void registerViewers(Boolean pub) {
|
|
|
|
// XXX Obviously the pub parameter does not determine public access or
|
|
// not!
|
|
Role viewers = m_section.getViewersGroup().createRole("Content Reader");
|
|
viewers.setDescription("Can view published pages within this section");
|
|
viewers.save();
|
|
|
|
// XXX Shouldn't read permission granted depending on pub=true?
|
|
viewers.grantPermission(m_section,
|
|
PrivilegeDescriptor.get("cms_read_item"));
|
|
|
|
String email = Boolean.TRUE.equals(pub) ? "public@nullhost"
|
|
: "registered@nullhost";
|
|
|
|
Party viewer = retrieveParty(email);
|
|
if (viewer == null)
|
|
throw new InitializationException(
|
|
(String) GlobalizationUtil.globalize(
|
|
"cms.installer.cannot_find_group_for_email").localize() + email);
|
|
|
|
s_log.info("Adding " + email + " to viewers role");
|
|
viewers.getGroup().addMemberOrSubgroup(viewer);
|
|
viewers.save();
|
|
}
|
|
|
|
/**
|
|
* Retrieves a party by eimail as part of the viewers registration process.
|
|
*
|
|
* @param email
|
|
* @return
|
|
*/
|
|
private Party retrieveParty(String email) {
|
|
PartyCollection parties = Party.retrieveAllParties();
|
|
parties.filter(email);
|
|
if (parties.next()) {
|
|
Party party = parties.getParty();
|
|
parties.close();
|
|
return party;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Checks for specific item resolver and template resolver classes probably
|
|
* specified in parameters, otherwise uses system wide default parameters
|
|
* specified in CMS global configuration file. Delegates persistence task
|
|
* to ContentSection.
|
|
*
|
|
* @param itemResolverClassName
|
|
* @param templateResolverClassName
|
|
*/
|
|
public void registerResolvers(String itemResolverClassName,
|
|
String templateResolverClassName) {
|
|
|
|
if (itemResolverClassName != null && itemResolverClassName.length()>0) {
|
|
m_section.setItemResolverClass(itemResolverClassName);
|
|
s_log.info("Registering " + itemResolverClassName
|
|
+ " as the item resolver class");
|
|
} else {
|
|
m_section.setItemResolverClass(ContentSection.getConfig()
|
|
.getDefaultItemResolverClass()
|
|
.getName());
|
|
s_log.info("Registering " + itemResolverClassName
|
|
+ " as the item resolver class");
|
|
}
|
|
if (templateResolverClassName != null && templateResolverClassName.length()>0) {
|
|
m_section.setTemplateResolverClass(templateResolverClassName);
|
|
s_log.info("Registering " + templateResolverClassName +
|
|
" as the template resolver class");
|
|
} else {
|
|
m_section.setTemplateResolverClass(ContentSection.getConfig()
|
|
.getDefaultTemplateResolverClass()
|
|
.getName());
|
|
s_log.info("Registering " + templateResolverClassName +
|
|
" as the template resolver class");
|
|
}
|
|
|
|
m_section.save();
|
|
}
|
|
|
|
/**
|
|
* Create a (default) publication cycle and store it in the datavbase
|
|
* (delegated to package com.arsdigita.cms.lifecycle)
|
|
*
|
|
* @throws InitializationException
|
|
*/
|
|
public void registerPublicationCycles()
|
|
throws InitializationException {
|
|
|
|
// The feature lifecycle.
|
|
LifecycleDefinition lcd = new LifecycleDefinition();
|
|
lcd.setLabel( (String) GlobalizationUtil.globalize(
|
|
"cms.installer.simple_publication").localize());
|
|
lcd.setDescription("A one-phase lifecycle for items.");
|
|
lcd.save();
|
|
|
|
PhaseDefinition pd = lcd.addPhaseDefinition(
|
|
"Live", "The first phase. It lasts forever.",
|
|
new Integer(0), null, null);
|
|
pd.save();
|
|
|
|
lcd.save();
|
|
|
|
m_lcd = lcd;
|
|
// Save the created life cycle for the section to be set up.
|
|
m_section.addLifecycleDefinition(lcd);
|
|
m_section.save();
|
|
}
|
|
|
|
/**
|
|
* Defines a (default for section) workflow which gets persisted in tne
|
|
* database.
|
|
*
|
|
* @throws InitializationException
|
|
*/
|
|
public void registerWorkflowTemplates()
|
|
throws InitializationException {
|
|
|
|
// The 3-step production workflow.
|
|
WorkflowTemplate wf = new WorkflowTemplate();
|
|
wf.setLabel( (String) GlobalizationUtil.globalize(
|
|
"cms.installer.production_workflow").localize());
|
|
wf.setDescription("A process that involves creating and approving content.");
|
|
wf.save();
|
|
|
|
CMSTask authoring = new CMSTask();
|
|
authoring.setLabel((String) GlobalizationUtil.globalize(
|
|
"cms.installer.authoring").localize());
|
|
authoring.setDescription("Create content.");
|
|
authoring.save();
|
|
|
|
|
|
Role author = (Role)m_tasks.get("Authoring");
|
|
if (author != null)
|
|
authoring.assignGroup(author.getGroup());
|
|
|
|
authoring.setTaskType(CMSTaskType.retrieve(CMSTaskType.AUTHOR));
|
|
authoring.save();
|
|
|
|
CMSTask approval = new CMSTask();
|
|
approval.setLabel( (String) GlobalizationUtil.globalize(
|
|
"cms.installer.approval").localize());
|
|
approval.setDescription("Approve content.");
|
|
approval.save();
|
|
approval.addDependency(authoring);
|
|
approval.save();
|
|
|
|
Role approver = (Role)m_tasks.get("Approval");
|
|
if (approver != null)
|
|
approval.assignGroup(approver.getGroup());
|
|
|
|
approval.setTaskType(CMSTaskType.retrieve(CMSTaskType.EDIT));
|
|
approval.save();
|
|
|
|
|
|
CMSTask deploy = new CMSTask();
|
|
deploy.setLabel((String) GlobalizationUtil.globalize(
|
|
"cms.installer.deploy").localize());
|
|
deploy.setDescription("Deploy content.");
|
|
deploy.save();
|
|
deploy.addDependency(approval);
|
|
deploy.save();
|
|
|
|
Role publisher = (Role)m_tasks.get("Publishing");
|
|
if (publisher != null)
|
|
deploy.assignGroup(publisher.getGroup());
|
|
|
|
deploy.setTaskType(CMSTaskType.retrieve(CMSTaskType.DEPLOY));
|
|
deploy.save();
|
|
|
|
wf.addTask(authoring);
|
|
wf.addTask(approval);
|
|
wf.addTask(deploy);
|
|
wf.save();
|
|
|
|
m_section.addWorkflowTemplate(wf);
|
|
m_section.save();
|
|
|
|
m_wf = wf;
|
|
}
|
|
|
|
/**
|
|
* Steps through a list of congtent types to be available for this content
|
|
* section and delegates processing of each type.
|
|
*
|
|
* @param types list of content types to be available for this content section
|
|
*/
|
|
public void registerContentTypes(List types) {
|
|
|
|
Iterator i = types.iterator();
|
|
|
|
while (i.hasNext()) {
|
|
Object obj = i.next();
|
|
if (obj instanceof String) {
|
|
registerContentType((String)obj);
|
|
} else {
|
|
List list = (List)obj;
|
|
String name = (String)list.get(0);
|
|
String file = (String)list.get(1);
|
|
|
|
ContentType type = registerContentType(name);
|
|
registerTemplate(type, file);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process one content type and registers it with the current content section.
|
|
*
|
|
* @param name
|
|
* @return
|
|
*/
|
|
ContentType registerContentType(String name) {
|
|
ContentType type = null;
|
|
try {
|
|
type = ContentType.findByAssociatedObjectType(name);
|
|
} catch (DataObjectNotFoundException ex) {
|
|
throw new UncheckedWrapperException(
|
|
(String) GlobalizationUtil.globalize(
|
|
"cms.installer.cannot_find_content_type").localize() + name, ex);
|
|
}
|
|
|
|
s_log.info("Adding type " + name + " to " + m_section.getDisplayName());
|
|
m_section.addContentType(type);
|
|
|
|
s_log.info("Setting the default lifecycle for " +
|
|
name + " to " + m_lcd.getLabel());
|
|
ContentTypeLifecycleDefinition.
|
|
updateLifecycleDefinition(m_section, type, m_lcd);
|
|
m_lcd.save();
|
|
|
|
s_log.info("Setting the default workflow template for " + name +
|
|
" to " + m_wf.getLabel());
|
|
ContentTypeWorkflowTemplate.updateWorkflowTemplate(m_section, type, m_wf);
|
|
m_wf.save();
|
|
|
|
return type;
|
|
}
|
|
|
|
void registerTemplate(ContentType type, String filename) {
|
|
// Use the base of the file name (ie without path & extension)
|
|
// as the template name
|
|
int pos1 = filename.lastIndexOf("/");
|
|
int pos2 = filename.lastIndexOf(".");
|
|
|
|
if (pos2 == -1)
|
|
pos2 = filename.length();
|
|
|
|
String label = filename.substring(pos1+1,pos2);
|
|
|
|
String typename = type.getClassName();
|
|
int pos3 = typename.lastIndexOf(".");
|
|
String name = typename.substring(pos3+1, typename.length()) + "-" + label;
|
|
|
|
Template temp = new Template();
|
|
temp.setContentSection(m_section);
|
|
temp.setName(name);
|
|
temp.setLabel(label);
|
|
temp.setParent(m_section.getTemplatesFolder());
|
|
|
|
final ClassLoader loader = Thread.currentThread
|
|
().getContextClassLoader();
|
|
final InputStream stream = loader.getResourceAsStream
|
|
(filename.substring(1));
|
|
|
|
if (stream == null) {
|
|
throw new IllegalStateException
|
|
((String) GlobalizationUtil.globalize
|
|
("cms.installer.cannot_find_file").localize() + filename);
|
|
}
|
|
|
|
final BufferedReader input = new BufferedReader
|
|
(new InputStreamReader(stream));
|
|
|
|
StringBuffer body = new StringBuffer();
|
|
String line;
|
|
for (;;) {
|
|
try {
|
|
line = input.readLine();
|
|
} catch (IOException ex) {
|
|
throw new UncheckedWrapperException(
|
|
(String) GlobalizationUtil.globalize(
|
|
"cms.installer.cannot_read_line_of_data").localize(), ex);
|
|
}
|
|
if (line == null)
|
|
break;
|
|
|
|
body.append(line);
|
|
body.append("\n");
|
|
}
|
|
|
|
temp.setText(body.toString());
|
|
|
|
temp.save();
|
|
|
|
TemplateManagerFactory.getInstance()
|
|
.addTemplate(m_section, type, temp, "public");
|
|
|
|
temp.publish(m_lcd, new Date());
|
|
// Dan said to comment this out
|
|
// temp.getLifecycle().start();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param filename
|
|
*/
|
|
public void registerCategories(String filename) {
|
|
|
|
if (filename == null) {
|
|
s_log.info("not loading any categories");
|
|
return;
|
|
}
|
|
|
|
s_log.info("loading categories from " + filename);
|
|
|
|
XML.parseResource(filename, new CategoryHandler(m_section));
|
|
}
|
|
|
|
/**
|
|
* For the content section to be created create a role whose member will
|
|
* receive alerts.
|
|
*/
|
|
public void registerAlerts() {
|
|
Role alert = m_section.getStaffGroup().createRole("Alert Recipient");
|
|
alert.setDescription(
|
|
"Receive alerts regarding expiration of pubished content");
|
|
alert.save();
|
|
}
|
|
|
|
|
|
// // Currently there is no way to persists alert preferemces, therefore
|
|
// // currently not a loader or setup task.
|
|
// /**
|
|
// * Steps through a string array of tasks and associated alert events
|
|
// * creating section specific CMStasks.
|
|
// *
|
|
// * @param tasks
|
|
// */
|
|
// public void loadTaskAlerts(String[] taskAlerts) {
|
|
//
|
|
// if (taskAlerts != null) {
|
|
// for (int i=0,n=taskAlerts.length; i<n; i++) {
|
|
// StringTokenizer tok = new StringTokenizer(taskAlerts[i],":");
|
|
// try {
|
|
// String taskName = tok.nextToken();
|
|
// while (tok.hasMoreTokens()) {
|
|
// String operation = tok.nextToken();
|
|
// CMSTask.addAlert(m_section, taskName, operation);
|
|
// }
|
|
// } catch (NoSuchElementException nsee) {
|
|
// s_log.warn("Invalid task alerts definition");
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// }
|
|
// Starting a background thread is an initializer task, not a setup task. The
|
|
// old initializer system did all types of tasks.
|
|
// Retained here for reference purpose until migration has completed.
|
|
// public Timer startNotifierTask(Boolean sendOverdue, Integer duration,
|
|
// Integer alertInterval, Integer max) {
|
|
// Timer unfinished = null;
|
|
// if (sendOverdue.booleanValue()) {
|
|
// if (duration == null || alertInterval == null || max == null) {
|
|
// s_log.info("Not sending overdue task alerts, " +
|
|
// "required initialization parameters were not specified");
|
|
// return null;
|
|
// }
|
|
// // start the Timer as a daemon, so it doesn't keep the JVM from exiting
|
|
// unfinished = new Timer(true);
|
|
// UnfinishedTaskNotifier notifier = new UnfinishedTaskNotifier(
|
|
// m_section, duration.intValue(),
|
|
// alertInterval.intValue(), max.intValue());
|
|
// // schedule the Task to start in 5 minutes, at 1 hour intervals
|
|
// unfinished.schedule(notifier, 5L * 60 * 1000, 60L * 60 * 1000);
|
|
// s_log.info("Sending overdue alerts for tasks greater than " +
|
|
// duration + " hours old");
|
|
// } else {
|
|
// s_log.info("Not sending overdue task alerts");
|
|
// }
|
|
//
|
|
// return unfinished;
|
|
// }
|
|
|
|
// ///////////////////// Private Class Section ////////////////////////////
|
|
|
|
/**
|
|
* SAX Handler for category lists. Creates the categories as they are
|
|
* defined, with structure, in the xml document.
|
|
*/
|
|
private class CategoryHandler extends DefaultHandler {
|
|
private Stack m_cats = new Stack();
|
|
private ContentSection m_section;
|
|
|
|
public CategoryHandler(ContentSection section) {
|
|
m_section = section;
|
|
}
|
|
|
|
@Override
|
|
public void startElement ( String uri, String local,
|
|
String qName, Attributes attrs ) {
|
|
if ("categories".equals(qName)) {
|
|
String name = attrs.getValue("name");
|
|
if (name == null) {
|
|
name = "Root";
|
|
}
|
|
String description = attrs.getValue("description");
|
|
String context = attrs.getValue("context");
|
|
|
|
Category root = Category.getRootForObject(m_section,
|
|
context);
|
|
if (root == null) {
|
|
root = new Category();
|
|
}
|
|
root.setName(name);
|
|
root.setDescription(description);
|
|
|
|
if (root.isNew()) {
|
|
Category.setRootForObject(m_section,
|
|
root,
|
|
context);
|
|
}
|
|
m_cats.push(root);
|
|
PermissionService.setContext(root, m_section);
|
|
} else if ( "category".equals(qName) ) {
|
|
String name = attrs.getValue("name");
|
|
String description = attrs.getValue("description");
|
|
String url = attrs.getValue("url");
|
|
|
|
// set the default description to the name of the category
|
|
if ( description == null ) {
|
|
description = name;
|
|
}
|
|
|
|
s_log.debug("creating category '" + name + "'");
|
|
Category cat = new Category(name, description, url);
|
|
cat.save();
|
|
|
|
Category parent = null;
|
|
try {
|
|
parent = (Category)m_cats.peek();
|
|
} catch ( EmptyStackException ex ) {
|
|
throw new UncheckedWrapperException("no root category", ex);
|
|
}
|
|
|
|
parent.addChild(cat);
|
|
parent.save();
|
|
cat.setDefaultParentCategory(parent);
|
|
cat.save();
|
|
|
|
m_cats.push(cat);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void endElement ( String uri, String local, String qName ) {
|
|
if ( "category".equals(qName) ) {
|
|
m_cats.pop();
|
|
} else if ( "categories".equals(qName)) {
|
|
m_cats.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|