/** * Import object unserialized with midgard_replicator::unserialize() * * This method does ACL checks and triggers watchers etc. * * @param object $unserialized_object object gotten from midgard_replicator::unserialize() * @param boolean $use_force set use of force for the midcom_helper_replicator_import_object() call * @return boolean indicating success/failure * @todo refactor to smaller methods * @todo Add some magic to prevent importing of replication loops (see documentation/TODO for details about the potential problem) * @todo Verify support for the special cases of privilege * @todo Make sure older version is not imported over newer one (maybe configurable override ?) */ function import(&$unserialized_object, $use_force = false) { if (is_a($unserialized_object, 'midgard_blob')) { debug_add("You must use import_blob method to import BLOBs", MIDCOM_LOG_ERROR); midcom_connection::set_error(MGD_ERR_ERROR); return false; } // We need this helper (workaround Zend bug) if (!function_exists('midcom_helper_replicator_import_object')) { midcom::get('componentloader')->load('midcom.helper.replicator'); } if (!midcom::get('dbclassloader')->is_mgdschema_object($unserialized_object)) { debug_add("Unserialized object " . get_class($unserialized_object) . " is not recognized as supported MgdSchema class.", MIDCOM_LOG_ERROR); midcom_connection::set_error(MGD_ERR_ERROR); return false; } // Load the required component for DBA class $midcom_dba_classname = midcom::get('dbclassloader')->get_midcom_class_name_for_mgdschema_object($unserialized_object); if (!midcom::get('dbclassloader')->load_mgdschema_class_handler($midcom_dba_classname)) { debug_add("Failed to load the handling component for {$midcom_dba_classname}, cannot import.", MIDCOM_LOG_ERROR); midcom_connection::set_error(MGD_ERR_ERROR); return false; } // Get an object for ACL checks, use existing one if possible $acl_object = new $midcom_dba_classname($unserialized_object->guid); if (is_object($acl_object) && $acl_object->id) { if (!midcom::get('dbfactory')->is_a($acl_object, get_class($unserialized_object))) { $acl_class = get_class($acl_object); $unserialized_class = get_class($unserialized_object); debug_add("The local object we got is not of compatible type ({$acl_class} vs {$unserialized_class}), this means duplicate GUID", MIDCOM_LOG_ERROR); midcom_connection::set_error(MGD_ERR_DUPLICATE); return false; } // Got an existing object $acl_object_in_db = true; $actual_object_in_db = true; /* PONDER: should we copy values from unserialized here as well ?? likely we should (think moving article) midcom_baseclasses_core_dbobject::cast_object($acl_object, $unserialized_object) */ } else { $error_code = midcom_connection::get_error(); // switch is a loop, get the value only once this way switch ($error_code) { case MGD_ERR_ACCESS_DENIED: $actual_object_in_db = true; debug_add("Could not instantiate ACL object due to ACCESS_DENIED error, this means we can abort early", MIDCOM_LOG_ERROR); return false; break; case MGD_ERR_OBJECT_DELETED: case MGD_ERR_OBJECT_PURGED: $actual_object_in_db = true; break; default: $actual_object_in_db = false; break; } // Copy-construct $acl_object_in_db = false; $acl_object = new $midcom_dba_classname(); if (!midcom_baseclasses_core_dbobject::cast_object($acl_object, $unserialized_object)) { debug_add('Failed to cast MidCOM DBA object for ACL checks from $unserialized_object', MIDCOM_LOG_ERROR); debug_print_r('$unserialized_object: ', $unserialized_object); return false; } } // Magic to check for various corner cases to determine the action to take later on switch (true) { case $unserialized_object->action == 'purged': // Purges not supported yet debug_add("Purges not supported yet (they require extra special love)", MIDCOM_LOG_ERROR); midcom_connection::set_error(MGD_ERR_OBJECT_PURGED); return false; break; // action is created but object is already in db, cast to update // action is created but object is already in db, cast to update case $unserialized_object->action == 'created' && $actual_object_in_db: $handle_action = 'updated'; break; case $unserialized_object->action == 'updated' && !$actual_object_in_db: $handle_action = 'created'; break; default: if ($unserialized_object->action) { $handle_action = $unserialized_object->action; } else { $handle_action = 'updated'; } break; } if (!$acl_object->_on_importing()) { debug_add("The _on_importing event handler returned false.", MIDCOM_LOG_ERROR); // set errno if not set if (midcom_connection::get_error() === MGD_ERR_OK) { midcom_connection::set_error(MGD_ERR_ERROR); } return false; } switch ($handle_action) { case 'deleted': if (!$actual_object_in_db) { midcom_helper_replicator_import_object($unserialized_object, $use_force); break; } if (!midcom_baseclasses_core_dbobject::delete_pre_checks($acl_object)) { debug_add('delete pre-flight check returned false', MIDCOM_LOG_ERROR); return false; } // Actual import if (!midcom_helper_replicator_import_object($unserialized_object, $use_force)) { /** * BEGIN workaround * For http://trac.midgard-project.org/ticket/200 */ if (midcom_connection::get_error() === MGD_ERR_OBJECT_IMPORTED) { debug_add('Trying to workaround problem importing deleted action, calling $acl_object->delete()', MIDCOM_LOG_WARN); if ($acl_object->delete()) { debug_add('$acl_object->delete() succeeded, returning true early', MIDCOM_LOG_INFO); return true; } debug_add("\$acl_object->delete() failed for {$acl_object->guid}, errstr: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); // reset errno() midcom_connection::set_error(MGD_ERR_OBJECT_IMPORTED); } /** END workaround */ debug_add('midcom_helper_replicator_import_object returned false, errstr: ' . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } midcom_baseclasses_core_dbobject::delete_post_ops($acl_object); break; case 'updated': if (!midcom_baseclasses_core_dbobject::update_pre_checks($acl_object)) { debug_add('update pre-flight check returned false', MIDCOM_LOG_ERROR); return false; } // Actual import if (!midcom_helper_replicator_import_object($unserialized_object, $use_force)) { debug_add('midcom_helper_replicator_import_object returned false, errstr: ' . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } // "refresh" acl_object if (!midcom_baseclasses_core_dbobject::cast_object($acl_object, $unserialized_object)) { // this shouldn't happen, but shouldn't be fatal either... } midcom_baseclasses_core_dbobject::update_post_ops($acl_object); break; case 'created': if (!midcom_baseclasses_core_dbobject::create_pre_checks($acl_object)) { debug_add('creation pre-flight check returned false', MIDCOM_LOG_ERROR); return false; } // Actual import if (!midcom_helper_replicator_import_object($unserialized_object, $use_force)) { debug_add('midcom_helper_replicator_import_object returned false, errstr: ' . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR); return false; } // refresh object to avoid issues with _on_created requiring ID $acl_object_refresh = new $midcom_dba_classname($unserialized_object->guid); if (is_object($acl_object_refresh) && $acl_object_refresh->id) { $acl_object = $acl_object_refresh; midcom_baseclasses_core_dbobject::create_post_ops($acl_object); } else { // refresh failed (it really shouldn't), what to do ?? } break; default: debug_add("Do not know how to handle action '{$handle_action}'", MIDCOM_LOG_ERROR); midcom_connection::set_error(MGD_ERR_ERROR); return false; break; } $acl_object->_on_imported(); midcom::get('componentloader')->trigger_watches(MIDCOM_OPERATION_DBA_IMPORT, $acl_object); return true; }