adding a unique constraint to a field in Rx

We would like to add a unique constraint to one of our custom fields in Rhythmyx. Is there a straightforward way to do this? Rx implements this for sys_title already. Is there code we can leverage from that? Or would we have to write a custom extension? If we write an extension, what API would we employ to do the lookup to check for a row already containing the entered content? Examples?

This would be a nice option to have out of the box in the workbench … checkbox for “unique”.

Hi,

You can use the extension “sys_ValidateUniqueName” at your field validation (I just learn that too :wink: ).

Regards,

Guillaume

Thanks for the tip, Guillaume. I just tried applying that extension, but it did not have any effect. I can enter two content items with the same value on the field in question without error. Did you have to do anything else to activate this, other than restart the Rx instance?

It turns out the “sys_ValidateUniqueName” validator is broken as of 6.5.2. It is in the queue to be fixed in an upcoming release. We also learned that this validator only checks the current directory for duplicate entries (by design).

What we wanted was a check for field uniqueness across all content items with the given field. We had to implement that as a custom validation rule. IMO, this should be offered as a standard validator in the core product.

We are on 6.5.2 without the Java patch and it is working as designed (as in unique per folder). Was the uniqueness per folder not working for you?

[QUOTE=stephenh;1613]It turns out the “sys_ValidateUniqueName” validator is broken as of 6.5.2. It is in the queue to be fixed in an upcoming release. We also learned that this validator only checks the current directory for duplicate entries (by design).

What we wanted was a check for field uniqueness across all content items with the given field. We had to implement that as a custom validation rule. IMO, this should be offered as a standard validator in the core product.[/QUOTE]

The problem here is that everybody has their own requirements. I can certainly see that some customers will need uniqueness across the whole system, but others will want uniqueness only within a particular site (for example).

Since there are many options, I think our goal should be to make this flexible and easy to extend, rather than to cover every possible possibility “out of the box”.

That said, how difficult was it for you to implement a custom validator? What do we need to do to make that process easier?

Dave

Jitendra,

We are running 6.5.2 with the lastest patch. The problem we saw was that the validation rule was not executed, or at least presented to the UI, unless another error was present on the page. Maybe this is unique to our configuration, but it’s on the devel bug list regardless.

Dave,

Sharyn found your post on the forum which was helpful in figuring out how to do this.

http://forum.percussion.com/showthread.php?t=147

Having this doc and code set in the product’s Docs would be handy for a start. I agree that variations could exist on the unique constraint rule. But at it’s heart, it’s pretty common. Just look at any db implementation for proof of this! You could take the approach that setting a unique flag on a property in the Content Workbench would add a unique constraint on the table, and then have the app backing the content explorer catch any of these exceptions and convert them into error messages for the UI. But that might be too course grained.

We wound up writing a custom validator based off your examples for the specific property in question. We plan to refactor this at some point to accept any property from any table. For now, what we have works. The general version might make a good reference implementation for others. Care to give it a go? :smiley:

Steve

The forum doesn’t permit attaching .java files for some reason. Here is what we came up with for our custom unique field validator:

/**
*
*/
package com.percussion.pso.babycenter.validate;

import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.percussion.data.PSConversionException;
import com.percussion.extension.IPSFieldValidator;
import com.percussion.extension.PSDefaultExtension;
import com.percussion.extension.PSExtensionParams;
import com.percussion.server.IPSRequestContext;
import com.percussion.services.contentmgr.IPSContentMgr;
import com.percussion.services.contentmgr.PSContentMgrLocator;

/**

  • Validate entries in a column as if they are unique. No duplicates allowed.

  • @author jmather
    */
    public class BCValidateUniqueColumn extends PSDefaultExtension
    implements IPSFieldValidator
    {
    private static final Log log = LogFactory.getLog(BCValidateUniqueColumn.class);
    private static IPSContentMgr cmgr = null;

    /**

    • This method gets executed when the field validator is called.

    • @param args Any arguments that may be being passed to this validator

    • @param context The request context holding any form data that may

    • have been submitted.
      */
      public Object processUdf(Object[] args, IPSRequestContext context) throws PSConversionException
      {
      PSExtensionParams param = new PSExtensionParams(args);
      String fieldName = param.getStringParam(0, “”, true);

      String value = context.getParameter(fieldName);
      log.debug("field to validate: " + fieldName + ", looking up value: " + value);

      boolean isUnique = true;
      boolean isNew = true;
      Long contentId = null;
      Long revision = null;

      initService();

      if ((context.getParameter(“contentid”) != null)
      && (context.getParameter(“sys_revision”) != null))
      {
      isNew = false;
      contentId = Long.valueOf(context.getParameter(“contentid”));
      revision = Long.valueOf(context.getParameter(“sys_revision”));
      }

      if (StringUtils.isBlank(value))
      {
      return false;
      }

      String query = "SELECT rx:customUrl, rx:contentid, rx:sys_revision FROM rx:BCCustomURL " +
      “WHERE rx:customUrl =’” + value + “’”;
      if (StringUtils.equals(fieldName, “targetUrl”))
      {
      query = "SELECT rx:targetUrl, rx:contentid, rx:sys_revision FROM rx:BCCustomURL " +
      “where rx:targetUrl =’” + value + “’”;
      }
      isUnique = executeIsUniqueQuery(query, contentId, revision, isNew);

      return isUnique;
      }

    /**

    • Execute a query and iterate over any rows returned to see if they do
    • not match our content ID and revision. If there is no match, then the
    • field validated is not unique. If there is a match then it is unique.
    • @param query the query to execute
    • @param contentId ID of the content to validate, if an update. Will
    •  be null if we are validating a new item.
      
    • @param revision Revision number of the content to validate. Will be
    •  null if we are validating a new item.
      
    • @param isNew True if we are validating a new item, false otherwise
    • @return
      */
      private boolean executeIsUniqueQuery(String query, Long contentId, Long revision, boolean isNew)
      {
      try
      {
      Query q = cmgr.createQuery(query, Query.SQL);
      QueryResult result = cmgr.executeQuery(q, 1, null, null);
      RowIterator iterator = result.getRows();
      if (isNew)
      {
      return (iterator.getSize() == 0);
      }
      else
      {
      while (iterator.hasNext())
      {
      Row row = iterator.nextRow();
      Value cid = row.getValue(“contentid”);
      Value rid = row.getValue(“sys_revision”);
      if ((cid.getLong() != contentId) && (rid.getLong() != revision))
      {
      return false;
      }
      }
      }
      }
      catch(InvalidQueryException e)
      {
      log.error("Invalid query: " + e.getMessage());
      }
      catch(RepositoryException e)
      {
      log.error("Repository error: " + e.getMessage());
      }
      return true;
      }

    /**

    • Initialize the content manager which allows us to query the database.
      */
      private void initService()
      {
      if(cmgr == null)
      {
      cmgr = PSContentMgrLocator.getContentMgr();
      }
      }
      }

Stephen,

[QUOTE=stephenh;1629]
Dave,

Sharyn found your post on the forum which was helpful in figuring out how to do this.

http://forum.percussion.com/showthread.php?t=147

Having this doc and code set in the product’s Docs would be handy for a start.
Steve[/QUOTE]

This information will be included in the Rhythmyx Technical Reference in the next release.

RLJII

Sorry to pull up an old post, but I am noticing that we are having this same issue. Whenever we apply the sys_validateUniqueName() to a field it will only present an error to the UI if there is another error present. Was there ever a fix presented for this? I couldn’t find one on here, and it wasn’t in the current patch list. We are on 6.5.2 [RX-14225]

According to Tech Support, this was to be fixed in 6.6. So what you are seeing is to be expected until you upgrade or press for a patch including this fix.

Thank you. Tech support didn’t even tell me that.

Well, 6.6 is here, right? Does anyone know if this was fixed? We would greatly like to use it to ensure filenames are unique within a folder.