Dies ist die erste Version der lokalisierten Kategorien.

Folgendes funktioniert:

- Anlegen und Löschen von Sprachversionen per Adminoberfläche
- Config-Parameter für Fallback-Modus, unterstützte Sprachen und 
  Standard-Sprache
- Lokalisierte URLs in navigation
- Dekativieren von Sprachversionen

Folgendes funktioniert noch nicht:
- Bearbeiten von Sprachversionen per Adminoberfläche
- evt. ist die Untersützung von lokalisierten URLs noch nicht vollständig
  d.h. in anderen Anwendungen als navigation

Quasimodo



git-svn-id: https://svn.libreccm.org/ccm/trunk@41 8810af33-2d31-482b-a856-94f89814c4df
master
quasi 2008-05-08 18:35:32 +00:00
parent 606b7d90d9
commit 42b45983b8
14 changed files with 1125 additions and 152 deletions

View File

@ -262,6 +262,7 @@ cms.ui.cannot_assign_groups_to_owner=cannot assign groups to owner
cms.ui.categories=Categories
cms.ui.category._back= (back) 
cms.ui.category.add=Add category
cms.ui.categoryLocalization.add=Add category localization
cms.ui.category.add_index_item=Set index item
cms.ui.category.add_use_context=Add Use Context
cms.ui.category.assigned_purposes=Assigned Purposes:
@ -280,6 +281,7 @@ cms.ui.category.delete_prompt=Are you sure you want to delete this category?
cms.ui.category.descriptionn=Description:\n
cms.ui.category.details=Category details
cms.ui.category.edit=Edit category
cms.ui.categoryLocalization.edit=Edit category
cms.ui.category.edit_a_category=Edit a Category
cms.ui.category.edit_purposes=Edit purposes
cms.ui.category.index_item.select=Select an index item for this category
@ -291,6 +293,16 @@ cms.ui.category.is_not_abstract=Can you place objects in this category?
cms.ui.category.item.none=There are no items in this category
cms.ui.category.item=Categorized item
cms.ui.category.labeln=Label:\n
cms.ui.category.localizations=Category Localizations
cms.ui.category.localization.add=Add localization
cms.ui.category.localization.name=Name
cms.ui.category.localization.description=Description
cms.ui.category.localization.url=URL
cms.ui.category.localization.locale=Language
cms.ui.category.localization.action=Action
cms.ui.category.localization.confirm_delete=Delete this localization?
cms.ui.category.localization.none=This category has no localizations
cms.ui.category.localization.error_locale=Please select a locale
cms.ui.category.linked.add=Add or remove linked categories
cms.ui.category.linked.none=This category has no linked categories
cms.ui.category.linked=Linked categories

View File

@ -295,6 +295,16 @@ cms.ui.category.labeln=Label:\n
cms.ui.category.linked.add=Verkn\u00FCpfte Kategorien hinzuf\u00FCgen oder entfernen
cms.ui.category.linked.none=Diese Kategorie hat keine verkn\u00FCpften Kategorien
cms.ui.category.linked=Verkn\u00FCpfte Kategorien
cms.ui.category.localizations=Sprachversionen
cms.ui.category.localization.add=Sprachversion hinzuf\u00FCgen
cms.ui.category.localization.name=Name
cms.ui.category.localization.description=Beschreibung
cms.ui.category.localization.url=URL
cms.ui.category.localization.locale=Sprache
cms.ui.category.localization.action=Aktion
cms.ui.category.localization.confirm_delete=Soll diese Sprachversion gelöscht werden?
cms.ui.category.localization.none=Diese Kategorie hat keine Sprachversionen
cms.ui.category.localization.error_locale=Bitte wählen Sie eine Sprache aus
cms.ui.category.name_not_unique=Es gibt bereits eine Kategorie mit diesem Namen.
cms.ui.category.no_categorized_objects=Es gibt keine kategorisierten Objekte
cms.ui.category.no_category_purposes=No Category Purposes

View File

@ -58,9 +58,7 @@ class BaseCategoryForm extends BaseForm {
final TextField m_url;
final RadioGroup m_isAbstract;
final RadioGroup m_isEnabled;
private Label m_script = new Label("<script language=\"javascript\" src=\"/javascript/manipulate-input.js\"></script>", false);
private Label m_script = new Label("<script language=\"javascript\" src=\"/javascript/manipulate-input.js\"></script>", false);
private final static String NAME = "name";
private final static String DESCRIPTION = "description";

View File

@ -91,22 +91,28 @@ public final class CategoryAdminPane extends BaseAdminPane {
m_contextModel = new UseContextSelectionModel(new StringParameter(CONTEXT_SELECTED));
/* Left column */
/* Use context section */
List list = new List(new CategoryUseContextModelBuilder());
list.setSelectionModel(m_contextModel);
list.addChangeListener(new ContextSelectionListener());
/* Category tree section */
m_categoryTree = new BaseTree(new CategoryTreeModelBuilder(m_contextModel));
m_categoryTree.addChangeListener(new SelectionListener());
m_model = m_categoryTree.getSelectionModel();
setSelectionModel(m_model);
setSelector(m_categoryTree);
/* setup use context form */
final Section contextSection = new Section();
contextSection.setHeading(new Label(gz("cms.ui.category.use_contexts")));
ActionGroup contextGroup = new ActionGroup();
contextSection.setBody(contextGroup);
contextGroup.setSubject(list);
/* Add use context form to pane */
ActionLink addContextAction = new ActionLink(new Label(gz("cms.ui.category.add_use_context")));
Form addContextForm = new AddUseContextForm(m_contextModel);
getBody().add(addContextForm);
@ -129,6 +135,10 @@ public final class CategoryAdminPane extends BaseAdminPane {
m_parent = new ParentRequestLocal();
m_category = new SelectionRequestLocal();
/* Right column */
/* Context section aka category details */
/* Action links */
setAdd(gz("cms.ui.category.add"),
new CategoryAddForm(m_category, m_model));

View File

@ -73,8 +73,17 @@ public final class CategoryCollectionListModel implements ListModel {
return m_cat;
}
/**
* Liest den Namen der Kategorie aus. Angepaßt, damit hier immer
* der Wert aus Category gelesen wird und nicht die lokalisierte
* Version. Ist hier sinnvoll, da es Teil der Adminoberfläche für
* Kategorien ist. Eine lokalisierte Anzeige würde hier nur zu
* Verwirrung führen.
*
* Quasimodo
*/
public Object getElement() {
return getCategory().getName();
return getCategory().getName("");
}
public String getKey() {

View File

@ -61,9 +61,9 @@ final class CategoryEditForm extends BaseCategoryForm {
final PageState state = e.getPageState();
final Category category = m_category.getCategory(state);
m_name.setValue(state, category.getName());
m_description.setValue(state, category.getDescription());
m_url.setValue(state, category.getURL());
m_name.setValue(state, category.getName(""));
m_description.setValue(state, category.getDescription(""));
m_url.setValue(state, category.getURL(""));
// this seems anti-intuitive but the question is "can you place
// items in this category. If the user says "yes" then the
// category is not abstract
@ -73,7 +73,7 @@ final class CategoryEditForm extends BaseCategoryForm {
m_isAbstract.setValue(state, "yes");
}
if (category.isEnabled()) {
if (category.isEnabled("")) {
m_isEnabled.setValue(state, "yes");
} else {
m_isEnabled.setValue(state, "no");

View File

@ -32,6 +32,7 @@ import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.ChangeEvent;
import com.arsdigita.bebop.event.ChangeListener;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.categorization.CategorizationConfig;
import com.arsdigita.categorization.CategorizedCollection;
import com.arsdigita.categorization.Category;
import com.arsdigita.categorization.CategoryNotFoundException;
@ -54,6 +55,7 @@ import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.OID;
import com.arsdigita.toolbox.ui.ActionGroup;
import com.arsdigita.toolbox.ui.PropertyList;
import com.arsdigita.toolbox.ui.PropertyList.Property;
import com.arsdigita.toolbox.ui.Section;
import com.arsdigita.util.Assert;
import com.arsdigita.web.Web;
@ -73,146 +75,172 @@ import org.apache.log4j.Logger;
*/
class CategoryItemPane extends BaseItemPane {
public static final String versionId =
"$Id: CategoryItemPane.java 1329 2006-09-27 11:47:05Z sskracic $" +
"$Author: sskracic $" +
"$DateTime: 2004/08/17 23:15:09 $";
"$Id: CategoryItemPane.java 1329 2006-09-27 11:47:05Z sskracic $" +
"$Author: sskracic $" +
"$DateTime: 2004/08/17 23:15:09 $";
private static final Logger s_log = Logger.getLogger
(CategoryItemPane.class);
(CategoryItemPane.class);
private final SingleSelectionModel m_model;
private final CategoryRequestLocal m_category;
private final SimpleContainer m_detailPane;
public CategoryItemPane(final SingleSelectionModel model,
final CategoryRequestLocal category,
final ActionLink addLink,
final ActionLink editLink,
final ActionLink deleteLink) {
final CategoryRequestLocal category,
final ActionLink addLink,
final ActionLink editLink,
final ActionLink deleteLink) {
m_model = model;
m_category = category;
// Details
m_detailPane = new SimpleContainer();
add(m_detailPane);
setDefault(m_detailPane);
final ActionLink orderItemsLink =
new ActionLink(new Label(gz("cms.ui.category.categorized_objects"))) {
public boolean isVisible(PageState state) {
// update for live items only
if (!super.isVisible(state)) {
return false;
}
CategorizedCollection items = m_category.getCategory
(state).getObjects(ContentItem.BASE_DATA_OBJECT_TYPE);
items.addEqualsFilter(ContentItem.VERSION,ContentItem.LIVE);
boolean canOrder = items.size() > 1;
items.close();
return canOrder;
final ActionLink orderItemsLink = new ActionLink(new Label(gz("cms.ui.category.categorized_objects"))) {
public boolean isVisible(PageState state) {
// update for live items only
if (!super.isVisible(state)) {
return false;
}
};
CategorizedCollection items = m_category.getCategory
(state).getObjects(ContentItem.BASE_DATA_OBJECT_TYPE);
items.addEqualsFilter(ContentItem.VERSION,ContentItem.LIVE);
boolean canOrder = items.size() > 1;
items.close();
return canOrder;
}
};
final Form orderItemsForm = new OrderItemsForm(m_category);
final Form orderItemsForm2 = new OrderItemsForm(m_category);
add(orderItemsForm);
add(orderItemsForm2);
// Change index item
final ActionLink indexLink = new ActionLink(new Label(gz("cms.ui.category.change_index_item")));
final Form indexForm = new IndexItemSelectionForm(m_category);
add(indexForm);
ViewItemLink viewIndexLink = new ViewItemLink(new Label(gz("cms.ui.category.view_index_item")),"");
EditItemLink editIndexLink = new EditItemLink(new Label(gz("cms.ui.category.edit_index_item")),"");
// Summary
m_detailPane.add(new SummarySection(editLink, deleteLink, indexLink, viewIndexLink, editIndexLink, orderItemsLink));
// Quasimodo: BEGIN
// Localizations
ActionLink addCategoryLocalizationLink = new ActionLink(new Label(gz("cms.ui.category.localization.add"))) {
public boolean isVisible(PageState state) {
// Only show addLanguage button, if there are langauges to add
int countSupportedLanguages = (new CategorizationConfig()).getSupportedLanguages().countTokens();
long countLanguages = m_category.getCategory(state).getCategoryLocalizationCollection().size();
if(countLanguages < countSupportedLanguages) {
return true;
} else {
return false;
}
}
};
CategoryLocalizationAddForm addCategoryLocalizationForm = new CategoryLocalizationAddForm(m_category);
m_detailPane.add(new CategoryLocalizationSection(addCategoryLocalizationLink));
add(addCategoryLocalizationForm);
connect(addCategoryLocalizationLink, addCategoryLocalizationForm);
connect(addCategoryLocalizationForm);
// Quasimodo: END
// Subcategories
m_detailPane.add(new SubcategorySection(addLink));
// Linked categories
final ActionLink linkAddLink = new ActionLink
(new Label(gz("cms.ui.category.linked.add")));
(new Label(gz("cms.ui.category.linked.add")));
final Form linkForm = new LinkForm(m_category);
add(linkForm);
linkAddLink.addActionListener(new NavigationListener(linkForm));
linkForm.addSubmissionListener(new CancelListener(linkForm));
m_detailPane.add(new LinkedCategorySection(linkAddLink));
// Templates
m_detailPane.add(new AdminVisible(new CategoryTemplateSection()));
// Permissions
m_detailPane.add(new PermissionsSection());
connect(indexLink, indexForm);
connect(indexForm);
connect(orderItemsLink, orderItemsForm);
connect(orderItemsForm);
}
private class EditVisible extends VisibilityComponent {
EditVisible(final Component child) {
super(child, null);
}
public boolean hasPermission(PageState ps) {
return m_category.getCategory(ps).canEdit();
}
}
private class AdminVisible extends VisibilityComponent {
AdminVisible(final Component child) {
super(child, null);
}
public boolean hasPermission(PageState ps) {
return m_category.getCategory(ps).canAdmin();
}
}
private class SummarySection extends Section {
SummarySection(final ActionLink editLink,
final ActionLink deleteLink,
final ActionLink indexLink,
final ActionLink orderItemsLink) {
final ActionLink deleteLink,
final ActionLink indexLink,
final ActionLink orderItemsLink) {
setHeading(new Label(gz("cms.ui.category.details")));
final ActionGroup group = new ActionGroup();
setBody(group);
group.setSubject(new Properties());
group.addAction(new EditVisible(editLink), ActionGroup.EDIT);
group.addAction(new EditVisible(orderItemsLink));
group.addAction(new EditVisible(indexLink));
group.addAction(new AdminVisible(deleteLink), ActionGroup.DELETE);
}
/*
* This alternative constructor sets two additional links, allowing the user to view and
* edit the content index item.
*/
SummarySection(final ActionLink editLink,
final ActionLink deleteLink,
final ActionLink indexLink,
final BaseLink viewIndexItem,
final BaseLink editIndexItem,
final ActionLink orderItemsLink) {
final ActionLink deleteLink,
final ActionLink indexLink,
final BaseLink viewIndexItem,
final BaseLink editIndexItem,
final ActionLink orderItemsLink) {
setHeading(new Label(gz("cms.ui.category.details")));
final ActionGroup group = new ActionGroup();
setBody(group);
group.setSubject(new Properties());
group.addAction(new EditVisible(editLink), ActionGroup.EDIT);
group.addAction(new EditVisible(orderItemsLink));
group.addAction(new EditVisible(indexLink));
@ -220,131 +248,145 @@ class CategoryItemPane extends BaseItemPane {
group.addAction(new EditVisible(editIndexItem));
group.addAction(new AdminVisible(deleteLink), ActionGroup.DELETE);
}
private class Properties extends PropertyList {
protected final java.util.List properties(final PageState state) {
final java.util.List props = super.properties(state);
final Category category = m_category.getCategory(state);
final ACSObject item = category.getDirectIndexObject();
String itemTitle = "";
if (item != null) {
itemTitle = item.getDisplayName();
}
props.add(new Property(gz("cms.ui.name"),
category.getName()));
category.getName("")));
props.add(new Property(gz("cms.ui.description"),
category.getDescription()));
category.getDescription("")));
props.add(new Property(gz("cms.ui.category.url"),
category.getURL()));
category.getURL("")));
props.add(new Property(gz("cms.ui.category.is_not_abstract"),
category.isAbstract() ?
gz("cms.ui.no") :
gz("cms.ui.yes")));
category.isAbstract() ?
gz("cms.ui.no") :
gz("cms.ui.yes")));
props.add(new Property(gz("cms.ui.category.is_enabled"),
category.isEnabled() ?
gz("cms.ui.yes") :
gz("cms.ui.no")));
category.isEnabled("") ?
gz("cms.ui.yes") :
gz("cms.ui.no")));
props.add(new Property(gz("cms.ui.category.index_item"),
itemTitle));
itemTitle));
return props;
}
}
}
// Quasimodo: BEGIN
// CategoryLocalizationSection
private class CategoryLocalizationSection extends Section {
CategoryLocalizationSection(ActionLink addLink) {
setHeading(new Label(gz("cms.ui.category.localizations")));
final ActionGroup group = new ActionGroup();
setBody(group);
group.setSubject(new CategoryLocalizationTable(m_category, m_model));
group.addAction(new AdminVisible(addLink), ActionGroup.ADD);
}
}
private class SubcategorySection extends Section {
SubcategorySection(final ActionLink addLink) {
setHeading(new Label(gz("cms.ui.category.subcategories")));
final ActionGroup group = new ActionGroup();
setBody(group);
group.setSubject(new SubcategoryList(m_category, m_model));
group.addAction(new AdminVisible(addLink), ActionGroup.ADD);
}
}
private class LinkedCategorySection extends Section {
LinkedCategorySection(final ActionLink linkAddLink) {
setHeading(new Label(gz("cms.ui.category.linked")));
final ActionGroup group = new ActionGroup();
setBody(group);
group.setSubject(new CategoryLinks(m_category, m_model));
group.addAction(new EditVisible(linkAddLink), ActionGroup.EDIT);
}
public final boolean isVisible(final PageState state) {
return !m_category.getCategory(state).isRoot();
}
}
private class CategoryTemplateSection extends Section {
CategoryTemplateSection() {
setHeading(new Label(gz("cms.ui.category.templates")));
final ActionGroup group = new ActionGroup();
setBody(group);
group.setSubject(new CategoryTemplates(m_category));
// XXX secvis
//group.addAction(link);
}
}
private class PermissionsSection extends Section {
public boolean isVisible(PageState ps) {
Category cat = m_category.getCategory(ps);
return !cat.isRoot() && cat.canAdmin();
}
PermissionsSection() {
setHeading(new Label(gz("cms.ui.permissions")));
final ActionGroup group = new ActionGroup();
setBody(group);
PrivilegeDescriptor[] privs = new PrivilegeDescriptor[] {
PrivilegeDescriptor.EDIT,
Category.MAP_DESCRIPTOR,
PrivilegeDescriptor.DELETE,
PrivilegeDescriptor.ADMIN
};
HashMap privMap = new HashMap();
privMap.put("edit", "Edit");
privMap.put("delete", "Delete");
privMap.put(Category.MAP_DESCRIPTOR.getName(), "Categorize Items");
privMap.put("admin", "Admin");
final CMSPermissionsPane permPane = new CMSPermissionsPane
(privs, privMap, new ACSObjectSelectionModel(m_model)) {
(privs, privMap, new ACSObjectSelectionModel(m_model)) {
public void showAdmin(PageState ps) {
Assert.exists(m_model.getSelectedKey(ps));
super.showAdmin(ps);
getAdminListingPanel().setVisible(ps, false);
}
};
final ActionLink restoreDefault = new ActionLink(new Label(gz("cms.ui.restore_default_permissions"))) {
public boolean isVisible(PageState ps) {
Category cat = m_category.getCategory(ps);
return PermissionService.getContext(cat) == null;
}
};
final ActionLink useCustom = new ActionLink(new Label(gz("cms.ui.use_custom_permissions"))) {
public boolean isVisible(PageState ps) {
Category cat = m_category.getCategory(ps);
return PermissionService.getContext(cat) != null;
}
};
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent event) {
PageState state = event.getPageState();
@ -363,18 +405,18 @@ class CategoryItemPane extends BaseItemPane {
parent = cat.getDefaultParentCategory();
} catch (CategoryNotFoundException ce) {
throw new IllegalStateException(
"link shouldn't exist for root categories");
"link shouldn't exist for root categories");
}
PermissionService.setContext(cat, parent);
// revoke all direct permissions so category will only
// have inherited permissions
ObjectPermissionCollection perms =
PermissionService.getDirectGrantedPermissions(
PermissionService.getDirectGrantedPermissions(
cat.getOID());
while (perms.next()) {
PermissionService.revokePermission(
new PermissionDescriptor(
new PermissionDescriptor(
perms.getPrivilege(), cat.getOID(),
perms.getGranteeOID()));
}
@ -382,17 +424,17 @@ class CategoryItemPane extends BaseItemPane {
permPane.reset(state);
}
};
restoreDefault.addActionListener(al);
useCustom.addActionListener(al);
SimpleContainer links = new SimpleContainer();
links.add(restoreDefault);
links.add(useCustom);
group.setSubject(permPane);
group.addAction(links);
m_model.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
PageState ps = e.getPageState();
@ -400,22 +442,22 @@ class CategoryItemPane extends BaseItemPane {
});
}
}
private static class OrderItemsForm extends CMSForm {
public OrderItemsForm(CategoryRequestLocal category) {
super("orderItems", new SimpleContainer());
Label header = new Label(gz("cms.ui.category.categorized_objects"));
header.setFontWeight(Label.BOLD);
add(header);
add(new CategorizedObjectsList(category));
add(new Submit("Done"));
}
}
/*
* This private class creates a link to the index item for a category.
*/
@ -423,11 +465,11 @@ class CategoryItemPane extends BaseItemPane {
ViewItemLink(Component c, String s) {
super(c,s);
}
// Build the preview link. This uses a standard redirect link to find the content.
// The prepareURL method is called by the printwriter
protected String prepareURL(final PageState state, String location) {
ContentItem indexItem = ((ContentBundle)(m_category.getCategory(state).getDirectIndexObject())).getPrimaryInstance();
if(indexItem==null) {
return "";
@ -435,7 +477,7 @@ class CategoryItemPane extends BaseItemPane {
return "/redirect/?oid=" + URLEncoder.encode(indexItem.getOID().toString());
}
}
// We only show this link when an index item exists for this category
public boolean isVisible(PageState state) {
if (!super.isVisible(state)) {
@ -449,12 +491,12 @@ class CategoryItemPane extends BaseItemPane {
}
}
};
private class EditItemLink extends Link {
EditItemLink(Component c, String s) {
super(c,s);
}
// Build the preview link. This is based on code in the ContentSoonExpiredPane class.
// The prepareURL method of the parent is overwritten. This method is called by the printwriter
protected String prepareURL(final PageState state, String location) {
@ -470,7 +512,7 @@ class CategoryItemPane extends BaseItemPane {
return "item.jsp?item_id=" + draftID + "&set_tab=" + ContentItemPage.AUTHORING_TAB;
}
}
// We only show this link when an index item exists for this category and
// the user is allowed to edit this item.
public boolean isVisible(PageState state) {
@ -484,7 +526,7 @@ class CategoryItemPane extends BaseItemPane {
return isItemEditable((ContentItem)indexItem,state);
}
}
// This method checks whether a usern is allowed to edit a particular item
private boolean isItemEditable(ContentItem item, PageState state) {
BigDecimal id = item.getID();

View File

@ -22,10 +22,13 @@ model com.arsdigita.categorization;
import com.arsdigita.kernel.*;
object type Category extends ACSObject {
// Hier werden die Default-Werte gespeichert ???
String[0..1] description = cat_categories.description VARCHAR(4000);
String[1..1] name = cat_categories.name VARCHAR(200);
String[0..1] url = cat_categories.url VARCHAR(200);
Boolean[1..1] isEnabled = cat_categories.enabled_p CHAR(1);
Boolean[1..1] isAbstract = cat_categories.abstract_p CHAR(1);
String[1..1] defaultAncestors = cat_categories.default_ancestors VARCHAR(3209);
Boolean[1..1] ignoreParentIndexItem = cat_categories.ignore_parent_index_p CHAR(1);
@ -33,6 +36,28 @@ object type Category extends ACSObject {
reference key (cat_categories.category_id);
}
// locale dependend entries
object type CategoryLocalization extends ACSObject {
String[1..1] locale = cat_category_localizations.locale CHAR(2);
// Moved down from Category
String[0..1] description = cat_category_localizations.description VARCHAR(4000);
String[1..1] name = cat_category_localizations.name VARCHAR(200);
String[0..1] url = cat_category_localizations.url VARCHAR(200);
Boolean[1..1] isEnabled = cat_category_localizations.enabled_p CHAR(1);
reference key (cat_category_localizations.id);
}
association {
composite Category[1..1] category = join cat_category_localizations.category_id
to cat_categories.category_id;
component CategoryLocalization[0..n] localizations = join cat_categories.category_id
to cat_category_localizations.category_id;
}
object type UseContext {
BigDecimal[1..1] id = cat_root_cat_object_map.id;

View File

@ -0,0 +1,91 @@
/*
* CategorizationConfig.java
*
* Created on 17. Januar 2008, 15:29
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package com.arsdigita.categorization;
/**
*
* @author quasi
*/
import com.arsdigita.runtime.AbstractConfig;
import com.arsdigita.runtime.RuntimeConfig;
import com.arsdigita.util.Assert;
import com.arsdigita.util.parameter.BooleanParameter;
import com.arsdigita.util.parameter.StringParameter;
import com.arsdigita.util.parameter.ErrorList;
import com.arsdigita.util.parameter.IntegerParameter;
import com.arsdigita.util.parameter.Parameter;
import com.arsdigita.util.parameter.ParameterError;
import java.util.StringTokenizer;
import org.apache.log4j.Logger;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
/**
* Stores the configuration record for the Categorization functionality
*/
public final class CategorizationConfig extends AbstractConfig {
private static Logger s_log = Logger.getLogger(CategorizationConfig.class);
private final Parameter m_showInternalName;
private final Parameter m_supportedLanguages;
public CategorizationConfig() {
m_showInternalName = new BooleanParameter
("waf.categorization.show_internal_name",
Parameter.REQUIRED,
new Boolean(false));
m_supportedLanguages = new StringParameter
("waf.categorization.supported_languages",
Parameter.REQUIRED,
"en,de,fr");
register(m_showInternalName);
register(m_supportedLanguages);
loadInfo();
}
/**
* Returns the showInternalName flag.
*/
public final boolean getShowInternalName() {
return ((Boolean) get(m_showInternalName)).booleanValue();
}
/**
* Returns the defaultLanguage flag.
*/
public final String getDefaultLanguage() {
return ((String) get(m_supportedLanguages)).trim().substring(0, 2);
}
/**
* Returns the supportedLanguages as StringTokenizer.
*/
public final StringTokenizer getSupportedLanguages() {
return new StringTokenizer((String) get(m_supportedLanguages), ",", false);
}
/**
* Return true, if language lang is part of supported langs
*/
public final boolean hasLanguage(String lang) {
return ((String) get(m_supportedLanguages)).contains(lang);
}
}

View File

@ -0,0 +1,8 @@
waf.categorization.show_internal_name.title=Activate output of internal keys for categorization
waf.categorization.show_internal_name.purpose=Activate this setting to output internal keys for categories without the requested locale. This is usually for debugging.
waf.categorization.show_internal_name.example=false
waf.categorization.show_internal_name.format=[boolean]
waf.categorization.supported_languages.title=Set the supported languages for categorization
waf.categorization.supported_languages.purpose=Set the supported languages for categorization. First entry is the default language
waf.categorization.supported_languages.example=en,de,fr
waf.categorization.supported_languages.format=[string]

View File

@ -83,6 +83,24 @@ import org.apache.log4j.Logger;
*
* @author Randy Graebner
* @version $Revision: 1.1 $ $DateTime: $
*
* <p>Localization is done with some new classes, so the category tree is
* now multilanguage. This is completly transparent to the rest of the
* system (hopefully) and uses the negotiated language from the browser
* environment. The following attributes are localizable:
*
* <ul>
* <li>Name</li>
* <li>Description</li>
* <li>URL</li>
* <li>IsEnabled</li>
* </ul>
*
* To use localized URLs I had to change NavigationFileReolver.resolveCategory()
* in ccm-ldn-navigation to filter the categories in Java. There might be other
* location in the code where this patch may also be needed. So fix it.
*
* Quasimodo
*/
public class Category extends ACSObject {
private static final Logger s_log = Logger.getLogger(Category.class);
@ -103,6 +121,14 @@ public class Category extends ACSObject {
public static final PrivilegeDescriptor MAP_DESCRIPTOR =
new PrivilegeDescriptor("map_to_category");
// Quasimodo: Begin
private static CategorizationConfig s_config = new CategorizationConfig();
static {
s_config.load();
}
// Quasimodo: End
public static final String ROOT_CATEGORY = "rootCategory";
public static final String USE_CONTEXT = "useContext";
public static final String CATEGORY_OWNER = "categoryOwner";
@ -149,6 +175,8 @@ public class Category extends ACSObject {
public final static String CHILD_OBJECTS = "childObjects";
public final static String RELATED_CATEGORIES = RELATED;
public final static String CATEGORIES = "categories";
public static final String LOCALIZATIONS = "localizations";
// some named queries in the pdl files
private static final String CHILD_CATEGORY_IDS =
@ -158,6 +186,11 @@ public class Category extends ACSObject {
private HierarchyDenormalization m_hierarchy;
// Quasimodo: Begin
// Save the localized parts of category
private CategoryLocalizationCollection m_categoryLocalizationCollection;
// Quasimodo: End
protected String getBaseDataObjectType() {
return BASE_DATA_OBJECT_TYPE;
}
@ -316,6 +349,15 @@ public class Category extends ACSObject {
}
// Quasimodo: Begin
/**
* Retrieves the current configuration
*/
public static CategorizationConfig getConfig() {
return s_config;
}
/**
* @see com.arsdigita.domain.DomainObject#initialize()
*/
@ -334,21 +376,58 @@ public class Category extends ACSObject {
setAbstract(false);
//by default do not ignore the parent index item
setIgnoreParentIndexItem(false);
}
m_hierarchy = new HierarchyDenormalization
("com.arsdigita.categorization.updateCategoryDescendants", this,
DEFAULT_ANCESTORS) {};
}
m_categoryLocalizationCollection = new CategoryLocalizationCollection(this);
}
// Quasimodo: End
/**
* Quasimodo:
* Returns the localized name or the name key if localized version don't exist
*
* @return the category name.
*/
public String getName(String locale) {
// Test for localized version
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
// Return value of isEnabled from localized version, so categories could be disabled depending on locale
return m_categoryLocalizationCollection.getName();
} else {
// Return name key
return (String) get(NAME);
}
}
/**
* @return the category name.
*/
public String getName() {
return (String) get(NAME);
return getName(this.getNegotiatedLocale());
}
/**
* Returns the display name of the category. This overrides the parent
* implementation.
* @return the category name.
*/
public String getDisplayName(String locale) {
return getName(locale);
}
/**
* Returns the display name of the category. This overrides the parent
* implementation.
@ -462,17 +541,55 @@ public class Category extends ACSObject {
*
* @param value the new name of the category
*/
public void setName(String value) {
set(NAME, value);
public void setName(String name, String locale) {
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
m_categoryLocalizationCollection.getCategoryLocalization().setName(name);
}
}
/**
* Sets the name of the category.
*
* @param value the new name of the category
*/
public void setName(String name) {
set(NAME, name);
}
/**
* Returns the description of the category.
*
* Quasimodo:
* Returns localized version of description or description key if localized version don't exist
*
* @return the category description.
*/
public String getDescription(String locale) {
// Test for localized version
// HACK
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
// Return value of isEnabled from localized version, so categories could be disabled depending on locale
return m_categoryLocalizationCollection.getDescription();
} else {
// Return description key
return (String) get(DESCRIPTION);
}
}
/**
* Returns the description of the category.
* @return the category name.
*/
public String getDescription() {
return (String) get(DESCRIPTION);
return getDescription(this.getNegotiatedLocale());
}
/**
@ -487,10 +604,47 @@ public class Category extends ACSObject {
*
* @param value the new description of the category
*/
public void setDescription(String value) {
set(DESCRIPTION, value);
public void setDescription(String description, String locale) {
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
m_categoryLocalizationCollection.getCategoryLocalization().setDescription(description);
}
}
/**
* Sets the description of the category.
*
* @param value the new description of the category
*/
public void setDescription(String description) {
set(DESCRIPTION, description);
}
/**
* Returns the URL component of the category.
*
* Quasimodo:
* Returns the localized version of the URL or URL-key if localized version don't exist
*
* @return URL component used when browsing categories
*/
public String getURL(String locale) {
// Test for localized version
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
// Return value of isEnabled from localized version, so categories could be disabled depending on locale
return m_categoryLocalizationCollection.getURL();
} else {
// Return URL-key
return (String) get(URL);
}
}
/**
* Returns the URL component of the category.
@ -498,10 +652,24 @@ public class Category extends ACSObject {
* @return URL component used when browsing categories
*/
public String getURL() {
return (String) get(URL);
return getURL(this.getNegotiatedLocale());
}
/**
* Sets the URL component of the category.
*
* @param url URL component used when browsing categories
*/
public void setURL(String url, String locale) {
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
m_categoryLocalizationCollection.getCategoryLocalization().setURL(url);
}
}
/**
* Sets the URL component of the category.
*
@ -517,11 +685,64 @@ public class Category extends ACSObject {
*
* @return <code>true</code> if the category is enabled; <code>false</code>
* otherwise.
*
* Quasimodo:
* This is getting a bit more compliated:
* 1. Check if category is globally disabled
* 2. If not, check if localized version exists
* 2.1 If so, return isEnabled from localized version
* 2.2 If not, return Category.getConfig().getShowInternalName()
*
*/
public boolean isEnabled() {
return ((Boolean) get(IS_ENABLED)).booleanValue();
public boolean isEnabled(String locale) {
// If locale == "" return global status
// or if globally disabled, return category as disabled
if(locale == "" || ((Boolean) get(IS_ENABLED)).booleanValue() == false) {
return ((Boolean) get(IS_ENABLED)).booleanValue();
}
// Test for localized version
// HACK
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
// Return value of isEnabled from localized version, so categories could be disabled depending on locale
return m_categoryLocalizationCollection.isEnabled();
} else {
// Return value of Category.getConfig().getShowInternalName()
// This will disable all categories without selected locale, if Category.getConfig().getShowInternalName() == false
return Category.getConfig().getShowInternalName();
}
}
/**
* Determines the current state of the category.
*
* @return <code>true</code> if the category is enabled; <code>false</code>
* otherwise.
*/
public boolean isEnabled() {
return isEnabled(this.getNegotiatedLocale());
}
/**
* Sets whether the category is enabled.
*
* @param isEnabled <code>true</code> if the category is enabled;
* <code>false</false> otherwise.
*/
public void setEnabled(boolean isEnabled, String locale) {
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
m_categoryLocalizationCollection.getCategoryLocalization().setEnabled(isEnabled);
}
}
/**
* Sets whether the category is enabled.
@ -1706,6 +1927,11 @@ public class Category extends ACSObject {
* @return Array of constituent categories. The first element of the array
* is the current category (hence the array will always have length >=
* 1). If the path is bad, this returns <code>null</code>.
*
* This one may be patched to work with localized URLs. I didn't do it for now
* because I don't know where it is called and if it's really needed to patch.
* Quasimodo
*
*/
public Category[] getChildrenByURL(String path) {
final List children = new LinkedList();
@ -1971,4 +2197,103 @@ public class Category extends ACSObject {
(new PermissionDescriptor(PrivilegeDescriptor.ADMIN, this,
Kernel.getContext().getParty()));
}
// Quasimodo: Begin
/**
* Getting the negotiated locale from requestContext
* @return the negotiated language string if in supported lang list or default language else
*/
private String getNegotiatedLocale() {
String locale = null;
try {
// Try to get locale from request context
locale = com.arsdigita.dispatcher.DispatcherHelper.getRequestContext().getLocale().getLanguage();
} catch(NullPointerException ex) {
// If there is no request context (ex. during ccm setup) use default language
locale = Category.getConfig().getDefaultLanguage();
} finally {
// if supported lang contains locale
if(Category.getConfig().hasLanguage(locale)) {
// then return locale
return locale;
} else {
// else return default language
return Category.getConfig().getDefaultLanguage();
}
}
}
public CategoryLocalizationCollection getCategoryLocalizationCollection() {
return m_categoryLocalizationCollection;
}
public DataAssociation getLocalizations() {
return ((DataAssociation) this.get(LOCALIZATIONS));
}
public boolean hasLocalizations() {
return !m_categoryLocalizationCollection.isEmpty();
}
/**
* Add a new language set to this category
*/
public boolean addLanguage(String locale, String name, String description, String url) {
// If locale don't exist
if(locale != "" && m_categoryLocalizationCollection != null && !m_categoryLocalizationCollection.localizationExists(locale)) {
// Get DataAssociation
DataAssociation categoryLocalizationAssociation = this.getLocalizations();
// Add association with category
(new CategoryLocalization(locale, name, description, url)).addToAssociation(categoryLocalizationAssociation);
// Reload CategoryLocalizationCollection
// this.m_categoryLocalizationCollection = new CategoryLocalizationCollection(this);
return true;
}
return false;
}
/**
* Delete a language set from this category
*/
public boolean delLanguage(String locale) {
// If locale exist
if(locale != "" && m_categoryLocalizationCollection != null && m_categoryLocalizationCollection.localizationExists(locale)) {
// Get DataAssociation
DataAssociation categoryLocalizationAssociation = this.getLocalizations();
// Remove CategoryLocalization from Association
m_categoryLocalizationCollection.getCategoryLocalization().removeFromAssociation(categoryLocalizationAssociation);
// Reload CategoryLocalizationCollection
this.m_categoryLocalizationCollection = new CategoryLocalizationCollection(this);
return true;
}
return false;
}
}

View File

@ -0,0 +1,278 @@
/*
* CategoryLocalization.java
*
* Created on 17. Januar 2008, 14:36
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package com.arsdigita.categorization;
import com.arsdigita.kernel.ACSObject;
import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.OID;
import com.arsdigita.persistence.metadata.ObjectType;
import java.math.BigDecimal;
/**
*
* @author quasi
*/
public class CategoryLocalization extends ACSObject {
public static final String BASE_DATA_OBJECT_TYPE = "com.arsdigita.categorization.CategoryLocalization";
private static final String BASE_DATA_OBJECT_PACKAGE = "com.arsdigita.categorization";
// Constants to use in the code
public static final String LOCALE = "locale";
public static final String NAME = "name";
public static final String DESCRIPTION = "description";
public static final String URL = "url";
public static final String IS_ENABLED = "isEnabled";
protected String getBaseDataObjectType() {
return BASE_DATA_OBJECT_TYPE;
}
public static String getBaseDataObjectPackage() {
return BASE_DATA_OBJECT_PACKAGE;
}
/**
* Initializes the categoryLocalization with the specified data object.
*
* @param categoryObjectData the data object
**/
public CategoryLocalization(DataObject categoryLocalizationObjectData) {
super(categoryLocalizationObjectData);
}
/**
* Serves as a shortcut to {@link #CategoryLocalization(String)
* CategoryLocalization(CategoryLocalization.BASE_DATA_OBJECT_TYPE)}.
*
* @see com.arsdigita.domain.DomainObject#DomainObject(String)
**/
public CategoryLocalization() {
this(BASE_DATA_OBJECT_TYPE);
}
/**
* Initializes the contained data object with a new data object whose object
* type is specified by the passed in type name.
*
* @param typeName the object type for the contained data object
*
* @see com.arsdigita.domain.DomainObject#DomainObject(String)
* @see com.arsdigita.persistence.DataObject
* @see com.arsdigita.persistence.metadata.ObjectType
**/
public CategoryLocalization(String typeName) {
super(typeName);
}
/**
* Initializes the contained data object with a new data object whose object
* type is the specified type.
*
* @param type the object type for the contained data object
*
* @see com.arsdigita.domain.DomainObject#DomainObject(ObjectType)
* @see com.arsdigita.persistence.DataObject
**/
public CategoryLocalization(ObjectType type) {
super(type);
}
/**
* Retrieves the data object with the specified OID from the persistent
* storage mechanism.
*
* @param oid the OID for the data object to retrieve
* @throws DataObjectNotFoundException if this OID is invalid or has been
* deleted.
*
* @see com.arsdigita.domain.DomainObject#DomainObject(OID)
* @see com.arsdigita.persistence.DataObject
**/
public CategoryLocalization(OID oid) {
super(oid);
}
/**
* Retrieves the data object with the specified ID from the persistence
* storage mechanism. This method is just a wrapper for the {@link
* #CategoryLocalization(OID)} constructor.
*
* @throws DataObjectNotFoundException
*/
public CategoryLocalization(BigDecimal id) {
this(new OID(BASE_DATA_OBJECT_TYPE, id));
}
/**
* Creates a new categoryLocalization with the given name and description.
*
* @param name the name for the new category
* @param description the description for the new category
*/
public CategoryLocalization(String locale, String name, String description) {
this();
setLocale(locale);
setName(name);
setDescription(description);
}
/**
* Creates a new categoryLocalization with the given name, description and URL
* component.
*
* @param name the name for the new category
* @param description the description for the new category
* @param url URL component used when browsing categories.
*/
public CategoryLocalization(String locale, String name, String description, String url) {
this();
setLocale(locale);
setName(name);
setDescription(description);
setURL (url);
}
/**
* Retrieves the categoryLocalization with the given category ID, and sets the name and
* description. For the new name and descrption to be permanent, the caller
* must call the save() method.
*
* @param categoryID the category ID
* @param name the category name
* @param description the category description
* @exception DataObjectNotFoundException if this OID is
* invalid or has been deleted.
*
* @see com.arsdigita.domain.DomainObject#DomainObject(OID)
*/
public CategoryLocalization(OID categoryID, String name, String description) {
this(categoryID);
setName(name);
setDescription(description);
}
/**
* Retrieves the categoryLocalization with the given category ID, and sets the name and
* description. For the new name and descrption to be permanent, the caller
* must call the save() method.
*
* @param categoryID the category ID
* @param name the category name
* @param description the category description
* @param url URL component used when browsing categories.
* @exception DataObjectNotFoundException if this OID is
* invalid or has been deleted.
*
* @see com.arsdigita.domain.DomainObject#DomainObject(OID)
*/
public CategoryLocalization(OID categoryID, String name, String description, String url) {
this(categoryID);
setName(name);
setDescription(description);
setURL (url);
}
/**
* @see com.arsdigita.domain.DomainObject#initialize()
*/
protected void initialize() {
super.initialize();
if(isNew()) {
setEnabled(true);
}
}
// Getter / Setter methods
/**
* Returns the locale
*/
public String getLocale() {
return (String) get(LOCALE);
}
/**
* Sets the locale
*/
private void setLocale(String locale) {
set(LOCALE, locale);
}
/**
* Returns the localized name
*/
public String getName() {
return (String) get(NAME);
}
/**
* Set localized name
*/
public void setName(String name) {
set(NAME, name);
}
/**
* Returns the localized description
*/
public String getDescription() {
return (String) get(DESCRIPTION);
}
/**
* Set localized description
*/
public void setDescription(String description) {
set(DESCRIPTION, description);
}
/**
* Returns the localized URL
*/
public String getURL() {
return (String) get(URL);
}
/**
* Set localized URL
*/
public void setURL(String url) {
set(URL, url);
}
/**
* Returns the localized status
*/
public boolean isEnabled() {
return ((Boolean) get(IS_ENABLED)).booleanValue();
}
/**
* Set localized status
*/
public void setEnabled(boolean isEnabled) {
set(IS_ENABLED, new Boolean(isEnabled));
}
}

View File

@ -0,0 +1,140 @@
/*
* CategoryLocalizationCollection.java
*
* Created on 19. Januar 2008, 13:24
*
* Author: Quasimodo
*/
package com.arsdigita.categorization;
import com.arsdigita.kernel.ACSObject;
import com.arsdigita.kernel.ACSObjectCollection;
import com.arsdigita.persistence.DataAssociation;
import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.DataObject;
/**
* Represents a collection of categoryLocalizations.
*
* <p>Instances of this class are produced by various methods in {@link
* Category} and other classes. See, for example, {@link Category#getChildren()}
* or {@link Category#getDescendants()}.</p>
*
* @author Randy Graebner (randyg@alum.mit.edu)
* @version $Revision: #15 $ $DateTime: 2004/08/16 18:10:38 $
**/
public class CategoryLocalizationCollection extends ACSObjectCollection {
public CategoryLocalizationCollection(Category category) {
super(category.getLocalizations().getDataCollection());
}
public CategoryLocalizationCollection(DataCollection dataCollection) {
super(dataCollection);
}
/**
* Returns the locale of the categoryLocalization.
*
* @return the category locale.
* @see Category#getLocale()
*/
public String getLocale() {
return getCategoryLocalization().getLocale();
}
/**
* Returns the name of the category.
*
* @return the category name.
* @see Category#getName()
*/
public String getName() {
return getCategoryLocalization().getName();
}
/**
* Returns the description.
*
* @return the description
* @see Category#getDescription()
*/
public String getDescription() {
return getCategoryLocalization().getDescription();
}
/**
* Returns the URL.
*
* @return the URL
* @see Category#getURL()
*/
public String getURL() {
return getCategoryLocalization().getURL();
}
/**
* Determines the current state of the category.
*
* @return <code>true</code> if the category is enabled; <code>false</code>
* otherwise.
* @see Category#isEnabled()
*/
public boolean isEnabled() {
return getCategoryLocalization().isEnabled();
}
/**
* Wrapper to <code>getDomainObject()</code> that casts the returned
* <code>DomainObject</code> as a <code>CategoryLocalization</code>.
*
* @return a <code>CategoryLocalization</code> for the current position in the
* collection.
**/
public CategoryLocalization getCategoryLocalization() {
return (CategoryLocalization) getDomainObject();
}
public ACSObject getACSObject() {
return getCategoryLocalization();
}
/**
* Search for the requested localization in the Collection
*
* @return result of the search. If true, the CollectionCursor is set to the position of the requested locale.
*/
public boolean localizationExists(String locale) {
//
if(!m_dataCollection.isEmpty() && locale != "") {
// First check, if we are already at the right position. This will speed up repeated access for the same locale
if(this.getPosition() > 0 && this.getCategoryLocalization().getLocale().equals(locale)) return true;
// Nope, so we have to start a search
this.rewind();
while(this.next()) {
if(this.getCategoryLocalization().getLocale().equals(locale)) return true;
}
}
// Not found
return false;
}
/**
* Sorts the category collection by the category sort key.
*
* @see CategorizedCollection#sort(boolean)
**/
public final void sort(boolean ascending) {
if ( ascending ) {
addOrder("link.sortKey asc");
} else {
addOrder("link.sortKey desc");
}
}
}

View File

@ -16,6 +16,10 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Diese Klasse ist für die Verabeitung der URLs innerhalb der Anwendung navigation zuständig.
*/
package com.arsdigita.london.navigation;
@ -315,7 +319,20 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
return NavigationFileResolver.resolveCategory(root, path);
}
/**
* In dieser Methode wird eine URL mit dem Kategorienbaum abgeglichen
* und die angeforderte Kategorie zurückgegeben, soweit sie vorhanden
* ist.
*
* Ursprünglich hat diese Methode addEqualsFilter verwendet, um die
* passenden Kategorien direkt in der SQL-Abfrage zu filtern. Das ist
* aber mit den lokalisierten URL der neuen, lokalisierten Kategorien
* nicht mehr möglich - oder zumindest habe ich keinen Weg gefunden.
* Stattdessen wird die Filterung nun in Java vorgenommen.
*
* Quasimodo
*/
public static Category[] resolveCategory(Category root,
String path) {
String[] bits = StringUtils.split(path, '/');
@ -333,16 +350,24 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
}
CategoryCollection children = cat.getChildren();
children.addEqualsFilter(Category.URL, bits[i]);
children.addEqualsFilter(Category.IS_ENABLED, Boolean.TRUE);
if (children.next()) {
// children.addEqualsFilter(Category.URL, bits[i]);
// children.addEqualsFilter(Category.IS_ENABLED, Boolean.TRUE);
// if (children.next()) {
boolean found = false;
while (children.next()) {
cat = children.getCategory();
if (s_log.isDebugEnabled()) {
s_log.debug("Got category " + cat);
if(cat.getURL().equals(bits[i]) && cat.isEnabled() == true) {
if (s_log.isDebugEnabled()) {
s_log.debug("Got category " + cat);
}
cats.add(cat);
children.close();
found = true;
break;
}
cats.add(cat);
children.close();
} else {
}
// } else {
if(found == false) {
if (s_log.isDebugEnabled()) {
s_log.debug("No category found ");
}