/** * Imports data for the personalinformation artefact type, by looking for * it in the persondata element */ public static function import_author_data(PluginImport $importer, $persondataid) { if ($persondataid) { $composites = array(); $person = $importer->get_entry_by_id($persondataid); $namespaces = $importer->get_namespaces(); $ns = $importer->get_leap2a_namespace(); $persondata = $person->xpath($namespaces[$ns] . ':persondata'); foreach ($persondata as $item) { $leapattributes = PluginImportLeap::get_attributes($item, $ns); if (!isset($leapattributes['field'])) { // 'Field' is required // http://wiki.cetis.ac.uk/2009-03/Leap2A_personal_data#field $importer->trace('WARNING: persondata element did not have leap2:field attribute'); continue; } if ($leapattributes['field'] == 'dob') { $composites['dateofbirth'] = (string) $item; } if ($leapattributes['field'] == 'gender') { $gender = (string) $item; if ($gender == '1') { $composites['gender'] = 'male'; } else { if ($gender == '2') { $composites['gender'] = 'female'; } else { $importer->trace('WARNING: gender found but not male or female - no gender stored for this user'); } } } $maharaattributes = PluginImportLeap::get_attributes($item, PluginImportLeap::NS_MAHARA); if (isset($maharaattributes['field'])) { if (in_array($maharaattributes['field'], array('placeofbirth', 'citizenship', 'visastatus', 'maritalstatus'))) { $composites[$maharaattributes['field']] = (string) $item; } } } if ($composites) { $importer->trace('Resume personal information:'); $importer->trace($composites); $artefact = new ArtefactTypePersonalinformation(0, array('owner' => $importer->get('usr'))); foreach ($composites as $key => $value) { $artefact->set_composite($key, $value); } $artefact->commit(); } } }
/** * Logic for processing an $entry into a Mahara view. * @param SimpleXMLElement $entry * @return array An array of data that can be used to create the view, or store it into an import_request */ private function get_mahara_view_entry_data(SimpleXMLElement $entry) { static $blocktypes_installed = null; static $columnlayouts = null; static $viewtypes = null; $viewelement = $entry->xpath('mahara:view[1]'); if (count($viewelement) != 1) { // This isn't a Mahara view return false; } if (is_null($columnlayouts)) { $columnlayouts = get_records_assoc('view_layout_columns'); } if (is_null($viewtypes)) { $viewtypes = get_column('view_type', 'type'); } $maharaattributes = PluginImportLeap::get_attributes($viewelement[0], PluginImportLeap::NS_MAHARA); $type = 'portfolio'; if (isset($maharaattributes['type']) && in_array($maharaattributes['type'], $viewtypes)) { $type = $maharaattributes['type']; } $ownerformat = intval($maharaattributes['ownerformat']); if (!$ownerformat) { $ownerformat = FORMAT_NAME_DISPLAYNAME; } $rows = $entry->xpath('mahara:view[1]/mahara:row'); $rowcount = count($rows); // A flag that indicates whether this is an old-style one-row layout, or a new-style multi-row layout $onerowlayout = false; if ($rowcount < 1 || $rowcount > View::$maxlayoutrows) { // Check for a pre-1.8 (one-row) layout $columns = $entry->xpath('mahara:view[1]/mahara:column'); $columncount = count($columns); if ($columncount < 1 || $columncount > 5) { // Whoops, invalid number of rows $this->trace("Invalid number of rows specified for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); return false; } else { $onerowlayout = true; $rows = array($columns); $rowcount = 1; } } $layout = null; if (isset($maharaattributes['layout'])) { $rowwidths = explode('-', $maharaattributes['layout']); if (count($rowwidths) != $rowcount) { $this->trace("Row widths and number of rows do not match for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); return false; } $columnids = array(); foreach ($columnlayouts as $columnlayout) { foreach ($rowwidths as $key => $widths) { if ($columnlayout->widths == $widths) { $columnids[$key + 1] = $columnlayout->id; } } } if (count($columnids) != $rowcount) { $this->trace("Invalid row widths were specified for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); return false; } $rowscolssql = ''; for ($i = 0; $i < count($columnids); $i++) { $rowscolssql .= '(row = ' . ($i + 1) . ' AND columns = ' . $columnids[$i + 1] . ')'; if ($i != count($columnids) - 1) { $rowscolssql .= ' OR '; } } // search in default layout options for a match // this will return first possible match with exact match (if any) // at front of possibles. More than one possible match can occur // if there are 3 or more rows in leap2a layout and 2 of those rows // match more than one possible view layout. $sql = 'SELECT vlrc.viewlayout AS id FROM {view_layout} vl INNER JOIN {view_layout_rows_columns} vlrc ON vl.id = vlrc.viewlayout INNER JOIN ( SELECT viewlayout, COUNT(*) FROM {view_layout_rows_columns} GROUP BY viewlayout HAVING COUNT(*) = ? ) vlrc2 ON vlrc.viewlayout = vlrc2.viewlayout INNER JOIN {usr_custom_layout} ucl ON ucl.layout = vl.id WHERE (' . $rowscolssql . ') AND ( vl.iscustom = 0 OR ( vl.iscustom = 1 AND ucl.usr = ? AND ucl.group IS NULL AND ucl.institution IS NULL ) ) GROUP BY vlrc.viewlayout HAVING count(*) = ? LIMIT 1'; $layout = get_record_sql($sql, array($rowcount, $this->get('usr'), $rowcount)); if (!$layout) { require_once get_config('docroot') . 'lib/layoutpreviewimage.php'; // No existing layout matches their page. This probably means they used a custom layout. So, create a new custom layout for them. // First check to see whether the custom layout they're using is acceptable in our system // TODO: A clever way to squeeze their page into one of the standard layouts if it isn't acceptable. // Maybe just put everything into a one-column layout and let them rearrange it? if (count($rowwidths) < 1 || count($rowwidths) > View::$maxlayoutrows) { $this->trace("Invalid layout specified for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); return false; } $i = 1; $layoutdata = array(); $layoutdata['numrows'] = count($rowwidths); foreach ($rowwidths as $row) { // First, check to see whether this row matches a valid row layout in the DB $rowcolid = get_field('view_layout_columns', 'id', 'widths', $row); if (!$rowcolid) { $this->trace("Invalid layout specified for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); return false; } // Data to help us generate the layout $layoutdata["row{$i}"] = $rowcolid; $i++; } // Now that we know the layout is valid, generate a record and a thumbnail image for it. db_begin(); // An empty view object, since this view isn't present in the DB yet. We need this in order to access the layout methods $viewobj = new View(0, array('owner' => $this->get('usr'), 'deleted' => true)); $layoutresult = $viewobj->addcustomlayout($layoutdata); if (empty($layoutresult['layoutid'])) { $this->trace("Invalid layout specified for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); db_rollback(); return false; } $layout = (object) array('id' => $layoutresult['layoutid']); db_commit(); } } // Extract the view description in the entry 'summary' // A description may be wrapped in XHTML div // See more PluginExportLeap::parse_xhtmlish_content() $description = ''; if ((string) $entry->summary['type'] === 'xhtml' || (string) $entry->summary['type'] === 'html') { $summaryelements = (string) $entry->summary['type'] === 'xhtml' && $entry->summary->div->div ? $entry->summary->div->div : $entry->summary; $summarychildren = $summaryelements->children(); foreach ($summarychildren as $c) { $description .= $c->asXML(); } } else { $description = (string) $entry->summary; } $config = array('title' => (string) $entry->title, 'description' => $description, 'type' => $type, 'layout' => $layout->id, 'tags' => self::get_entry_tags($entry), 'numrows' => $rowcount, 'owner' => $this->get('usr'), 'ownerformat' => $ownerformat); $rowindex = 1; foreach ($rows as $row) { // If this is the old one-row layout, we'll have handled that earlier, and have the one row's columns be in $columns if (!$onerowlayout) { $columns = $row->xpath('mahara:column'); $columncount = count($columns); if ($columncount < 1 || $columncount > 5) { // Whoops, invalid number of columns $this->trace("Invalid number of columns specified for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); return false; } } $colindex = 1; foreach ($columns as $column) { $blockinstances = $column->xpath('mahara:blockinstance'); $order = 1; $config['rows'][$rowindex]['columns'][$colindex] = array(); foreach ($blockinstances as $blockinstance) { $attrs = self::get_attributes($blockinstance, PluginImportLeap::NS_MAHARA); if (!isset($attrs['blocktype'])) { $this->trace(" No mahara:blocktype attribute set for blockinstance at row {$rowindex} col {$colindex}, order {$order}: skipping"); continue; } $this->trace(" Found block with type {$attrs['blocktype']} at [{$rowindex}][{$colindex}][{$order}]", self::LOG_LEVEL_VERBOSE); if ($blocktypes_installed === null) { $blocktypes_installed = array_map(create_function('$a', 'return $a->name;'), plugins_installed('blocktype')); } if (in_array($attrs['blocktype'], $blocktypes_installed)) { $configelements = $blockinstance->xpath('mahara:*'); $config['rows'][$rowindex]['columns'][$colindex][$order] = array('type' => $attrs['blocktype'], 'title' => $attrs['blocktitle'], 'config' => array()); foreach ($configelements as $element) { $value = json_decode((string) $element); if (is_array($value) && isset($value[0])) { $config['rows'][$rowindex]['columns'][$colindex][$order]['config'][$element->getName()] = $value[0]; } else { $this->trace(" Value for {$element->getName()} is not an array, ignoring (value follows below)"); $this->trace($value); } } $order++; } else { $this->trace(" Ignoring unknown blocktype {$attrs['blocktype']}"); } } $colindex++; } // cols $rowindex++; } //rows return $config; }
/** * Get name fields from imported namedata */ private static function get_namefields(PluginImportLeap $importer, array $persondata) { $namefields = array('full_name' => false, 'legal_family_name' => false, 'legal_given_name' => false, 'preferred_family_name' => false, 'preferred_given_name' => false, 'family_name_first' => false, 'name_prefix' => false, 'name_suffix' => false); foreach ($persondata as $item) { $leapattributes = PluginImportLeap::get_attributes($item, $importer->get_leap2a_namespace()); if (isset($leapattributes['field'])) { if (in_array($leapattributes['field'], array_keys($namefields))) { // legal_given_name is allowed to occur any number of times if ($leapattributes['field'] == 'legal_given_name' && $namefields['legal_given_name'] != '') { $namefields['legal_given_name'] .= ' ' . (string) $item; } else { $namefields[$leapattributes['field']] = (string) $item; } } } else { // 'Field' is required // http://wiki.cetis.ac.uk/2009-03/Leap2A_personal_data#field $importer->trace('WARNING: persondata element did not have leap:field attribute'); continue; } } $familynamefirst = $namefields['family_name_first'] == 'yes' ? true : false; // Try to guess reasonable values for first/last names if they're not set if ($namefields['legal_given_name'] === false && $namefields['preferred_given_name'] !== false) { $namefields['legal_given_name'] = $namefields['preferred_given_name']; } if ($namefields['legal_family_name'] === false && $namefields['preferred_family_name'] !== false) { $namefields['legal_family_name'] = $namefields['preferred_family_name']; } // This is _an_ algorithm for parsing this info, I'm not saying it's the _best_ one ;) if ($familynamefirst) { $firstname = (string) $namefields['legal_given_name'] . ' ' . (string) $namefields['name_suffix']; $lastname = (string) $namefields['name_prefix'] . (string) $namefields['legal_family_name']; $preferredname = (string) $namefields['preferred_family_name'] . ' ' . (string) $namefields['preferred_given_name']; } else { $firstname = (string) $namefields['name_prefix'] . ' ' . (string) $namefields['legal_given_name']; $lastname = (string) $namefields['legal_family_name'] . ' ' . (string) $namefields['name_suffix']; $preferredname = (string) $namefields['preferred_given_name'] . ' ' . (string) $namefields['preferred_family_name']; } return array('firstname' => trim($firstname), 'lastname' => trim($lastname), 'preferredname' => trim($preferredname)); }
/** * Attempts to import an entry as a mahara view * * We look for custom mahara namespaced tags that explain the View * structure. If they're present, we use them to create a View using that * structure. * * This differs a bit from the Leap2A specification, but we do so * deliberately to get 100% compatibility with Mahara to Mahara exports. * Other systems can also construct content in the right format to trigger * Mahara to import things as a full view. * * If the mahara tags are not present, we give up. * * @param SimpleXMLElement $entry The entry to be imported * @return boolean Whether it could be imported. */ private function import_entry_as_mahara_view(SimpleXMLElement $entry) { static $blocktypes_installed = null; static $viewlayouts = null; static $viewtypes = null; $viewelement = $entry->xpath('mahara:view[1]'); if (count($viewelement) != 1) { // This isn't a Mahara view return false; } if (is_null($viewlayouts)) { $viewlayouts = get_records_assoc('view_layout', '', '', '', 'widths, id'); } if (is_null($viewtypes)) { $viewtypes = get_column('view_type', 'type'); } $maharaattributes = PluginImportLeap::get_attributes($viewelement[0], PluginImportLeap::NS_MAHARA); $layout = null; if (isset($viewlayouts[$maharaattributes['layout']])) { $layout = $viewlayouts[$maharaattributes['layout']]->id; } $type = 'portfolio'; if (isset($maharaattributes['type']) && in_array($maharaattributes['type'], $viewtypes)) { $type = $maharaattributes['type']; } $ownerformat = intval($maharaattributes['ownerformat']); if (!$ownerformat) { $ownerformat = FORMAT_NAME_DISPLAYNAME; } $columns = $entry->xpath('mahara:view[1]/mahara:column'); $columncount = count($columns); if ($columncount < 1 || $columncount > 5) { // Whoops, invalid number of columns $this->trace("Invalid number of columns specified for potential view {$entry->id}, falling back to standard import", self::LOG_LEVEL_VERBOSE); return false; } $config = array('title' => (string) $entry->title, 'description' => (string) $entry->summary, 'type' => $type, 'layout' => $layout, 'tags' => self::get_entry_tags($entry), 'numcolumns' => $columncount, 'owner' => $this->get('usr'), 'ownerformat' => $ownerformat); $col = 1; foreach ($columns as $column) { $blockinstances = $column->xpath('mahara:blockinstance'); $row = 1; $config['columns'][$col] = array(); foreach ($blockinstances as $blockinstance) { $attrs = self::get_attributes($blockinstance, PluginImportLeap::NS_MAHARA); if (!isset($attrs['blocktype'])) { $this->trace(" No mahara:blocktype attribute set for blockinstance at col {$col}, row {$row}: skipping"); continue; } $this->trace(" Found block with type {$attrs['blocktype']} at [{$col}][{$row}]", self::LOG_LEVEL_VERBOSE); if ($blocktypes_installed === null) { $blocktypes_installed = array_map(create_function('$a', 'return $a->name;'), plugins_installed('blocktype')); } if (in_array($attrs['blocktype'], $blocktypes_installed)) { $configelements = $blockinstance->xpath('mahara:*'); $config['columns'][$col][$row] = array('type' => $attrs['blocktype'], 'title' => $attrs['blocktitle'], 'config' => array()); foreach ($configelements as $element) { $value = json_decode((string) $element); if (is_array($value) && isset($value[0])) { $config['columns'][$col][$row]['config'][$element->getName()] = $value[0]; } else { $this->trace(" Value for {$element->getName()} is not an array, ignoring (value follows below)"); $this->trace($value); } } $row++; } else { $this->trace(" Ignoring unknown blocktype {$attrs['blocktype']}"); } } $col++; } $view = View::import_from_config($this->rewrite_artefact_ids($config), $this->get('usr'), 'leap'); if ($published = strtotime((string) $entry->published)) { $view->set('ctime', $published); } if ($updated = strtotime((string) $entry->updated)) { $view->set('mtime', $updated); } $view->set('owner', $this->get('usr')); $view->commit(); $this->viewids[(string) $entry->id] = $view->get('id'); return true; }