added the possibility to search for articles by topic
authorzapata <zapata>
Sun, 25 Dec 2005 21:38:45 +0000 (21:38 +0000)
committerzapata <zapata>
Sun, 25 Dec 2005 21:38:45 +0000 (21:38 +0000)
bundles/admin_en.properties
source/mir/entity/adapter/EntityAdapterEngine.java
source/mir/servlet/AdminServletModule.java
source/mircoders/localizer/basic/MirBasicProducerAssistantLocalizer.java
source/mircoders/servlet/ServletModuleAdmin.java
source/mircoders/servlet/ServletModuleContent.java
templates/admin/FUNCTIONS.template

index bb55f11..d2f2f87 100755 (executable)
@@ -1,6 +1,6 @@
 ########## admin ##########
 # language: english
-# $Id: admin_en.properties,v 1.48.2.26 2005/11/09 14:08:56 zapata Exp $
+# $Id: admin_en.properties,v 1.48.2.27 2005/12/25 21:38:45 zapata Exp $
 
 languagename=English
 
@@ -236,6 +236,7 @@ content.operation.newswire=newswire
 
 content.preview.default = Preview
 
+contentsearch.topic = Topic
 contentsearch.value = search
 contentsearch.field = field
 contentsearch.field.title = Title
index 04e9989..ea29caf 100755 (executable)
  */
 package mir.entity.adapter;
 
-import java.util.List;
-
 import mir.entity.EntityBrowser;
 import mir.storage.DatabaseFailure;
 
+import java.util.List;
+
 public class EntityAdapterEngine {
   private EntityAdapterEngine() {
   }
 
-  public static List retrieveAdapterList(EntityAdapterModel aModel, String aDefinition, String aQualifier, String anOrder, int aLimit, int anOffset) {
+  public static List retrieveAdapterList(EntityAdapterModel aModel, String aDefinition,
+                                         String aQualifier, String anOrder, int aLimit, int anOffset) {
+    return retrieveAdapterList(aModel, aDefinition, "", null, aQualifier, anOrder, aLimit, anOffset);
+  }
+
+  public static List retrieveAdapterList(EntityAdapterModel aModel, String aDefinition, String aMainTablePrefix, List someExtraTables,
+                                         String aQualifier, String anOrder, int aLimit, int anOffset) {
     try {
-      EntityBrowser browser = new EntityBrowser(aModel.getMappingForName(aDefinition).getDatabase(), aQualifier, anOrder, 30, aLimit, anOffset);
+      EntityBrowser browser = new EntityBrowser(aModel.getMappingForName(aDefinition).getDatabase(),
+          aMainTablePrefix, someExtraTables, aQualifier, anOrder, 30, aLimit, anOffset);
 
       return new EntityListAdapter(aModel, aDefinition, browser, aLimit);
     }
index 4c79040..60389f4 100755 (executable)
@@ -36,6 +36,7 @@ import mir.module.ModuleExc;
 import mir.storage.Database;
 import mir.util.HTTPRequestParser;
 import mir.util.URLBuilder;
+import mir.util.StringRoutines;
 import mircoders.global.MirGlobal;
 import mircoders.localizer.MirLocalizerExc;
 import mircoders.servlet.ServletHelper;
@@ -130,8 +131,10 @@ public abstract class AdminServletModule extends ServletModule {
     String where = requestParser.getParameter("where");
     String order = requestParser.getParameterWithDefault("order", getDefaultListOrdering());
     int offset = requestParser.getIntegerWithDefault("offset", 0);
+    String mainTablePrefix = requestParser.getParameter("mainTablePrefix");
+    String extraTables = requestParser.getParameter("extraTables");
 
-    returnList(aRequest, aResponse, where, order, offset);
+    returnList(aRequest, aResponse, where, order, offset, Collections.EMPTY_MAP, mainTablePrefix, extraTables);
   }
 
 
@@ -143,14 +146,24 @@ public abstract class AdminServletModule extends ServletModule {
   public void returnList(HttpServletRequest aRequest, HttpServletResponse aResponse,
                          String aWhereClause, String anOrderByClause, int anOffset,
                          Map anOverridingRequestParameters) throws ServletModuleExc {
+    returnList(aRequest, aResponse, aWhereClause, anOrderByClause, anOffset, anOverridingRequestParameters, "", null);
+  }
+  public void returnList(HttpServletRequest aRequest, HttpServletResponse aResponse,
+                         String aWhereClause, String anOrderByClause, int anOffset,
+                         Map anOverridingRequestParameters, String aMainTablePrefix, String someExtraTables) throws ServletModuleExc {
     HTTPRequestParser requestParser = new HTTPRequestParser(aRequest, anOverridingRequestParameters);
     URLBuilder urlBuilder = new URLBuilder();
 
     try {
       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, getLocales(aRequest));
 
+      List extraTables = null;
+      if (someExtraTables!=null && someExtraTables.length() > 0) {
+        extraTables = StringRoutines.splitString(someExtraTables, ",");
+      }
+
       List list =
-         EntityAdapterEngine.retrieveAdapterList(model, definition, aWhereClause, anOrderByClause, nrEntitiesPerListPage, anOffset);
+         EntityAdapterEngine.retrieveAdapterList(model, definition, aMainTablePrefix, extraTables, aWhereClause, anOrderByClause, nrEntitiesPerListPage, anOffset);
 
       responseData.put("nexturl", null);
       responseData.put("prevurl", null);
@@ -160,6 +173,8 @@ public abstract class AdminServletModule extends ServletModule {
       urlBuilder.setValue("do", "list");
       urlBuilder.setValue("where", aWhereClause);
       urlBuilder.setValue("order", anOrderByClause);
+      urlBuilder.setValue("extraTables", someExtraTables);
+      urlBuilder.setValue("mainTablePrefix", aMainTablePrefix);
 
 
       urlBuilder.setValue("offset", anOffset);
index 6d31d17..db4bdf6 100755 (executable)
@@ -72,7 +72,7 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
   private RE regularExpressionWhitespace;
 
   public MirBasicProducerAssistantLocalizer() throws MirLocalizerFailure {
-    try{
+    try {
       regularExpressionLT = new RE("<");
       regularExpressionGT = new RE(">");
       regularExpressionWhitespace = new RE("\\s+");
@@ -82,7 +82,7 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
     }
   }
 
-  public void initializeGenerationValueSet(Map aValueSet) throws MirLocalizerExc, MirLocalizerFailure  {
+  public void initializeGenerationValueSet(Map aValueSet) throws MirLocalizerExc, MirLocalizerFailure {
     try {
       Iterator i;
 
@@ -110,13 +110,13 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
 
       aValueSet.put("config", configMap);
 
-      aValueSet.put("utility", new Utility()); 
+      aValueSet.put("utility", new Utility());
 
       aValueSet.put("languages",
-        new EntityIteratorAdapter("", "", 20, MirGlobal.localizer().dataModel().adapterModel(), "language"));
+          new EntityIteratorAdapter("", "", 20, MirGlobal.localizer().dataModel().adapterModel(), "language"));
 
       aValueSet.put("topics",
-        new EntityIteratorAdapter("", "", 20, MirGlobal.localizer().dataModel().adapterModel(), "topic"));
+          new EntityIteratorAdapter("", "", 20, MirGlobal.localizer().dataModel().adapterModel(), "topic"));
 
       Map articleTypeMap = new HashMap();
       articleTypeMap.put("openposting", "0");
@@ -150,7 +150,7 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
     }
 
   }
-  
+
   public static class getLanguageIdFunction implements Generator.Function {
     private Map languageCodeToId;
     private String otherLanguageId;
@@ -180,12 +180,14 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
 
     public Object perform(List aParameters) throws GeneratorExc, GeneratorFailure {
       try {
-        if (aParameters.size() != 1)
+        if (aParameters.size() != 1) {
           throw new GeneratorExc("getLanguageIdFunction: 1 parameter expected: language-code");
+        }
 
         String result = (String) languageCodeToId.get(aParameters.get(0));
-        if (result == null)
+        if (result == null) {
           result = otherLanguageId;
+        }
 
         return result;
       }
@@ -205,11 +207,11 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
     try {
       String result =
           StringUtil.createHTML(
-          StringUtil.removeHTMLTags(aText),
-          MirGlobal.config().getString("Producer.ImageRoot"),
-          MirGlobal.config().getString("Producer.MailLinkName"),
-          MirGlobal.config().getString("Producer.ExtLinkName"),
-          MirGlobal.config().getString("Producer.IntLinkName"));
+              StringUtil.removeHTMLTags(aText),
+              MirGlobal.config().getString("Producer.ImageRoot"),
+              MirGlobal.config().getString("Producer.MailLinkName"),
+              MirGlobal.config().getString("Producer.ExtLinkName"),
+              MirGlobal.config().getString("Producer.IntLinkName"));
       logger.debug("done filtering non-HTML text ");
       return result;
     }
@@ -291,53 +293,81 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
   }
 
 
-  private String[] badAttributeValuePrefixes= {"javascript","vbscript","about","wysiwyg","data","view-source","ms-its","mhtml","shell","lynxexec","lynxcgi","hcp","ms-help","help","disk","vnd.ms.radio","opera","res","resource","chrome","mocha","livescript"};
-
-  private String[] badAttributes = {"onabort", "onblur",  "onchange", "onclick", "ondblclick", "onerror", "onfocus", "onkeydown", "onKeypress", "onkeyup", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onreset", "onselect", "onsubmit", "onunload","onload","onclick","onfocus","onblur","FSCommand","onAbort","onActivate","onAfterPrint","onAfterUpdate","onBeforeActivate","onBeforeCopy","onBeforeCut","onBeforeDeactivate","onBeforeEditFocus","onBeforePaste","onBeforePrint","onBeforeUnload","onBegin","onBlur","onBounce","onCellChange","onChange","onClick","onContextMenu","onControlSelect","onCopy","onCut","onDataAvailible","onDataSetChanged","onDataSetComplete","onDblClick","onDeactivate","onDrag","onDragEnd","onDragLeave","onDragEnter","onDragOver","onDragDrop","onDrop","onEnd","onError","onErrorUpdate","onExit","onFilterChange","onFinish","onFocus","onFocusIn","onFocusOut","onHelp","onKeyDown","onKeyPress","onKeyUp","onLayoutComplete","onLoad","onLoseCapture","onMediaComplete","onMediaError","onMouseDown","onMouseEnter","onMouseLeave","onMouseMove","onMouseOut","onMouseOver","onMouseUp","onMouseWheel","onMove","onMoveEnd","onMoveStart","onOutOfSync","onPaste","onPause","onProgress","onPropertyChange","onReadyStateChange","onRepeat","onReset","onResize","onResizeEnd","onResizeStart","onResume","onReverse","onRowEnter","onRowExit","onRowDelete","onRowInserted","onScroll","onSeek","onSelect","onSelectionChange","onSelectStart","onStart","onStop","onSynchRestored","onSubmit","onTimeError","onTrackChange","onUnload","onURLFlip","seekSegmentTime","style","height","width"};
-  
-  private boolean isBadAttr(String attrName){
-    for (int i=0;i<badAttributes.length;i++){
-      if (badAttributes[i].toLowerCase().equals(attrName.toLowerCase()))
-       return true;
+  private String[] badAttributeValuePrefixes = {
+      "javascript", "vbscript", "about", "wysiwyg", "data", "view-source",
+      "ms-its", "mhtml", "shell", "lynxexec", "lynxcgi", "hcp", "ms-help",
+      "help", "disk", "vnd.ms.radio", "opera", "res", "resource", "chrome",
+      "mocha", "livescript"};
+
+
+  private String[] badAttributes = {
+      "onabort", "onblur", "onchange", "onclick", "ondblclick", "onerror",
+      "onfocus", "onkeydown", "onKeypress", "onkeyup", "onload", "onmousedown",
+      "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onreset",
+      "onselect", "onsubmit", "onunload", "onload", "onclick", "onfocus",
+      "onblur", "FSCommand", "onAbort", "onActivate", "onAfterPrint",
+      "onAfterUpdate", "onBeforeActivate", "onBeforeCopy", "onBeforeCut",
+      "onBeforeDeactivate", "onBeforeEditFocus", "onBeforePaste",
+      "onBeforePrint", "onBeforeUnload", "onBegin", "onBlur", "onBounce",
+      "onCellChange", "onChange", "onClick", "onContextMenu", "onControlSelect",
+      "onCopy", "onCut", "onDataAvailible", "onDataSetChanged", "onDataSetComplete",
+      "onDblClick", "onDeactivate", "onDrag", "onDragEnd", "onDragLeave", "onDragEnter",
+      "onDragOver", "onDragDrop", "onDrop", "onEnd", "onError", "onErrorUpdate", "onExit",
+      "onFilterChange", "onFinish", "onFocus", "onFocusIn", "onFocusOut", "onHelp",
+      "onKeyDown", "onKeyPress", "onKeyUp", "onLayoutComplete", "onLoad", "onLoseCapture",
+      "onMediaComplete", "onMediaError", "onMouseDown", "onMouseEnter", "onMouseLeave",
+      "onMouseMove", "onMouseOut", "onMouseOver", "onMouseUp", "onMouseWheel", "onMove",
+      "onMoveEnd", "onMoveStart", "onOutOfSync", "onPaste", "onPause", "onProgress",
+      "onPropertyChange", "onReadyStateChange", "onRepeat", "onReset", "onResize",
+      "onResizeEnd", "onResizeStart", "onResume", "onReverse", "onRowEnter", "onRowExit",
+      "onRowDelete", "onRowInserted", "onScroll", "onSeek", "onSelect", "onSelectionChange",
+      "onSelectStart", "onStart", "onStop", "onSynchRestored", "onSubmit", "onTimeError",
+      "onTrackChange", "onUnload", "onURLFlip", "seekSegmentTime", "style", "height", "width"};
+
+  private boolean isBadAttr(String attrName) {
+    for (int i = 0; i < badAttributes.length; i++) {
+      if (badAttributes[i].toLowerCase().equals(attrName.toLowerCase())) {
+        return true;
       }
+    }
     return false;
   }
 
-  private String stripWhitespace(String aString){
-    try{
+  private String stripWhitespace(String aString) {
+    try {
       return regularExpressionWhitespace.substituteAll(aString, "");
-     }
-    catch (Throwable t){
+    }
+    catch (Throwable t) {
       return "";
     }
   }
 
   private boolean checkAttr(String attrName) {
-    if (isBadAttr(attrName)){
-       return false;
+    if (isBadAttr(attrName)) {
+      return false;
     }
     return true;
 
   }
 
   private boolean checkAttrValue(String attrValue) {
-    for (int i=0;i<badAttributeValuePrefixes.length;i++){
-      if ((stripWhitespace(attrValue.toLowerCase())).startsWith(badAttributeValuePrefixes[i].toLowerCase()+":")){
-       return false;
-      } 
+    for (int i = 0; i < badAttributeValuePrefixes.length; i++) {
+      if ((stripWhitespace(attrValue.toLowerCase())).startsWith(badAttributeValuePrefixes[i].toLowerCase() + ":")) {
+        return false;
+      }
     }
     return true;
   }
 
 
   private boolean checkNode(String nodeName) {
-    List languages =  StringRoutines.splitString(MirGlobal.config().getString("Localizer.HTML.Whitelist"), ";");
-    
+    List languages = StringRoutines.splitString(MirGlobal.config().getString("Localizer.HTML.Whitelist"), ";");
+
     Iterator i = languages.iterator();
     while (i.hasNext()) {
-      if (nodeName.equals(i.next()))
+      if (nodeName.equals(i.next())) {
         return true;
+      }
     }
     return false;
   }
@@ -371,13 +401,13 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
               out.write(' ');
               out.write(attrs.item(i).getNodeName());
               out.write("=\"");
-             
+
               out.write(attrs.item(i).getNodeValue());
               out.write('"');
             }
           }
 
-          if (node.getChildNodes()==null || node.getChildNodes().getLength()==0) {
+          if (node.getChildNodes() == null || node.getChildNodes().getLength() == 0) {
             out.write("/");
           }
           out.write('>');
@@ -392,21 +422,21 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
         break;
 
       case Node.TEXT_NODE:
-       String value=node.getNodeValue();
-       try{
-         value=regularExpressionLT.substituteAll(value, "&lt;");
-         value=regularExpressionGT.substituteAll(value, "&gt;");
-       }
-       catch (Throwable t){
-         value="";
-       }
-       out.write(value);
+        String value = node.getNodeValue();
+        try {
+          value = regularExpressionLT.substituteAll(value, "&lt;");
+          value = regularExpressionGT.substituteAll(value, "&gt;");
+        }
+        catch (Throwable t) {
+          value = "";
+        }
+        out.write(value);
 
         break;
 
     }
 
-    if (type == Node.ELEMENT_NODE && canOutput && node.getChildNodes()!=null && node.getChildNodes().getLength()>0) {
+    if (type == Node.ELEMENT_NODE && canOutput && node.getChildNodes() != null && node.getChildNodes().getLength() > 0) {
       out.write("</");
       out.write(node.getNodeName());
       out.write('>');
@@ -416,13 +446,13 @@ public class MirBasicProducerAssistantLocalizer implements MirProducerAssistantL
   }
 
   public static class Utility extends ReflectionGeneratorFunctionsAdapter {
-    public Utility () {
+    public Utility() {
       super(new MirBasicUtilityFunctions());
     }
 
     public Object getDatetime() {
       return new GeneratorDateTimeFunctions.DateTimeFunctions(
-        MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone"));
+          MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone"));
     }
 
     public Object getCompressWhitespace() {
index 737bc01..aa1d879 100755 (executable)
@@ -125,6 +125,7 @@ public class ServletModuleAdmin extends AdminServletModule {
       templateData.put("searchispublished", null);
       templateData.put("searcharticletype", null);
       templateData.put("searchorder", null);
+      templateData.put("searchtopic", null);
       templateData.put("selectarticleurl", null);
       templateData.put("recipes", MirGlobal.localizer().producers().getRecipeNames());
 
index 563fde8..39b6b0f 100755 (executable)
@@ -70,12 +70,15 @@ public class ServletModuleContent extends AdminServletModule {
 
   public ServletModuleContent() {
     addPropagatedParameter("selectarticleurl");
+    addPropagatedParameter("searchtopic");
+
     definition = "content";
     mainModule = contentModule;
   }
 
   public void search(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletModuleExc, ServletModuleFailure {
     try {
+      String extraTables = "";
       HTTPRequestParser requestParser = new HTTPRequestParser(aRequest);
       SQLQueryBuilder queryBuilder = new SQLQueryBuilder();
       String searchField = requestParser.getParameterWithDefault("searchfield", "");
@@ -83,6 +86,7 @@ public class ServletModuleContent extends AdminServletModule {
       String searchOrder = requestParser.getParameterWithDefault("searchorder", "");
       String searchispublished = requestParser.getParameterWithDefault("searchispublished", "");
       String searchArticleType = requestParser.getParameterWithDefault("searcharticletype", "");
+      String searchTopic = requestParser.getParameterWithDefault("searchtopic", "");
 
       if (searchValue.length()>0) {
         if ("id".equals(searchField)) {
@@ -129,7 +133,15 @@ public class ServletModuleContent extends AdminServletModule {
         }
       }
 
-      returnList(aRequest, aResponse, queryBuilder.getWhereClause(), queryBuilder.getOrderByClause(), 0);
+      if (searchTopic.length() > 0) {
+        queryBuilder.appendAndCondition("cxt.content_id = id");
+        queryBuilder.appendAndCondition("cxt.topic_id = " + JDBCStringRoutines.escapeStringLiteral(searchTopic));
+
+        extraTables = "content_x_topic cxt";
+      }
+
+      returnList(aRequest, aResponse, queryBuilder.getWhereClause(), queryBuilder.getOrderByClause(), 0,
+          Collections.EMPTY_MAP, null, extraTables);
     }
     catch (Throwable e) {
       throw new ServletModuleFailure(e);
index 759ad87..a5e957a 100755 (executable)
@@ -15,7 +15,7 @@
   <call showGETButton(anUrl, aLabel)>
 <comment>
   <form method="POST" action="${utility.encodeHTML(config.actionRoot+"?"+anUrl)}"><input type="submit" class="majorbutton" value="${aLabel}"></form>
-</comment>  
+</comment>
 </function>
 
 
     <tr>
       <list aHeaders as i>
         <td class="${TABLE_HEAD_CLASS}">
-          <b>${i}</b> 
+          <b>${i}</b>
         </td>
       </list>
       <if aShowDelete == "1" || aShowEdit == "1">
         <td class="${TABLE_HEAD_CLASS}">
           &nbsp;
-        </td> 
+        </td>
       </if>
     </tr>
     <assign alternative="0">
-    
+
     <list anEntityList as entry>
       <if alternative=="0">
         <assign alternative="1">
@@ -56,8 +56,8 @@
         <assign alternative="0">
         <tr class="${LIST_ROW_CLASS_ALTERNATIVE}">
       </if>
-        
-        <list aDataKeys as i>   
+
+        <list aDataKeys as i>
             <td>
               ${utility.prettyEncodeHTML(entry[i])}
             </td>
@@ -68,7 +68,7 @@
             <a class="listcommand" href="${config.actionRoot}?module=${aModuleName}&amp;do=delete&amp;id=${entry.id}&amp;okurl=${utility.encodeURI(thisurl)}&amp;cancelurl=${utility.encodeURI(thisurl)}">${lang("delete")}</a>
             </if>
             <if aShowDelete == "1" && aShowEdit == "1">
-            | 
+            |
             </if>
             <if aShowEdit == "1">
               <a class="listcommand" href="${config.actionRoot}?module=${aModuleName}&do=edit&id=${entry.id}">${lang("edit")}</a>
         </if>
       </tr>
     </list>
-    
-    
-    <tr>    
+
+
+    <tr>
       <td colspan="${nrColumns}" class="${TABLE_FOOT_CLASS}">
         ${aCount} ${lang("records")} / ${lang("show_from_to", aFrom, aTo)}
       </td>
     </tr>
-  
+
   </table>
 </function>
 
         </list>
      </td>
    </tr>
-</function>   
+</function>
 
 <function PulldownTableRow (label, fieldname, entrieslist, keyfield, valuefield, value, langprefix)>
   <tr>
        </select>
      </td>
    </tr>
-</function>   
+</function>
 
 <function ReadonlyTextTableRowNormal (label, fieldcontent)>
   <call ReadonlyTextTableRow(label, fieldcontent, "listrow2")>
-</function>  
+</function>
 
 
 <function _TextInputTableRow(label, size, maxlength, fieldname, fieldcontent, aCanEdit)>
     <td align="right" class="table-left">
       <b>${label}:</b>
     </td>
-    
+
     <td class="listrow2">
       <input type="text" size="${size}" maxlength="${maxlength}" name="${fieldname}" value="${utility.encodeHTML(fieldcontent)}">
     </td>
     <td align="right" class="table-left">
       <b>${label}:</b>
     </td>
-    
+
     <td class="${class}">
       ${utility.encodeHTML(fieldcontent)}
     </td>
     <td align="right" class="table-left">
       <b>${label}:</b>
     </td>
-    
+
     <td class="listrow2">
       <input type="password" size="${size}" maxlength="${maxlength}" name="${fieldname}" value="${utility.encodeHTML(fieldcontent)}">
     </td>
     <td align="right" class="table-left-light">
       <b>${label}:</b>
     </td>
-    
+
     <td class="listrow1">
       <input type="text" size="${size}" maxlength="${maxlength}" name="${fieldname}" value="${utility.encodeHTML(fieldcontent)}">
     </td>
 
 <function CheckboxTableRow (label, fieldname, fieldcontent )>
   <call EditCheckboxNormal(label, fieldname, fieldcontent)>
-</function> 
+</function>
 
 <function _CheckboxTableRow (label, fieldname, fieldcontent, aCanEdit)>
   <if aCanEdit=="1">
   <else>
     <call ReadOnlyCheckboxNormal(label, fieldcontent)>
   </if>
-</function> 
+</function>
 
 <function _TextAreaTableRow (label, hint, cols, rows, fieldname, fieldcontent, aCanEdit)>
   <if aCanEdit=="1">
       <b>${label}:</b>
       <if hint>
         <br>
-        <span class="small">${hint}</span>      
+        <span class="small">${hint}</span>
       </if>
     </td>
-    
+
     <td class="listrow2">
       ${utility.prettyEncodeHTML(fieldcontent)}
     </td>
       <b>${label}:</b>
       <if hint>
         <br>
-        <span class="small">${hint}</span>      
+        <span class="small">${hint}</span>
       </if>
     </td>
-    
+
     <td class="listrow2">
       <textarea cols="${cols}" rows="${rows}" name="${fieldname}" wrap="virtual">${utility.encodeHTML(fieldcontent)}</textarea>
     </td>
       <b>${label}:</b>
       <if hint>
         <br>
-        <span class="small">${hint}</span>      
+        <span class="small">${hint}</span>
       </if>
     </td>
-    
+
     <td class="listrow1">
       <textarea cols="${cols}" rows="${rows}" name="${fieldname}" wrap="virtual">${utility.encodeHTML(fieldcontent)}</textarea>
     </td>
         <if selectarticleurl>
           <input type="hidden" name="selectarticleurl" value="${utility.encodeHTML(selectarticleurl)}">
         </if>
-         
+
         <table border="0" cellpadding="2" cellspacing="3">
           <tr <if !aLayout>class="bg-neutral"</if>>
             <td <if aLayout>class="table-head"<else>class="small bg-neutral"</if> valign="bottom"> ${lang("contentsearch.value")}</td>
             <td <if aLayout>class="table-head"<else>class="small bg-neutral"</if> valign="bottom"> ${lang("contentsearch.field")}</td>
             <td <if aLayout>class="table-head"<else>class="small bg-neutral"</if> valign="bottom"> ${lang("contentsearch.publishedstate")}</td>
             <td <if aLayout>class="table-head"<else>class="small bg-neutral"</if> valign="bottom"> ${lang("contentsearch.articletype")}</td>
+            <td <if aLayout>class="table-head"<else>class="small bg-neutral"</if> valign="bottom"> ${lang("contentsearch.topic")}</td>
             <td <if aLayout>class="table-head"<else>class="small bg-neutral"</if> valign="bottom"> ${lang("contentsearch.order")}</td>
       <td <if aLayout>class="table_head"<else>class="small bg_neutral"</if> valign="bottom"> &nbsp;</td>
           </tr>
                 </select>
             </td>
             <td <if aLayout>class="listrow2"</if>>
+                <select name="searchtopic">
+                  <option value="">--</option>
+                  <list topics as t>
+                    <option value="${t.id}" <if searchtopic && searchtopic==t.id> selected</if>>${t.title}</option>
+                  </list>
+                </select>
+            </td>
+            <td <if aLayout>class="listrow2"</if>>
                 <select name="searchorder">
                   <option value="datedesc" <if searchorder && searchorder=="datedesc"> selected</if>>${lang("contentsearch.order.datedesc")}</option>
                   <option value="dateasc" <if searchorder && searchorder=="dateasc"> selected</if>>${lang("contentsearch.order.dateasc")}</option>
 
 <comment>browse function, only called if necessary</comment>
 <function PrevNext (align)>
-  <if prevurl || nexturl>      
+  <if prevurl || nexturl>
     <p align="${align}">
     <if prevurl>
-      <a class="link-box" href="${config.actionRoot}?${prevurl}"> [ &lt; ] ${lang("list.previous")}</a>&nbsp;&nbsp; 
+      <a class="link-box" href="${config.actionRoot}?${prevurl}"> [ &lt; ] ${lang("list.previous")}</a>&nbsp;&nbsp;
     </if>
     <if nexturl>
       <a  class="link-box" href="${config.actionRoot}?${nexturl}">   ${lang("list.next")} [ &gt; ]</a>
 
 <comment>previous next links</comment>
 <function showPrevNextLinks (aPreviousUrl, aNextUrl, anAlignment)>
-  <if aPreviousUrl || aNextUrl>      
+  <if aPreviousUrl || aNextUrl>
     <p align="${anAlignment}">
     <if aPreviousUrl>
       <a class="link-box" href="${config.actionRoot}?${aPreviousUrl}"> [ &lt; ] ${lang("list.previous")}</a>
     </if>
-    <if aPreviousUrl && aNextUrl>      
-      &nbsp;&nbsp; 
+    <if aPreviousUrl && aNextUrl>
+      &nbsp;&nbsp;
     </if>
     <if aNextUrl>
       <a class="link-box" href="${config.actionRoot}?${aNextUrl}">   ${lang("list.next")} [ &gt; ]</a>
 <comment>multifunctional help - popup or not - big or small designs</comment>
 <function Help (HelpUrl, popup, big )>
   <if popup>
-    
+
     <Script Language="JavaScript">
-      <!-- 
+      <!--
       function openwindowlink() {
       newwin = window.open("${HelpUrl}","windowname","height=320,width=320,top=200,left=300, scrollbars,resizable")
       }
       // end hiding -->
     </Script>
-    
+
     <a href="JavaScript: openwindowlink()">
       <if big><span class="link-help"><else><span class="link-help-small"></if>
         <if big> [ ? ] ${lang("help")}<else>[?]</if>
       </span>
-    </a>    
-  
+    </a>
+
   <else>
-    
+
     <if big>
       <a href="${HelpUrl}" target="_blank"><span class="link-help"> [ ? ] ${lang("help")}</span></a>
     <else>
-      <a href="${HelpUrl}" target="_blank"><span class="link-help-small">[?]</span></a>   
+      <a href="${HelpUrl}" target="_blank"><span class="link-help-small">[?]</span></a>
     </if>
   </if>
 </function>
 
     <tr>
       <td align=right valign=top class="table-left">
-        ${lang("content.images")}: 
+        ${lang("content.images")}:
       </td>
       <if aCanEdit=="1">
         <td align="left" valign="top" class="listrow2">
       </if>
       </tr>
     </list>
-    
+
 
     <tr>
       <td align=right valign=top class="table-left">
     <list anObject.to_all_media_video as m>
       <tr>
       <td align=right valign=top>
-        <a href="${config.actionRoot}?module=Video&amp;do=getMedia&amp;id=${m["id"]}&amp;returnurl=${utility.encodeURI(aReturnUrl)}"><img src="${config.docRoot}/img/${m["big_icon"]}" alt="edit" border="0"></a>&nbsp;        
+        <a href="${config.actionRoot}?module=Video&amp;do=getMedia&amp;id=${m["id"]}&amp;returnurl=${utility.encodeURI(aReturnUrl)}"><img src="${config.docRoot}/img/${m["big_icon"]}" alt="edit" border="0"></a>&nbsp;
       </td>
       <if aCanEdit=="1">
         <td align="left" valign="top">
       </tr>
     </list>
 
-    
+
     <tr>
       <td align=right valign=top class="table-left">
         ${lang("content.other")}:
     <list anObject.to_all_media_other as m>
       <tr>
       <td align=right valign=top">
-      <a href="${config.actionRoot}?module=OtherMedia&amp;do=getMedia&amp;id=${m["id"]}&amp;returnurl=${utility.encodeURI(aReturnUrl)}"><img src="${config.docRoot}/img/${m["big_icon"]}" alt="edit" border="0"></a>&nbsp;  
+      <a href="${config.actionRoot}?module=OtherMedia&amp;do=getMedia&amp;id=${m["id"]}&amp;returnurl=${utility.encodeURI(aReturnUrl)}"><img src="${config.docRoot}/img/${m["big_icon"]}" alt="edit" border="0"></a>&nbsp;
       </td>
       <if aCanEdit=="1">
         <td align="left" valign="top">
       </tr>
     </list>
   </table>
-</function>  
+</function>
 
 <function showCommentAttachment(aComment, aReturnUrl)>
   <call showAttachment(0, aComment, "1", aReturnUrl)>