Example #1
0
 /**
  * Copy an object
  *
  * @param mixed &$source     MgdSchema object for reading the parameters
  * @param mixed &$parent      MgdSchema parent object
  * @param array $defaults
  * @return boolean Indicating success
  */
 public function copy_object(&$source, &$parent = null, $defaults = array())
 {
     // Resolve the source object
     self::resolve_object($source);
     // Duplicate the object
     $class_name = get_class($source);
     $target = new $class_name();
     $properties = $this->get_object_properties($source);
     // Copy the object properties
     foreach ($properties as $property) {
         // Skip certain fields
         if (preg_match('/^(_|metadata|guid|id)/', $property)) {
             continue;
         }
         $target->{$property} = $source->{$property};
     }
     // Override requested root object properties
     if (isset($this->target->guid) && $target->guid === $this->target->guid) {
         foreach ($this->root_object_values as $name => $value) {
             $target->{$name} = $value;
         }
     }
     // Override with defaults
     if ($defaults) {
         foreach ($defaults as $name => $value) {
             $target->{$name} = $value;
         }
     }
     $parent_property = $this->get_parent_property($source);
     // Copy the link to parent
     if ($parent) {
         self::resolve_object($parent);
         if (!$parent || !$parent->guid) {
             return false;
         }
         // @TODO: Is there a sure way to determine if the parent is
         // GUID or is it ID? If so, please change it here.
         if (is_string($source->{$parent_property})) {
             $parent_key = 'guid';
         } else {
             $parent_key = 'id';
         }
         $target->{$parent_property} = $parent->{$parent_key};
     } else {
         if (is_string($source->{$parent_property})) {
             $target->{$parent_property} = '';
         } else {
             $target->{$parent_property} = 0;
         }
     }
     $name_property = midcom_helper_reflector::get_name_property($target);
     $resolver = new midcom_helper_reflector_nameresolver($target);
     if (!empty($name_property) && !$resolver->name_is_safe_or_empty($name_property)) {
         debug_add('Source object ' . get_class($source) . " {$source->guid} has unsafe name, rewriting to safe form for the target", MIDCOM_LOG_WARN);
         $name_property = midcom_helper_reflector::get_name_property($target);
         if (empty($name_property)) {
             $this->errors[] = sprintf($this->_l10n->get('cannot fix unsafe name for source object %s, skipping'), get_class($source) . " {$source->guid}");
             return false;
         }
         $name_parts = explode('.', $target->{$name_property}, 2);
         if (isset($name_parts[1])) {
             $target->{$name_property} = midcom_helper_misc::generate_urlname_from_string($name_parts[0]) . ".{$name_parts[1]}";
             // Doublecheck safety and fall back if needed
             if (!$resolver->name_is_safe_or_empty()) {
                 $target->{$name_property} = midcom_helper_misc::generate_urlname_from_string($target->{$name_property});
             }
         } else {
             $target->{$name_property} = midcom_helper_misc::generate_urlname_from_string($target->{$name_property});
         }
         unset($name_parts, $name_property);
     }
     if ($this->allow_name_catenate && $name_property) {
         $name = $resolver->generate_unique_name();
         if ($name !== $target->{$name_property}) {
             $target->{$name_property} = $name;
         }
     }
     // This needs to be here, otherwise it will be overridden
     $target->allow_name_catenate = true;
     if (!$target->create()) {
         $this->errors[] = $this->_l10n->get('failed to create object: ' . mgd_errstr());
         return false;
     }
     // Store for later use - if ever needed
     $this->new_objects[] = $target;
     unset($name_property);
     // Copy parameters
     if (!$this->copy_parameters($source, $target) && $this->halt_on_errors) {
         $this->errors[] = $this->_l10n->get('failed to copy parameters');
         return false;
     }
     // Copy metadata
     if (!$this->copy_metadata($source, $target) && $this->halt_on_errors) {
         $this->errors[] = $this->_l10n->get('failed to copy metadata');
         return false;
     }
     // Copy attachments
     if (!$this->copy_attachments($source, $target) && $this->halt_on_errors) {
         $this->errors[] = $this->_l10n->get('failed to copy attachments');
         return false;
     }
     // Copy privileges
     if (!$this->copy_privileges($source, $target) && $this->halt_on_errors) {
         $this->errors[] = $this->_l10n->get('failed to copy privileges');
         return false;
     }
     return $target;
 }
Example #2
0
 /**
  * @dataProvider providerGet_name_property
  */
 public function testGet_name_property($classname, $property)
 {
     $object = new $classname();
     $reflector = new midcom_helper_reflector($classname);
     $this->assertEquals($property, $reflector->get_name_property($object));
 }
Example #3
0
 /**
  * Helper method to call in the _xxx_pre_checks, handles the API
  * level checks and automatic operations as specified in ticket #809
  *
  * @see http://trac.midgard-project.org/ticket/809
  * Quoting the ticket API-level section:
  * <pre>
  *      1. Checks will be done in the pre-flight check phase (ie just after _on_creating/_on_updating)
  *      2. If name is not unique false is returned for pre-flight check, preventing create/update
  *          2.2 UNLESS a property in the object ('allow_name_catenate') is set to true in which case unique one is generated by catenating an incrementing number to the name.
  *      3. if name is empty unique name is generated from title property (unless title is empty too)
  *      4. if name is not URL-safe false is returned
  * </pre>
  *
  * @param midcom_core_dbaobject $object The DBA object we're working on
  * @return boolean indicating whether from our point of view everything is ok
  *
  * @see midcom_helper_reflector_nameresolver::name_is_safe_or_empty()
  * @see midcom_helper_reflector_nameresolver::name_is_unique_or_empty()
  * @see midcom_helper_reflector_nameresolver::generate_unique_name()
  */
 private static function _pre_check_name(midcom_core_dbaobject $object)
 {
     // Make sure name is empty of unique if the object has such property
     $name_property = midcom_helper_reflector::get_name_property($object);
     if (empty($name_property)) {
         // This object has no name property, return early
         return true;
     }
     $resolver = new midcom_helper_reflector_nameresolver($object);
     /**
      * If name is empty, try to generate new, unique one
      *
      * @see http://trac.midgard-project.org/ticket/809
      */
     if (empty($object->{$name_property})) {
         // name is empty, try to generate
         $new_name = $resolver->generate_unique_name();
         if (!empty($new_name)) {
             $object->{$name_property} = $new_name;
         }
         unset($new_name);
     }
     /**
      * Enforce URL-safe (or empty) names
      *
      * @see http://trac.midgard-project.org/ticket/809
      */
     if (!$resolver->name_is_safe_or_empty()) {
         midcom_connection::set_error(MGD_ERR_INVALID_NAME);
         return false;
     }
     /**
      * Enforce unique (or empty) names
      *
      * @see http://trac.midgard-project.org/ticket/809
      */
     if (!$resolver->name_is_unique_or_empty()) {
         if ($object->allow_name_catenate) {
             // Transparent catenation allowed, let's try again.
             $new_name = $resolver->generate_unique_name();
             if (!empty($new_name)) {
                 $object->{$name_property} = $new_name;
                 return true;
             } else {
                 debug_add('allow_name_catenate was set but midcom_helper_reflector_nameresolver::generate_unique_name() returned empty value, falling through', MIDCOM_LOG_WARN);
             }
         }
         midcom_connection::set_error(MGD_ERR_OBJECT_NAME_EXISTS);
         return false;
     }
     // All checks ok, we're fine.
     return true;
 }
Example #4
0
 private function _add_linked_field($key)
 {
     $linked_type = $this->_reflector->get_link_name($key);
     $linked_type_reflector = midcom_helper_reflector::get($linked_type);
     $field_type = $this->_reflector->get_midgard_type($key);
     if ($key == 'up') {
         $field_label = sprintf($this->_l10n->get('under %s'), midgard_admin_asgard_plugin::get_type_label($linked_type));
     } else {
         $type_label = midgard_admin_asgard_plugin::get_type_label($linked_type);
         if (substr($type_label, 0, strlen($key)) == $key) {
             // Handle abbreviations like "lang" for "language"
             $field_label = $type_label;
         } else {
             if ($key == $type_label) {
                 $field_label = $key;
             } else {
                 $ref = midcom_helper_reflector::get($this->_object);
                 $component_l10n = $ref->get_component_l10n();
                 $field_label = sprintf($this->_l10n->get('%s (%s)'), $component_l10n->get($key), $type_label);
             }
         }
     }
     // Get the chooser widgets
     switch ($field_type) {
         case MGD_TYPE_UINT:
         case MGD_TYPE_STRING:
         case MGD_TYPE_GUID:
             $class = midcom::get('dbclassloader')->get_midcom_class_name_for_mgdschema_object($linked_type);
             if (!$class) {
                 break;
             }
             $component = midcom::get('dbclassloader')->get_component_for_class($linked_type);
             $this->_schemadb['object']->append_field($key, array('title' => $field_label, 'storage' => $key, 'type' => 'select', 'type_config' => array('require_corresponding_option' => false, 'options' => array(), 'allow_other' => true, 'allow_multiple' => false), 'widget' => 'chooser', 'widget_config' => array('class' => $class, 'component' => $component, 'titlefield' => $linked_type_reflector->get_label_property(), 'id_field' => $this->_reflector->get_link_target($key), 'searchfields' => $linked_type_reflector->get_search_properties(), 'result_headers' => $this->_get_result_headers($linked_type_reflector), 'orders' => array(), 'creation_mode_enabled' => true, 'creation_handler' => midcom_connection::get_url('self') . "__mfa/asgard/object/create/chooser/{$linked_type}/", 'creation_default_key' => $linked_type_reflector->get_label_property(), 'generate_path_for' => midcom_helper_reflector::get_name_property($this->_object))));
             break;
     }
 }
Example #5
0
<?php

/**
 * @package midcom.helper.reflector
 * @author The Midgard Project, http://www.midgard-project.org
 * @copyright The Midgard Project, http://www.midgard-project.org
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
 */
// FIXME: make generic
$article = new midcom_db_article();
$article->topic = 5;
$article->title = 'Duplicate name test with (with allow_catenate)';
$article->name = 'gathering-09';
$article->allow_name_catenate = true;
$reflected_name_property = midcom_helper_reflector::get_name_property($article);
$resolver = new midcom_helper_reflector_nameresolver($article);
$reflected_name = $resolver->get_object_name();
echo "Reflector thinks name is '{$reflected_name}' (from property '{$reflected_name_property}')<br>\n";
if ($resolver->name_is_safe()) {
    echo "OK: '{$reflected_name}' is considered URL-safe<br>\n";
} else {
    echo "ERROR: '{$reflected_name}' is NOT considered URL-safe<br>\n";
}
if ($resolver->name_is_clean()) {
    echo "OK: '{$reflected_name}' is considered 'clean'<br>\n";
} else {
    echo "WARN: '{$reflected_name}' is NOT considered 'clean'<br>\n";
}
if ($resolver->name_is_unique()) {
    echo "OK: '{$reflected_name}' is unique (among siblings)<br>\n";
} else {
Example #6
0
 /**
  * Helper to resolve the base value for the incrementing suffix and for the name.
  *
  * @see midcom_helper_reflector_nameresolver::generate_unique_name()
  * @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())
  * @param string $extension The file extension, when working with attachments
  * @return array first key is the resolved $i second is the $base_name, which is $current_name without numeric suffix
  */
 private function _generate_unique_name_resolve_i($current_name, $extension)
 {
     if (preg_match('/(.*?)-([0-9]{3,})' . $extension . '$/', $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::get('auth')->request_sudo('midcom.helper.reflector');
     $parent = midcom_helper_reflector_tree::get_parent($this->_object);
     // TODO: Refactor to reduce duplicate code with _name_is_unique_check_siblings
     if ($parent && !empty($parent->guid)) {
         // We have parent, check siblings
         $parent_resolver = new midcom_helper_reflector_tree($parent);
         $sibling_classes = $parent_resolver->get_child_classes();
         if (!in_array('midgard_attachment', $sibling_classes)) {
             $sibling_classes[] = 'midgard_attachment';
         }
         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;
             }
             $resolver = midcom_helper_reflector_tree::get($schema_type);
             $qb =& $resolver->_child_objects_type_qb($schema_type, $parent, false);
             if (!is_object($qb)) {
                 continue;
             }
             $qb->add_constraint($child_name_property, 'LIKE', "{$base_name}-%" . $extension);
             // Do not include current object in results, this is the easiest way
             if (!empty($this->_object->guid)) {
                 $qb->add_constraint('guid', '<>', $this->_object->guid);
             }
             $qb->add_order('name', 'DESC');
             // One result should be enough
             $qb->set_limit(1);
             $siblings = $qb->execute();
             if (empty($siblings)) {
                 // we don't care about fatal qb errors here
                 continue;
             }
             $sibling = $siblings[0];
             $sibling_name = $sibling->{$child_name_property};
             if (preg_match('/(.*?)-([0-9]{3,})' . $extension . '$/', $sibling_name, $name_matches)) {
                 // Name already has i and base parts, split them.
                 $sibling_i = (int) $name_matches[2];
                 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);
     } else {
         unset($parent);
         // No parent, we might be a root level class
         $is_root_class = false;
         $root_classes = midcom_helper_reflector_tree::get_root_classes();
         foreach ($root_classes as $schema_type) {
             if (midcom::get('dbfactory')->is_a($this->_object, $schema_type)) {
                 $is_root_class = true;
             }
         }
         if (!$is_root_class) {
             // This should not happen, logging error and returning true (even though it's potentially dangerous)
             midcom::get('auth')->drop_sudo();
             debug_add("Object #{$this->_object->guid} has no valid parent but is not listed in the root classes, don't know what to do, letting higher level decide", MIDCOM_LOG_ERROR);
             unset($root_classes, $is_root_class);
             return array($i, $base_name);
         } else {
             // TODO: Refactor to reduce duplicate code with _name_is_unique_check_roots
             foreach ($root_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;
                 }
                 $resolver =& midcom_helper_reflector_tree::get($schema_type);
                 $deleted = false;
                 $qb =& $resolver->_root_objects_qb($deleted);
                 if (!$qb) {
                     continue;
                 }
                 unset($deleted);
                 $qb->add_constraint($child_name_property, 'LIKE', "{$base_name}-%" . $extension);
                 // Do not include current object in results, this is the easiest way
                 if (!empty($this->_object->guid)) {
                     $qb->add_constraint('guid', '<>', $this->_object->guid);
                 }
                 $qb->add_order($child_name_property, '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;
                 }
                 $sibling = $siblings[0];
                 $sibling_name = $sibling->{$child_name_property};
                 if (preg_match('/(.*?)-([0-9]{3,})' . $extension . '$/', $sibling_name, $name_matches)) {
                     // Name already has i and base parts, split them.
                     $sibling_i = (int) $name_matches[2];
                     if ($sibling_i >= $i) {
                         $i = $sibling_i + 1;
                     }
                     unset($sibling_i, $name_matches);
                 }
             }
             unset($root_classes, $schema_type, $child_name_property, $sibling, $sibling_name);
         }
     }
     midcom::get('auth')->drop_sudo();
     return array($i, $base_name);
 }