/**
* Prints the supplied string to "standard error" (STDERR) instead of the "standard output" (STDOUT) stream
*
* @param string	$source_csv_filename		The CSV import file containing the asset attribute and metadata field specifications
* @param string	$asset_type_code			The Matrix asset type code or the assets which are to be created
* @param object	$parent_id					The asset ID of the parent asset under which the new assets are to reside
* @param int	$schema_id					The asset ID of the Metadata Schema to associate with the new assets
* @param array	$metadata_mapping			A structure containing Supplied Field Name to Metadata Field asset ID associations
* @param array	$attribute_mapping			A structure containing Supplied Field Name to Asset Attribute Name associations
* @param boolean$new_assets_live			When set to TRUE, assets created during the import will be set to 'Live'
* @param string $unique_record_field		The CSV field to be treated as a primary identifier for editing and deletion purposes
* @param string $record_modification_field	The CSV field to determine whether the associated record is to be (A)dded, (E)dited, or (D)eleted
* @param array	$ignore_fields				The fields for the ignoring
*
* @return array
* @access public
*/
function importAssets($source_csv_filename, $asset_type_code, $parent_id, $schema_id, array $metadata_mapping, array $attribute_mapping, $new_assets_live = FALSE, $unique_record_field = '', $record_modification_field = '', array $ignore_fields = array())
{
    $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
    $num_assets_imported = 0;
    $num_assets_modified = 0;
    $num_assets_deleted = 0;
    $csv_fd = fopen($source_csv_filename, 'r');
    if (!$csv_fd) {
        printUsage();
        printStdErr("* The supplied CSV import file was not found\n\n");
        fclose($csv_fd);
        exit(-6);
    }
    $parent_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($parent_id);
    if (!$parent_asset->id) {
        printUsage();
        printStdErr("* The specified parent asset was not found\n\n");
        exit(-7);
    }
    $header_line = TRUE;
    $headers = array();
    $trash = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('trash_folder');
    $root_folder = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('root_folder');
    // Set to true if temporary trash folder is created, where all the assets to be deleted are moved to
    $temp_trash_folder = FALSE;
    while (($data = fgetcsv($csv_fd, 0, ',')) !== FALSE) {
        $num_fields = count($data);
        $asset_spec = array();
        foreach ($data as $key => $val) {
            if ($header_line) {
                $headers[$key] = trim($val);
            } else {
                $asset_spec[$headers[$key]] = $val;
            }
        }
        if ($header_line) {
            $header_line = FALSE;
        } else {
            // If a Record Modification Field was specified, we also require that a Unique Field was specified for the import
            // These two fields must be present in the CSV file for us to Edit and Delete existing assets. Otherwise, we'll just add
            $record_handling = IMPORT_ADD_RECORD;
            if (!empty($unique_record_field) && !empty($record_modification_field) && isset($asset_spec[$unique_record_field]) && isset($asset_spec[$record_modification_field])) {
                $record_modification_state = strtoupper($asset_spec[$record_modification_field]);
                switch ($record_modification_state) {
                    case 'D':
                        $record_handling = IMPORT_DELETE_RECORD;
                        break;
                    case 'E':
                        $record_handling = IMPORT_EDIT_RECORD;
                        break;
                }
                // Okey dokey, let's find the existing asset as we are either performing an (E)dit or (D)elete operation...
                // Also try to find an existing asset as we may be unfortunately performing an (A)dd that matches the unique
                // identifier, in which case we are probably intending to (E)dit the existing matching asset
                $existing_asset_id = 0;
                // Our search is limited to the exact asset type used for the import, and the parent root node (and children) specified
                // The unique field may be either one to be assigned to an attribute or to a metadata field
                $search_field = '';
                $search_value = '';
                if (isset($metadata_mapping[$unique_record_field])) {
                    $search_type = 'metadata';
                    $search_field = $metadata_mapping[$unique_record_field];
                    $search_value = $asset_spec[$unique_record_field];
                }
                if (isset($attribute_mapping[$unique_record_field])) {
                    $search_type = 'attribute';
                    $search_field = $attribute_mapping[$unique_record_field];
                    $search_value = $asset_spec[$unique_record_field];
                }
                $search = array($search_type => array('field' => $search_field, 'value' => $search_value));
                $existing_assets = findAsset($parent_id, $asset_type_code, $search);
                if (count($existing_assets) > 1) {
                    // Multiple matching assets - skip
                    echo "\n*\t* The record for '" . $search_value . "' matched multiple existing assets. Cannot determine how to proceed - continuing to the next record.\n";
                    continue;
                }
                $existing_asset_id = reset($existing_assets);
                // If it is an (E)dit request and the asset was not found, then let's make it an (A)dd instead
                if (empty($existing_assets) && $record_handling == IMPORT_EDIT_RECORD) {
                    echo "\n*\t* The following 'Edit' request for '" . $search_value . "' has been changed to 'Add' as there is not an existing matching asset\n";
                    $record_handling = IMPORT_ADD_RECORD;
                }
                // If it's there and we wanted to (A)dd, then make it an (E)dit instead
                if ($existing_asset_id > 0 && $record_handling == IMPORT_ADD_RECORD) {
                    echo "\n*\t* The following 'Add' request for '" . $search_value . "' has been changed to 'Edit' as this asset already exists.\n";
                    $record_handling = IMPORT_EDIT_RECORD;
                }
                // If it is a (D)elete request and the asset was not found, then skip this record gracefully
                if (empty($existing_assets) && $record_handling == IMPORT_DELETE_RECORD) {
                    echo "\n*\t* Deletion request for asset with unique field value '" . $search_value . "' was aborted due to a missing matching asset. Continuing to the next record.\n";
                    continue;
                }
                if ($record_handling == IMPORT_DELETE_RECORD) {
                    // Deletify
                    echo '- Deleting asset';
                    $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($existing_asset_id);
                    if ($asset) {
                        // Create temporary trash folder, if not already created
                        if (!$temp_trash_folder) {
                            $GLOBALS['SQ_SYSTEM']->am->includeAsset('folder');
                            $temp_trash_folder = new Folder();
                            $temp_trash_folder->setAttrValue('name', 'temp_trash_folder');
                            $link_array = array('asset' => $root_folder, 'value' => '', 'link_type' => SQ_LINK_TYPE_1);
                            $linkid = $temp_trash_folder->create($link_array);
                            // If cannot create the temporary trash folder then we cannot delete any asset
                            if (!$linkid) {
                                echo "\n*\t* Deletion request for asset with unique field value '" . $search_value . "' was aborted due to unable to create temporary trash folder. Continuing to the next record.\n";
                                $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
                                continue;
                            }
                        }
                        // Move the asset to the temporary trash folder
                        $asset_linkid_old = $GLOBALS['SQ_SYSTEM']->am->getLinkByAsset($parent_id, $asset->id);
                        $linkid = $GLOBALS['SQ_SYSTEM']->am->moveLink($asset_linkid_old['linkid'], $temp_trash_folder->id, SQ_LINK_TYPE_1, -1);
                        // If cannot move the asset to temporary trash folder then it cannot be deleted
                        if (!$linkid) {
                            echo "\n*\t* Deletion request for asset with unique field value '" . $search_value . "' was aborted due to unable to move this asset to temporary trash folder. Continuing to the next record.\n";
                            $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
                            continue;
                        }
                        echo $search_value . ',' . $existing_asset_id . ",D\n";
                        $num_assets_deleted++;
                    }
                    // End if asset
                } else {
                    if ($record_handling == IMPORT_EDIT_RECORD) {
                        // Editise
                        // Ok we are editing - has the user specified fields to ignore at this point? If so, let's eliminificate
                        foreach ($ignore_fields as $ignore_field_name => $val) {
                            if (isset($asset_spec[$ignore_field_name])) {
                                unset($asset_spec[$ignore_field_name]);
                            }
                        }
                        echo '- Modifying asset with unique field value';
                        editAsset($existing_asset_id, $asset_spec, $attribute_mapping, $metadata_mapping, $schema_id);
                        echo $search_value . ',' . $existing_asset_id . ",E\n";
                        $num_assets_modified++;
                    }
                }
            }
            if ($record_handling == IMPORT_ADD_RECORD) {
                $asset_info = createAsset($asset_spec, $asset_type_code, $parent_asset, $schema_id, $metadata_mapping, $attribute_mapping);
                $asset_id = 0;
                if (is_array($asset_info)) {
                    $asset_id = reset($asset_info);
                }
                if ($asset_id) {
                    // Ok see if we need to set it live
                    if ($new_assets_live) {
                        $new_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($asset_id);
                        $new_asset->processStatusChange(SQ_STATUS_LIVE);
                        $GLOBALS['SQ_SYSTEM']->am->forgetAsset($new_asset);
                    }
                    echo key($asset_info) . ',' . $asset_id . ",A\n";
                    $num_assets_imported++;
                }
            }
        }
    }
    // End while
    fclose($csv_fd);
    $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
    // Now actually delete all the assets moved to "temporary purge folder" by purging this folder
    if ($temp_trash_folder && $GLOBALS['SQ_SYSTEM']->am->trashAsset($temp_trash_folder->id)) {
        $trash_folder = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('trash_folder');
        $trash_linkid = $GLOBALS['SQ_SYSTEM']->am->getLinkByAsset($trash_folder->id, $temp_trash_folder->id);
        if (isset($trash_linkid['linkid']) && $trash_linkid['linkid'] > 0) {
            $hh = $GLOBALS['SQ_SYSTEM']->getHipoHerder();
            $vars = array('purge_root_linkid' => $trash_linkid['linkid']);
            $errors = $hh->freestyleHipo('hipo_job_purge_trash', $vars);
            if (!empty($errors)) {
                $error_msg = '';
                foreach ($errors as $error) {
                    $error_msg .= ' * ' . $error['message'];
                }
                echo "Following errors occured while deleting asset(s):\n{$error_msg}\n";
            }
        }
    }
    $status_report = array('num_added' => $num_assets_imported, 'num_modified' => $num_assets_modified, 'num_deleted' => $num_assets_deleted);
    return $status_report;
}
    if (!empty($linkid)) {
        // purge trash hipo will know what to do
        $vars['purge_root_linkid'] = $linkid;
    } else {
        printStdErr("Purge root node assetid id #" . $purge_rootnode . " not found\n");
        exit(1);
    }
}
$hh = $GLOBALS['SQ_SYSTEM']->getHipoHerder();
$errors = $hh->freestyleHipo('hipo_job_purge_trash', $vars);
if (!empty($errors)) {
    $error_msg = '';
    foreach ($errors as $error) {
        $error_msg .= ' * ' . $error['message'];
    }
    printStdErr("Following errors occured while deleting asset(s):\n{$error_msg}\n");
    exit(1);
}
/**
* Prints the supplied string to "standard error" (STDERR) instead of the "standard output" (STDOUT) stream
*
* @param string $string The string to write to STDERR
*
* @return void
* @access public
*/
function printStdErr($string)
{
    fwrite(STDERR, "{$string}");
}
//end printStdErr()
/**
* Ensures that the XML mapping contains a correct and complete definition of the target metadata schema
*
* @param array	&$metadata_mapping	The metadata mapping structure which is used to import the CSV data
*
* @return void
* @access public
*/
function validateMetadataMapping(&$metadata_mapping)
{
    // Start Matrix
    printStdErr('- Initialising Matrix...');
    // Check the schema is available in the system
    $schema_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($metadata_mapping['metadata_schema_id']);
    if (!$schema_asset) {
        printStdErr('* The supplied schema (ID: ' . $metadata_mapping['metadata_schema_id'] . ") could not be found in the system\n");
        exit(-21);
    }
    if ($schema_asset->type() != 'metadata_schema') {
        printStdErr('* The supplied schema (ID: ' . $metadata_mapping['metadata_schema_id'] . ") is not a valid Metadata Schema in the system\n");
        exit(-22);
    }
    // Now we have the schema, find its fields and make sure that they were specified
    $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
    $metadata_fields = $mm->getMetadataFields(array($metadata_mapping['metadata_schema_id']));
    // Fields specified in the system...
    $metadata_system_field_ids = array_keys($metadata_fields);
    $metadata_mapping_field_ids = array_values($metadata_mapping['metadata_fields']);
    $metadata_differences = array_diff($metadata_system_field_ids, $metadata_mapping_field_ids);
    if (count($metadata_differences) > 0) {
        printStdErr('* One or more system metadata fields were not specified in the mapping file');
        printStdErr('  The following metadata field IDs are required:');
        foreach ($metadata_differences as $metadata_field_id) {
            printStdErr('  ' . $metadata_field_id);
        }
        exit(-23);
    }
    // All expected metadata fields were defined in the mapping file. Add the data types to our mapping.
    // This will be used while importing to verify that the fields are correct
    $metadata_mapping['metadata_field_types'] = $metadata_fields;
    $am = $GLOBALS['SQ_SYSTEM']->am;
    foreach ($metadata_fields as $metadata_field_id => $metadata_field_type) {
        $metadata_field_type = $metadata_field_type[0]['type_code'];
        $field = $am->getAsset($metadata_field_id, $metadata_field_type);
        $metadata_mapping['metadata_field_objects'][$metadata_field_id] = $field;
        if ($field->attr('required')) {
            if (isset($metadata_mapping['metadata_ignored_fields'][$metadata_field_id])) {
                printStdErr('* The metadata field (ID ' . $metadata_field_id . ') cannot be ignored upon import as it is mandatory');
                exit(-24);
            }
            $metadata_mapping['metadata_required_fields'][$metadata_field_id] = 1;
        }
    }
}
/**
* Assigns a title attribute value to a selected asset
*
* @param int	$asset_id		The asset ID
* @param string	$asset_title	The title to be set for the asset
*
* @return boolean
* @access public
*/
function setAssetTitle($asset_id, $asset_title)
{
    $success = FALSE;
    $asset =& $GLOBALS['SQ_SYSTEM']->am->getAsset($asset_id);
    if ($asset->id) {
        // Make sure that we have a 'title' attribute
        $current_title = $asset->attr('title');
        if ($current_title == NULL) {
            printStdErr('* Asset ID ' . $asset_id . " does not have a title field, so one cannot be assigned.\n");
            printStdErr('User Options ------------');
            printStdErr('(I)gnore this asset and continue with modification of titles');
            printStdErr('(C)ancel modification script');
            $user_choice = getUserInput(array('i', 'c'));
            if ($user_choice == 'c') {
                printStdErr("\n- Titles modification cancelled by user");
                exit(-7);
            }
        } else {
            // Set the 'title' attribute for the asset
            $asset->setAttrValue('title', $asset_title);
            $asset->saveAttributes();
            // Read back the title to ensure that it is set as expected
            $success = $asset->attr('title') == $asset_title;
            if (!$success) {
                printStdErr('* The title "' . $asset_title . '" could not be set for asset ID ' . $asset_id . "\n");
                printStdErr('User Options ------------');
                printStdErr('(I)gnore this asset and continue with modification of titles');
                printStdErr('(C)ancel modification script');
                $user_choice = getUserInput(array('i', 'c'));
                if ($user_choice == 'c') {
                    printStdErr("\n- Titles modification cancelled by user");
                    exit(-8);
                }
            }
        }
    }
    //end if
    return $success;
}
/**
* Creates a Folder asset with the specified name and parent
*
* @param string	$name			The name of the Folder to create
* @param object	&$parent_folder	The parent asset
*
* @return int
* @access public
*/
function createFolder($name, &$parent_folder)
{
    printStdErr('- Creating Folder ' . $name);
    $folder = new Folder();
    printStdErr('.');
    // Set Folder name
    $folder->setAttrValue('name', $name);
    printStdErr('.');
    // Link the new asset under the parent folder
    $link_id = createLink($parent_folder, $folder);
    printStdErr('.');
    // Free memory
    $GLOBALS['SQ_SYSTEM']->am->forgetAsset($folder);
    printStdErr(' => asset ID ' . $folder->id . "\n");
    echo formatCSVValue($name) . ',' . $folder->id;
    return $folder->id;
}