MidCOM svn: r20988 - in branches/ragnaroek/midcom/midcom.helper.reflector: . exec

rambo midcom-commits at lists.midgard-project.org
Tue Mar 10 15:49:54 CET 2009


Author: rambo
Date: Tue Mar 10 15:49:54 2009
New Revision: 20988
URL: http://trac.midgard-project.org/changeset/20988

Log:
method for generating unique names for objects, refs #809

Modified:
   branches/ragnaroek/midcom/midcom.helper.reflector/exec/test_name.php
   branches/ragnaroek/midcom/midcom.helper.reflector/main.php
   branches/ragnaroek/midcom/midcom.helper.reflector/tree.php

Modified: branches/ragnaroek/midcom/midcom.helper.reflector/exec/test_name.php
==============================================================================
--- branches/ragnaroek/midcom/midcom.helper.reflector/exec/test_name.php	(original)
+++ branches/ragnaroek/midcom/midcom.helper.reflector/exec/test_name.php	Tue Mar 10 15:49:54 2009
@@ -12,7 +12,7 @@
 $article = new midcom_db_article();
 $article->topic = 5;
 $article->title = 'unsafe name test';
-$article->name = 'ääkkösiä';
+$article->name = 'gathering-09';
 
 /*
 $stat = (int)$article->create();
@@ -54,4 +54,9 @@
 {
     echo "ERROR: '{$reflected_name}' is NOT unique (among siblings)<br>\n";
 }
+
+$new_name = midcom_helper_reflector_tree::generate_unique_name($article);
+echo "midcom_helper_reflector_tree::generate_unique_name(\$article) returned '{$new_name}'<br>\n";
+
+
 ?>
\ No newline at end of file

Modified: branches/ragnaroek/midcom/midcom.helper.reflector/main.php
==============================================================================
--- branches/ragnaroek/midcom/midcom.helper.reflector/main.php	(original)
+++ branches/ragnaroek/midcom/midcom.helper.reflector/main.php	Tue Mar 10 15:49:54 2009
@@ -52,7 +52,7 @@
         if (empty($this->mgdschema_class))
         {
             debug_push_class(__CLASS__, __FUNCTION__);
-            debug_add("Could not determnine MgdSchema baseclass", MIDCOM_LOG_ERROR);
+            debug_add("Could not determine MgdSchema baseclass for '{$this->_original_class}'", MIDCOM_LOG_ERROR);
             debug_pop();
             $x = false;
             return $x;

Modified: branches/ragnaroek/midcom/midcom.helper.reflector/tree.php
==============================================================================
--- branches/ragnaroek/midcom/midcom.helper.reflector/tree.php	(original)
+++ branches/ragnaroek/midcom/midcom.helper.reflector/tree.php	Tue Mar 10 15:49:54 2009
@@ -1190,6 +1190,7 @@
         }
 
         // Start the magic
+        $_MIDCOM->auth->request_sudo('midcom.helper.reflector');
         $parent = midcom_helper_reflector_tree::get_parent($object);
         $parent_resolver = new midcom_helper_reflector_tree($parent);
         $sibling_classes = $parent_resolver->get_child_classes();
@@ -1224,6 +1225,7 @@
             // Guard against QB failure
             if ($results === false)
             {
+                $_MIDCOM->auth->drop_sudo();
                 debug_push_class(__CLASS__, __FUNCTION__);
                 debug_add("Querying for siblings of class {$schema_type} failed critically, last Midgard error: " . midgard_connection::get_error_string(), MIDCOM_LOG_ERROR);
                 debug_pop();
@@ -1232,6 +1234,7 @@
             }
             if ($results > 0)
             {
+                $_MIDCOM->auth->drop_sudo();
                 debug_push_class(__CLASS__, __FUNCTION__);
                 debug_add("Name clash in sibling class {$schema_type} for " . get_class($object) . " #{$object->id} (path '" . midcom_helper_reflector_tree::resolve_path($object, '/') . "')" , MIDCOM_LOG_DEBUG);
                 debug_pop();
@@ -1239,9 +1242,224 @@
                 return false;
             }
         }
+        $_MIDCOM->auth->drop_sudo();
         // If we get this far we know we don't have name clashes
         unset($sibling_classes, $schema_type, $qb, $parent, $parent_resolver);
         return true;
     }
+
+
+    /**
+     * statically callable method to generates an unique name for the given object.
+     *
+     * 1st IF name is empty, we generate one from title (if title is empty too, we return false)
+     * Then we check if it's unique, if not we add an incrementing
+     * number to it (before this we make some educated guesses about a
+     * good starting value)
+     *
+     * @see midcom_helper_reflector_tree::generate_unique_name_nonstatic()
+     * @param object $object reference to the object to handle
+     * @param srting $title_property, property of the object to use at title, if null will be reflected (see  midcom_helper_reflector::get_object_title())
+     * @return string string usable as name or boolean false on critical failures
+     */
+    function generate_unique_name(&$object, $title_property=null)
+    {
+        $resolver = new midcom_helper_reflector_tree($object);
+        return $resolver->generate_unique_name_nonstatic($object, $title_property);
+    }
+    
+
+    /**
+     * Generates an unique name for the given object.
+     *
+     * 1st IF name is empty, we generate one from title (if title is empty too, we return false)
+     * Then we check if it's unique, if not we add an incrementing
+     * number to it (before this we make some educated guesses about a
+     * good starting value)
+     *
+     * @param object $object reference to the object to handle
+     * @param srting $title_property, property of the object to use at title, if null will be reflected (see  midcom_helper_reflector::get_object_title())
+     * @return string string usable as name or boolean false on critical failures
+     */
+    function generate_unique_name_nonstatic(&$object, $title_property=null)
+    {
+        // Check against static calling
+        if (   !isset($this->mgdschema_class)
+            || empty($this->mgdschema_class))
+        {
+            debug_push_class(__CLASS__, __FUNCTION__);
+            debug_add('May not be called statically', MIDCOM_LOG_ERROR);
+            debug_pop();
+            return false;
+        }
+        
+        // Get current name and sanity-check
+        $original_name = midcom_helper_reflector::get_object_name($object);
+        if ($original_name === false)
+        {
+            // Fatal error with name resolution
+            debug_push_class(__CLASS__, __FUNCTION__);
+            debug_add("Object " . get_class($object) . " #{$object->id} returned critical failure for name resolution, aborting", MIDCOM_LOG_WARN);
+            debug_pop();
+            return false;
+        }
+
+        // We need the name of the "name" property later
+        $name_prop = midcom_helper_reflector::get_name_property($object);
+        
+        if (!empty($original_name))
+        {
+            $current_name = (string)$original_name;
+        }
+        else
+        {
+            // Empty name, try to generate from title
+            $title_copy = midcom_helper_reflector::get_object_title($object, $title_property);
+            if ($title_copy === false)
+            {
+                unset($title_copy);
+                // Fatal error with title resolution
+                debug_push_class(__CLASS__, __FUNCTION__);
+                debug_add("Object " . get_class($object) . " #{$object->id} returned critical failure for title resolution when name was empty, aborting", MIDCOM_LOG_WARN);
+                debug_pop();
+                return false;
+            }
+            if (empty($title_copy))
+            {
+                unset($title_copy);
+                debug_push_class(__CLASS__, __FUNCTION__);
+                debug_add("Object " . get_class($object) . " #{$object->id} has empty name and title, aborting", MIDCOM_LOG_WARN);
+                debug_pop();
+                return false;
+            }
+            $current_name = midcom_generate_urlname_from_string($title_copy);
+            unset($title_copy);
+        }
+
+        // incrementer, the number to add as suffix and the base name. see _generate_unique_name_nonstatic_resolve_i()
+        list ($i, $base_name) = $this->_generate_unique_name_nonstatic_resolve_i($current_name, $object);
+        $object->name = $base_name;
+        // decementer, do not try more than this many times (the incrementer can raise above this if we start high enough.
+        $d = 100;
+
+        // The loop, usually we *should* hit gold in first try
+        do
+        {
+            if ($i > 1)
+            {
+                // Start suffixes from -002
+                $object->{$name_prop} = $base_name . sprintf('-%03d', $i);
+            }
+
+            // Handle the decrementer
+            --$d;
+            if ($d < 1)
+            {
+                // Decrementer undeflowed
+                debug_push_class(__CLASS__, __FUNCTION__);
+                debug_add("Maximum number of tries exceeded, current name was: " . $object->{$name_prop} , MIDCOM_LOG_ERROR);
+                debug_pop();
+                $object->{$name_prop} = $original_name;
+                unset($i, $d, $name_prop, $original_name, $base_name);
+                return false;
+            }
+            // and the incrementer
+            ++$i;
+        }
+        while (!$this->name_is_unique_nonstatic($object));
+        unset($i, $d);
+
+        // Get a copy of the current, usable name
+        $ret = (string)$object->{$name_prop};
+        // Restore the original name
+        $object->{$name_prop} = $original_name;
+        unset($name_prop, $original_name, $base_name);
+        return $ret;
+    }
+
+    /**
+     * Helper to resolve the base value for the incementing suffix and for the name.
+     *
+     * @see midcom_helper_reflector_tree::generate_unique_name_nonstatic()
+     * @param string $current_name the "current name" of the object (might not be the actual name value see the title logic in generate_unique_name_nonstatic())
+     * @param object $object reference to the object we're working on
+     * @return array first key is the resolved $i second is the $base_name, which is $current_name without numeric suffix
+     */
+    function _generate_unique_name_nonstatic_resolve_i($current_name, &$object)
+    {
+        if (preg_match('/(.*?)-([0-9]{3,})$/', $current_name, $name_matches))
+        {
+            // Name already has i and base parts, split them.
+            $i = (int)$name_matches[2];
+            $base_name = (string)$name_matches[1];
+            unset($name_matches);
+        }
+        else
+        {
+            // Defaults
+            $i = 1;
+            $base_name = $current_name;
+        }
+        
+        // Look for siblings with similar names and see if they have higher i.
+        $_MIDCOM->auth->request_sudo('midcom.helper.reflector');
+        $parent = midcom_helper_reflector_tree::get_parent($object);
+        $parent_resolver = new midcom_helper_reflector_tree($parent);
+        $sibling_classes = $parent_resolver->get_child_classes();
+        foreach ($sibling_classes as $schema_type)
+        {
+            $dummy = new $schema_type();
+            $child_name_property = midcom_helper_reflector::get_name_property($dummy);
+            unset($dummy);
+            if (empty($child_name_property))
+            {
+                // This sibling class does not use names
+                continue;
+            }
+            $qb =& $this->_child_objects_type_qb($schema_type, $parent, false);
+            $qb->add_constraint($child_name_property, 'LIKE', "{$base_name}-%");
+            // Do not include current object in results, this is the easiest way
+            if (   isset($object->guid)
+                || !empty($object->guid))
+            {
+                $qb->add_constraint('guid', '<>', $object->guid);
+            }
+            $qb->add_order('name', 'DESC');
+            // One result should be enough
+            $qb->set_limit(1);
+            $siblings = $qb->execute();
+            if (empty($siblings))
+            {
+                // we dont' care about fatal qb errors here
+                continue;
+            }
+            /*
+            debug_push_class(__CLASS__, __FUNCTION__);
+            debug_print_r('$siblings', $siblings);
+            debug_pop();
+            */
+            $sibling = $siblings[0];
+            $sibling_name = $sibling->{$child_name_property};
+            if (preg_match('/(.*?)-([0-9]{3,})$/', $sibling_name, $name_matches))
+            {
+                // Name already has i and base parts, split them.
+                $sibling_i = (int)$name_matches[2];
+                /*
+                debug_push_class(__CLASS__, __FUNCTION__);
+                debug_add("\$sibling_i={$sibling_i}, \$i={$i}", $siblings);
+                debug_pop();
+                */
+                if ($sibling_i >= $i)
+                {
+                    $i = $sibling_i + 1;
+                }
+                unset($sibling_i, $name_matches);
+            }
+        }
+        unset($parent, $parent_resolver, $sibling_classes, $schema_type, $child_name_property, $sibling, $sibling_name);
+        $_MIDCOM->auth->drop_sudo();
+
+        return array($i, $base_name);
+    }
 }
 ?>
\ No newline at end of file


More information about the midcom-commits mailing list