/** * DM2 creation callback, binds to the current content topic. */ private function _create_article($title) { $this->_article = new midcom_db_article(); $author = $this->_find_email_person($this->_request_data['from']); if (!$author) { debug_add("Author '{$this->_request_data['from']}' not found", MIDCOM_LOG_WARN); if ($this->_config->get('api_email_abort_authornotfound') !== false) { throw new midcom_error("Author '{$this->_request_data['from']}' not found"); } $this->_article->author = midcom_connection::get_user(); } else { // TODO: This code needs a bit of rethinking $author_user = midcom::get('auth')->get_user($author->guid); if (!$this->_content_topic->can_do('midgard:create', $author_user)) { throw new midcom_error('Author doesn\'t have posting privileges'); } $this->_article->author = $author->id; } //Default to first user in DB if author is not set if (!$this->_article->author) { $qb = midcom_db_person::new_query_builder(); $qb->add_constraint('username', '<>', ''); $qb->set_limit(1); $results = $qb->execute(); unset($qb); if (empty($results)) { //No users found throw new midcom_error('Cannot set any author for the article'); } $this->_article->author = $results[0]->id; } $resolver = new midcom_helper_reflector_nameresolver($this->_article); $this->_article->topic = $this->_content_topic->id; $this->_article->title = $title; $this->_article->allow_name_catenate = true; $this->_article->name = $resolver->generate_unique_name('title'); if (empty($this->_article->name)) { debug_add('Could not generate unique name for the new article from title, using timestamp', MIDCOM_LOG_INFO); $this->_article->name = time(); $resolver = new midcom_helper_reflector_nameresolver($this->_article); if (!$resolver->name_is_unique()) { throw new midcom_error('Failed to create unique name for the new article, aborting.'); } } if (!$this->_article->create()) { debug_print_r('Failed to create article:', $this->_article); throw new midcom_error('Failed to create a new article. Last Midgard error was: ' . midcom_connection::get_error_string()); } $this->_article->parameter('midcom.helper.datamanager2', 'schema_name', $this->_config->get('api_email_schema')); return true; }
/** * 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; }
/** * Make sure our name is nice and clean * * @see http://trac.midgard-project.org/ticket/809 */ public function _on_validate() { $schema = $this->storage->_schema->fields[$this->name]; $copy = $this->_copy_object($this->storage->object); $property = $schema['storage']['location']; $resolver = new midcom_helper_reflector_nameresolver($copy); if (empty($this->value)) { if (isset($this->_datamanager->types[$this->title_field]) && $this->_datamanager->types[$this->title_field]->value) { $copy->{$property} = midcom_helper_misc::generate_urlname_from_string($this->_datamanager->types[$this->title_field]->value); $this->value = $resolver->generate_unique_name(); } } $copy->{$property} = $this->value; if (!$resolver->name_is_safe($property)) { $this->validation_error = sprintf($this->_l10n->get('type urlname: name is not "URL-safe", try "%s"'), midcom_helper_misc::generate_urlname_from_string($this->value)); return false; } if (!$this->allow_unclean && !$resolver->name_is_clean($property)) { $this->validation_error = sprintf($this->_l10n->get('type urlname: name is not "clean", try "%s"'), midcom_helper_misc::generate_urlname_from_string($this->value)); return false; } if (!$resolver->name_is_unique()) { $new_name = $resolver->generate_unique_name(); if ($this->allow_catenate) { // If allowed to, silently use the generated name $this->value = $new_name; $this->_orig_value = $new_name; $copy->{$property} = $this->value; } else { $this->validation_error = sprintf($this->_l10n->get('type urlname: name is already taken, try "%s"'), $new_name); return false; } } return true; }
/** * 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; }
/** * Make sure we have unique filename * * @todo Isn't there a reflector method that already does this (for filenames)? */ private function _generate_unique_name($filename) { $attachment = new midcom_db_attachment(); $attachment->name = $filename; $attachment->parentguid = $this->storage->object->guid; $resolver = new midcom_helper_reflector_nameresolver($attachment); if (!$resolver->name_is_unique()) { debug_add("Name '{$attachment->name}' is not unique, trying to generate", MIDCOM_LOG_INFO); $ext = ''; if (preg_match('/^(.*)(\\..*?)$/', $filename, $ext_matches)) { $ext = $ext_matches[2]; } $filename = $resolver->generate_unique_name('name', $ext); } return $filename; }
/** * Imports an item as an event */ private function import_event($item) { // Check that we're trying to import item suitable to be an event if (!isset($item['xcal']) && !isset($item['gd']['when@'])) { // Not an event return false; } // Get start and end times $start = null; $end = null; if (isset($item['xcal']['dtstart'])) { // xCal RSS feed, for example Upcoming or Last.fm $start = strtotime($item['xcal']['dtstart']); } elseif (isset($item['gd']['when@starttime'])) { // gData Atom feed, for example Dopplr $start = strtotime($item['gd']['when@starttime']); } if (isset($item['xcal']['dtend'])) { $end = strtotime($item['xcal']['dtend']); } elseif (isset($item['gd']['when@starttime'])) { $end = strtotime($item['gd']['when@endtime']); } if (!$start || !$end) { return false; } if (!$this->_datamanager) { $schemadb = midcom_helper_datamanager2_schema::load_database($this->_node_config->get('schemadb')); $this->_datamanager = new midcom_helper_datamanager2_datamanager($schemadb); } // TODO: Move to real geocoded stuff $location_parts = array(); if (isset($item['xcal']['x-calconnect-venue_adr_x-calconnect-venue-name'])) { $location_parts[] = $item['xcal']['x-calconnect-venue_adr_x-calconnect-venue-name']; } if (isset($item['xcal']['x-calconnect-venue_adr_x-calconnect-street'])) { $location_parts[] = $item['xcal']['x-calconnect-venue_adr_x-calconnect-street']; } if (isset($item['xcal']['x-calconnect-venue_adr_x-calconnect-city'])) { $location_parts[] = $item['xcal']['x-calconnect-venue_adr_x-calconnect-city']; } if (isset($item['gd']['where@valuestring'])) { $wherevalues = explode(' ', $item['gd']['where@valuestring']); foreach ($wherevalues as $val) { $location_parts[] = $val; } } $qb = net_nemein_calendar_event_dba::new_query_builder(); $qb->add_constraint('node', '=', $this->_feed->node); $qb->add_constraint('extra', '=', md5($item['guid'])); $events = $qb->execute(); if (count($events) > 0) { // This item has been imported already earlier. Update $event = $events[0]; $event->_activitystream_verb = 'http://community-equity.org/schema/1.0/clone'; $event->_rcs_message = sprintf(midcom::get('i18n')->get_string('%s was imported from %s', 'net.nemein.rss'), $event->title, $this->_feed->title); $event->allow_name_catenate = true; if (empty($event->name)) { $resolver = new midcom_helper_reflector_nameresolver($event); // To prevent validation errors in case the auto-catenate is not allowed in the urlname datatype $event->name = $resolver->generate_unique_name(); } } else { $node = new midcom_db_topic($this->_feed->node); $node_lang_code = $node->get_parameter('net.nemein.calendar', 'language'); // This is a new item $event = new net_nemein_calendar_event_dba(); $event->start = $start; $event->end = $end; $event->extra = md5($item['guid']); $event->node = $this->_feed->node; if ($node->get_parameter('net.nemein.calendar', 'symlink_topic') != '') { try { $symlink_topic = new midcom_db_topic($node->get_parameter('net.nemein.calendar', 'symlink_topic')); $event->node = $symlink_topic->id; } catch (midcom_error $e) { $e->log(); } } if ($node_lang_code != '') { $lang_id = midcom::get('i18n')->code_to_id($node_lang_code); $event->lang = $lang_id; } $event->allow_name_catenate = true; $event->title = (string) $item['title']; $event->_activitystream_verb = 'http://community-equity.org/schema/1.0/clone'; $event->_rcs_message = sprintf(midcom::get('i18n')->get_string('%s was imported from %s', 'net.nemein.rss'), $event->title, $this->_feed->title); $resolver = new midcom_helper_reflector_nameresolver($event); $event->name = $resolver->generate_unique_name(); if (!$event->create()) { return false; } } $this->_datamanager->autoset_storage($event); $this->_datamanager->types['start']->value = new DateTime(strftime('%Y-%m-%d %H:%M:%S', $start)); $this->_datamanager->types['end']->value = new DateTime(strftime('%Y-%m-%d %H:%M:%S', $end)); if (is_a($this->_datamanager->types['location'], 'midcom_helper_datamanager2_type_position')) { // Position type, give all values we got, assume order "Street, City, Country" $location_parts = array_reverse($location_parts); if (count($location_parts) > 0) { $country = org_routamc_positioning_country_dba::get_by_name($location_parts[0]); if ($country && $country->code) { $this->_datamanager->types['location']->location->county = $country->code; } } if (count($location_parts) > 1) { $city = org_routamc_positioning_city_dba::get_by_name($location_parts[1]); if ($city && $city->id) { $this->_datamanager->types['location']->location->city = $city->id; } } if (count($location_parts) > 2) { $this->_datamanager->types['location']->location->street = $location_parts[2]; } if (isset($item['gml'])) { $gml_parts = explode(' ', $item['gml']['where_point_pos']); if (count($gml_parts) == 2) { $this->_datamanager->types['location']->location->latitude = (double) $gml_parts[0]; $this->_datamanager->types['location']->location->longitude = (double) $gml_parts[1]; } } } else { // Just give the location string we got $this->_datamanager->types['location']->value = implode(', ', $location_parts); } foreach ($item as $key => $value) { if (isset($this->_datamanager->types[$key])) { $this->_datamanager->types[$key]->value = $value; } } if (!$this->_datamanager->save()) { return false; } // This should be unnecessary but left in place just to be sure if (strlen($this->_datamanager->storage->object->name) == 0) { // Generate something to avoid empty "/" links in case of failures $this->_datamanager->storage->object->name = time(); $this->_datamanager->storage->object->update(); } $this->parse_tags($event, $item, 'description'); $this->parse_parameters($event, $item); return $event->guid; }
* @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 { echo "ERROR: '{$reflected_name}' is NOT unique (among siblings)<br>\n"; } $new_name = $resolver->generate_unique_name(); echo "midcom_helper_reflector_nameresolver::generate_unique_name(\$article) returned '{$new_name}'<br>\n";