/** * Validates whether the gallery can be saved */ function validation() { // If a title is present, we can auto-populate some other properties if (isset($this->object->title)) { // If no name is present, use the title to generate one if (!isset($this->object->name)) { $this->object->name = sanitize_file_name(sanitize_title($this->object->title)); $this->object->name = apply_filters('ngg_gallery_name', $this->object->name); } // If no slug is set, use the title to generate one if (!isset($this->object->slug)) { $this->object->slug = nggdb::get_unique_slug(sanitize_title($this->object->title), 'gallery'); } } // Set what will be the path to the gallery if (empty($this->object->path)) { $storage = $this->object->get_registry()->get_utility('I_Gallery_Storage'); $this->object->path = $storage->get_upload_relpath($this->object); unset($storage); } $this->object->validates_presence_of('title'); $this->object->validates_presence_of('name'); $this->object->validates_uniqueness_of('slug'); $this->object->validates_numericality_of('author'); return $this->object->is_valid(); }
/** * Sets the defaults for an album * @param C_DataMapper_Model|C_Album|stdClass $entity */ function set_defaults($entity) { $this->object->_set_default_value($entity, 'name', ''); $this->object->_set_default_value($entity, 'albumdesc', ''); $this->object->_set_default_value($entity, 'sortorder', array()); $this->object->_set_default_value($entity, 'previewpic', 0); $this->object->_set_default_value($entity, 'exclude', 0); $this->object->_set_default_value($entity, 'slug', nggdb::get_unique_slug(sanitize_title($entity->name), 'album')); }
function media_upload_nextgen_save_image() { global $wpdb; check_admin_referer('ngg-media-form'); if (!empty($_POST['image'])) { foreach ($_POST['image'] as $image_id => $image) { // create a unique slug $image_slug = nggdb::get_unique_slug(sanitize_title($image['alttext']), 'image'); $wpdb->query($wpdb->prepare("UPDATE {$wpdb->nggpictures} SET image_slug= '%s', alttext= '%s', description = '%s' WHERE pid = %d", $image_slug, $image['alttext'], $image['description'], $image_id)); wp_cache_delete($image_id, 'ngg_image'); } } }
function add_gallery_gid($gid = '', $title = '', $path = '', $description = '', $pageid = 0, $previewpic = 0, $author = 0 ) { global $wpdb; $slug = nggdb::get_unique_slug( sanitize_title( $title ), 'gallery' ); if ( false === $wpdb->query( $wpdb->prepare( "INSERT INTO $wpdb->nggallery ( gid, name, slug, path, title, galdesc, pageid, previewpic, author ) VALUES ( %d, %s, %s, %s, %s, %s, %d, %d, %d)", $gid, $slug, $slug, $path, $title, $description, $pageid, $previewpic, $author ))) { return false; } $galleryID = (int) $wpdb->insert_id; //and give me the new id return $galleryID; }
function update_pictures() { global $wpdb, $nggdb; //TODO:Error message when update failed $description = isset($_POST['description']) ? $_POST['description'] : array(); $alttext = isset($_POST['alttext']) ? $_POST['alttext'] : array(); $exclude = isset($_POST['exclude']) ? $_POST['exclude'] : false; $taglist = isset($_POST['tags']) ? $_POST['tags'] : false; $pictures = isset($_POST['pid']) ? $_POST['pid'] : false; if (is_array($pictures)) { foreach ($pictures as $pid) { $image = $nggdb->find_image($pid); if ($image) { // description field $image->description = $description[$image->pid]; // only uptade this field if someone change the alttext if ($image->alttext != $alttext[$image->pid]) { $image->alttext = $alttext[$image->pid]; $image->image_slug = nggdb::get_unique_slug(sanitize_title($image->alttext), 'image', $image->pid); } // set exclude flag if (is_array($exclude)) { $image->exclude = array_key_exists($image->pid, $exclude) ? 1 : 0; } else { $image->exclude = 0; } // update the database $wpdb->query($wpdb->prepare("UPDATE {$wpdb->nggpictures} SET image_slug = '%s', alttext = '%s', description = '%s', exclude = %d WHERE pid = %d", $image->image_slug, $image->alttext, $image->description, $image->exclude, $image->pid)); // remove from cache wp_cache_delete($image->pid, 'ngg_image'); // hook for other plugins after image is updated do_action('ngg_image_updated', $image); } } } //TODO: This produce 300-400 queries ! if (is_array($taglist)) { foreach ($taglist as $key => $value) { $tags = explode(',', $value); wp_set_object_terms($key, $tags, 'ngg_tag'); } } return; }
function set_defaults($entity) { // If not set already, we'll add an exclude property. This is used // by NextGEN Gallery itself, as well as the Attach to Post module $this->object->_set_default_value($entity, 'exclude', 0); // Ensure that the object has a description attribute $this->object->_set_default_value($entity, 'description', ''); // If not set already, set a default sortorder $this->object->_set_default_value($entity, 'sortorder', 0); // The imagedate must be set if (!isset($entity->imagedate) or is_null($entity->imagedate) or $entity->imagedate == '0000-00-00 00:00:00') { $entity->imagedate = date("Y-m-d H:i:s"); } // If a filename is set, and no alttext is set, then set the alttext // to the basename of the filename (legacy behavior) if (isset($entity->filename)) { $path_parts = pathinfo($entity->filename); $alttext = !isset($path_parts['filename']) ? substr($path_parts['basename'], 0, strpos($path_parts['basename'], '.')) : $path_parts['filename']; $this->object->_set_default_value($entity, 'alttext', $alttext); } // Set unique slug if (isset($entity->alttext) && !isset($entity->image_slug)) { $entity->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($entity->alttext), 'image'); } // Ensure that the exclude parameter is an integer or boolean-evaluated // value if (is_string($entity->exclude)) { $entity->exclude = intval($entity->exclude); } // Trim alttext and description $entity->description = trim($entity->description); $entity->alttext = trim($entity->alttext); }
/** * Add an gallery to the database * * @since V1.7.0 * @param (optional) string $title or name of the gallery * @param (optional) string $path * @param (optional) string $description * @param (optional) int $pageid * @param (optional) int $previewpic * @param (optional) int $author * @return bool|int result of the ID of the inserted gallery */ static function add_gallery($title = '', $path = '', $description = '', $pageid = 0, $previewpic = 0, $author = 0) { global $wpdb; // slug must be unique, we use the title for that $slug = nggdb::get_unique_slug(sanitize_title($title), 'gallery'); // Note : The field 'name' is deprecated, it's currently kept only for compat reason with older shortcodes, we copy the slug into this field if (false === $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->nggallery} (name, slug, path, title, galdesc, pageid, previewpic, author)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t VALUES (%s, %s, %s, %s, %s, %d, %d, %d)", $slug, $slug, $path, $title, $description, $pageid, $previewpic, $author))) { return false; } $galleryID = (int) $wpdb->insert_id; //and give me the new id return $galleryID; }
/** * This rebuild the slugs for albums, galleries and images as ajax routine, max 50 elements per request * * @since 1.7.0 * @return string '1' */ function ngg_ajax_rebuild_unique_slugs() { global $wpdb; $action = $_POST['_action']; $offset = (int) $_POST['offset']; switch ($action) { case 'images': $images = $wpdb->get_results("SELECT * FROM {$wpdb->nggpictures} ORDER BY pid ASC LIMIT {$offset}, 50", OBJECT_K); if (is_array($images)) { foreach ($images as $image) { //slug must be unique, we use the alttext for that $image->slug = nggdb::get_unique_slug(sanitize_title($image->alttext), 'image'); $wpdb->query($wpdb->prepare("UPDATE {$wpdb->nggpictures} SET image_slug= '%s' WHERE pid = '%d'", $image->slug, $image->pid)); } } break; case 'gallery': $galleries = $wpdb->get_results("SELECT * FROM {$wpdb->nggallery} ORDER BY gid ASC LIMIT {$offset}, 50", OBJECT_K); if (is_array($galleries)) { foreach ($galleries as $gallery) { //slug must be unique, we use the title for that $gallery->slug = nggdb::get_unique_slug(sanitize_title($gallery->title), 'gallery'); $wpdb->query($wpdb->prepare("UPDATE {$wpdb->nggallery} SET slug= '%s' WHERE gid = '%d'", $gallery->slug, $gallery->gid)); } } break; case 'album': $albumlist = $wpdb->get_results("SELECT * FROM {$wpdb->nggalbum} ORDER BY id ASC LIMIT {$offset}, 50", OBJECT_K); if (is_array($albumlist)) { foreach ($albumlist as $album) { //slug must be unique, we use the name for that $album->slug = nggdb::get_unique_slug(sanitize_title($album->name), 'album'); $wpdb->query($wpdb->prepare("UPDATE {$wpdb->nggalbum} SET slug= '%s' WHERE id = '%d'", $album->slug, $album->id)); } } break; } die(1); }
function update_album() { global $wpdb, $nggdb; check_admin_referer('ngg_thickbox_form'); if (!nggGallery::current_user_can('NextGEN Edit album settings')) { wp_die(__('Cheatin’ uh?')); } $name = $_POST['album_name']; $desc = $_POST['album_desc']; $prev = (int) $_POST['previewpic']; $link = (int) $_POST['pageid']; // slug must be unique, we use the title for that $slug = nggdb::get_unique_slug(sanitize_title($name), 'album', $this->currentID); $result = $wpdb->query($wpdb->prepare("UPDATE {$wpdb->nggalbum} SET slug= '%s', name= '%s', albumdesc= '%s', previewpic= %d, pageid= %d WHERE id = '%d'", $slug, $name, $desc, $prev, $link, $this->currentID)); //hook for other plugin to update the fields do_action('ngg_update_album', $this->currentID, $_POST); if ($result) { nggGallery::show_message(__('Update Successfully', 'nggallery')); } }
/** * Copies (or moves) images into another gallery * * @param array $images * @param int|object $gallery * @param boolean $db optionally only copy the image files * @param boolean $move move the image instead of copying * @return mixed NULL on failure, array|image-ids on success */ public function copy_images($images, $gallery, $db = TRUE, $move = FALSE) { $new_image_pids = array(); // the return value // legacy requires passing just a numeric ID if (is_numeric($gallery)) { $gallery = $this->object->_gallery_mapper->find($gallery); } // move_images() is a wrapper to this function so we implement both features here $func = $move ? 'rename' : 'copy'; // legacy allows for arrays of just the ID if (!is_array($images)) { $images = array($images); } // Ensure we have a valid gallery $gallery_id = $this->object->_get_gallery_id($gallery); if (!$gallery_id) { return array(); } $image_key = $this->object->_image_mapper->get_primary_key_column(); $gallery_abspath = $this->object->get_gallery_abspath($gallery); // Check for folder permission if (!is_dir($gallery_abspath) && !wp_mkdir_p($gallery_abspath)) { echo sprintf(__('Unable to create directory %s.', 'nggallery'), esc_html($gallery_abspath)); return $new_image_pids; } if (!is_writable($gallery_abspath)) { echo sprintf(__('Unable to write to directory %s. Is this directory writable by the server?', 'nggallery'), esc_html($gallery_abspath)); return $new_image_pids; } foreach ($images as $image) { if ($this->object->is_current_user_over_quota()) { throw new E_NoSpaceAvailableException(__('Sorry, you have used your space allocation. Please delete some files to upload more files.', 'nggallery')); } // again legacy requires that it be able to pass just a numeric ID if (is_numeric($image)) { $image = $this->object->_image_mapper->find($image); } $old_pid = $image->{$image_key}; // update the DB if requested $new_image = clone $image; $new_pid = $old_pid; if ($db) { unset($new_image->extras_post_id); $new_image->galleryid = $gallery_id; if (!$move) { $new_image->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($image->alttext), 'image'); unset($new_image->{$image_key}); } $new_pid = $this->object->_image_mapper->save($new_image); } if (!$new_pid) { echo sprintf(__('Failed to copy database row for picture %s', 'nggallery'), $old_pid) . '<br />'; continue; } // Copy each image size foreach ($this->object->get_image_sizes() as $size) { // if backups are off there's no backup file to copy if (!C_NextGen_Settings::get_instance()->imgBackup && $size == 'backup') { continue; } $orig_path = $this->object->get_image_abspath($image, $size, TRUE); if (!$orig_path || !@file_exists($orig_path)) { echo sprintf(__('Failed to get image path for %s', 'nggallery'), esc_html(M_I18n::mb_basename($orig_path))) . '<br/>'; continue; } $new_path = $this->object->get_image_abspath($new_image, $size, FALSE); // Prevent duplicate filenames: check if the filename exists and begin appending '-#' if (!ini_get('safe_mode') && @file_exists($new_path)) { // prevent get_image_abspath() from using the thumbnail filename in metadata unset($new_image->meta_data['thumbnail']['filename']); $file_exists = TRUE; $i = 0; do { $i++; $parts = explode('.', $image->filename); $extension = array_pop($parts); $tmp_filename = implode('.', $parts) . '-' . $i . '.' . $extension; $new_image->filename = $tmp_filename; $tmp_path = $this->object->get_image_abspath($new_image, $size, FALSE); if (!@file_exists($tmp_path)) { $file_exists = FALSE; $new_path = $tmp_path; if ($db) { $this->object->_image_mapper->save($new_image); } } } while ($file_exists == TRUE); } // Copy files if (!@$func($orig_path, $new_path)) { echo sprintf(__('Failed to copy image %1$s to %2$s', 'nggallery'), esc_html($orig_path), esc_html($new_path)) . '<br/>'; continue; } // disabling: this is a bit too verbose // if (!empty($tmp_path)) // echo sprintf(__('Image %1$s (%2$s) copied as image %3$s (%4$s) » The file already existed in the destination gallery.', 'nggallery'), $old_pid, esc_html($orig_path), $new_pid, esc_html($new_path)) . '<br />'; // else // echo sprintf(__('Image %1$s (%2$s) copied as image %3$s (%4$s)', 'nggallery'), $old_pid, esc_html($orig_path), $new_pid, esc_html($new_path)) . '<br />'; // Copy tags if ($db) { $tags = wp_get_object_terms($old_pid, 'ngg_tag', 'fields=ids'); $tags = array_map('intval', $tags); wp_set_object_terms($new_pid, $tags, 'ngg_tag', true); } } $new_image_pids[] = $new_pid; } $title = '<a href="' . admin_url() . 'admin.php?page=nggallery-manage-gallery&mode=edit&gid=' . $gallery_id . '" >'; $title .= $gallery->title; $title .= '</a>'; echo '<hr/>' . sprintf(__('Copied %1$s picture(s) to gallery %2$s .', 'nggallery'), count($new_image_pids), $title); return $new_image_pids; }
function import_gallery_from_fs($abspath, $gallery_id = FALSE, $move_files = TRUE) { $retval = FALSE; if (@file_exists($abspath)) { // Ensure that this folder has images $files_all = scandir($abspath); $files = array(); // first perform some filtering on file list foreach ($files_all as $file) { if ($file == '.' || $file == '..') { continue; } $files[] = $file; } if (!empty($files)) { // Get needed utilities $fs = $this->get_registry()->get_utility('I_Fs'); $gallery_mapper = $this->get_registry()->get_utility('I_Gallery_Mapper'); // Sometimes users try importing a directory, which actually has all images under another directory $first_file_abspath = $fs->join_paths($abspath, $files[0]); if (is_dir($first_file_abspath) && count($files) == 1) { return $this->import_gallery_from_fs($first_file_abspath, $gallery_id, $move_files); } // If no gallery has been specified, then use the directory name as the gallery name if (!$gallery_id) { // Create the gallery $gallery = $gallery_mapper->create(array('title' => basename($abspath))); if (!$move_files) { $gallery->path = str_ireplace(ABSPATH, '', $abspath); } // Save the gallery if ($gallery->save()) { $gallery_id = $gallery->id(); } } // Ensure that we have a gallery id if ($gallery_id) { $retval = array('gallery_id' => $gallery_id, 'image_ids' => array()); foreach ($files as $file) { if (!preg_match("/\\.(jpg|jpeg|gif|png)/i", $file)) { continue; } $file_abspath = $fs->join_paths($abspath, $file); $image = null; if ($move_files) { $image = $this->object->upload_base64_image($gallery_id, file_get_contents($file_abspath), str_replace(' ', '_', $file)); } else { // Create the database record ... TODO cleanup, some duplication here from upload_base64_image $factory = $this->object->get_registry()->get_utility('I_Component_Factory'); $image = $factory->create('image'); $image->alttext = sanitize_title_with_dashes(basename($file_abspath, '.' . pathinfo($file_abspath, PATHINFO_EXTENSION))); $image->galleryid = $this->object->_get_gallery_id($gallery_id); $image->filename = basename($file_abspath); $image->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($image->alttext), 'image'); $image_key = $this->object->_image_mapper->get_primary_key_column(); $abs_filename = $file_abspath; if ($image_id = $this->object->_image_mapper->save($image)) { try { // backup and image resizing should have already been performed, better to avoid # if ($settings->imgBackup) # $this->object->backup_image($image); # if ($settings->imgAutoResize) # $this->object->generate_image_clone( # $abs_filename, # $abs_filename, # $this->object->get_image_size_params($image_id, 'full') # ); // Ensure that fullsize dimensions are added to metadata array $dimensions = getimagesize($abs_filename); $full_meta = array('width' => $dimensions[0], 'height' => $dimensions[1]); if (!isset($image->meta_data) or is_string($image->meta_data) && strlen($image->meta_data) == 0) { $image->meta_data = array(); } $image->meta_data = array_merge($image->meta_data, $full_meta); $image->meta_data['full'] = $full_meta; // Generate a thumbnail for the image $this->object->generate_thumbnail($image); // Set gallery preview image if missing $this->object->get_registry()->get_utility('I_Gallery_Mapper')->set_preview_image($gallery, $image_id, TRUE); // Notify other plugins that an image has been added do_action('ngg_added_new_image', $image); // delete dirsize after adding new images delete_transient('dirsize_cache'); // Seems redundant to above hook. Maintaining for legacy purposes do_action('ngg_after_new_images_added', $gallery_id, array($image->{$image_key})); } catch (Exception $ex) { throw new E_InsufficientWriteAccessException(FALSE, $abs_filename, FALSE, $ex); } } else { throw new E_InvalidEntityException(); } } $retval['image_ids'][] = $image->{$image->id_field}; } // Add the gallery name to the result $gallery = $gallery_mapper->find($gallery_id); $retval['gallery_name'] = $gallery->title; unset($gallery); } } } return $retval; }
/** * This rebuild the slugs for albums, galleries and images as ajax routine, max 50 elements per request * * @since 1.7.0 * @return string '1' */ function ngg_ajax_rebuild_unique_slugs() { global $wpdb; // check for correct NextGEN capability if ( !current_user_can('NextGEN Change options') ) die('No access'); $action = $_POST['_action']; $offset = (int) $_POST['offset']; switch ($action) { case 'images': $images = $wpdb->get_results("SELECT * FROM $wpdb->nggpictures ORDER BY pid ASC LIMIT $offset, 50", OBJECT_K); if ( is_array($images) ) { foreach ($images as $image) { //slug must be unique, we use the alttext for that $image->slug = nggdb::get_unique_slug( sanitize_title( $image->alttext ), 'image', $image->pid ); $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->nggpictures SET image_slug= '%s' WHERE pid = '%d'" , $image->slug, $image->pid ) ); } } break; case 'gallery': $galleries = $wpdb->get_results("SELECT * FROM $wpdb->nggallery ORDER BY gid ASC LIMIT $offset, 50", OBJECT_K); if ( is_array($galleries) ) { foreach ($galleries as $gallery) { //slug must be unique, we use the title for that $gallery->slug = nggdb::get_unique_slug( sanitize_title( $gallery->title ), 'gallery', $gallery->gid ); $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->nggallery SET slug= '%s' WHERE gid = '%d'" , $gallery->slug, $gallery->gid ) ); } } break; case 'album': $albumlist = $wpdb->get_results("SELECT * FROM $wpdb->nggalbum ORDER BY id ASC LIMIT $offset, 50", OBJECT_K); if ( is_array($albumlist) ) { foreach ($albumlist as $album) { //slug must be unique, we use the name for that $album->slug = nggdb::get_unique_slug( sanitize_title( $album->name ), 'album', $album->id ); $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->nggalbum SET slug= '%s' WHERE id = '%d'" , $album->slug, $album->id ) ); } } break; } die(1); }
/** * Uploads base64 file to a gallery * @param int|stdClass|C_Gallery $gallery * @param $data base64-encoded string of data representing the image * @param type $filename specifies the name of the file * @return C_Image */ function upload_base64_image($gallery, $data, $filename = FALSE) { $settings = C_NextGen_Settings::get_instance(); $memory_limit = intval(ini_get('memory_limit')); if ($memory_limit < 256) { @ini_set('memory_limit', '256M'); } $retval = NULL; if ($gallery_id = $this->object->_get_gallery_id($gallery)) { // Ensure that there is capacity available require_once ABSPATH . 'wp-admin/includes/ms.php'; if (is_multisite() && nggWPMU::wpmu_enable_function('wpmuQuotaCheck')) { if (upload_is_user_over_quota(FALSE)) { throw new E_NoSpaceAvailableException(); } } // Get path information. The use of get_upload_abspath() might // not be the best for some drivers. For example, if using the // WordPress Media Library for uploading, then the wp_upload_bits() // function should perhaps be used $upload_dir = $this->object->get_upload_abspath($gallery); // Perhaps a filename was given instead of base64 data? if ($data[0] == '/' && @file_exists($data)) { if (!$filename) { $filename = basename($data); } $data = file_get_contents($data); } // Determine filenames $filename = $filename ? sanitize_title_with_dashes($filename) : uniqid('nextgen-gallery'); if (preg_match("/\\-(png|jpg|gif|jpeg)\$/i", $filename, $match)) { $filename = str_replace($match[0], '.' . $match[1], $filename); } $abs_filename = path_join($upload_dir, $filename); // Create the database record $factory = $this->object->get_registry()->get_utility('I_Component_Factory'); $retval = $image = $factory->create('image'); $image->alttext = sanitize_title_with_dashes(basename($filename, '.' . pathinfo($filename, PATHINFO_EXTENSION))); $image->galleryid = $this->object->_get_gallery_id($gallery); $image->filename = $filename; $image->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($image->alttext), 'image'); $image_key = $this->object->_image_mapper->get_primary_key_column(); // If we can't write to the directory, then there's no point in continuing if (!@file_exists($upload_dir)) { @wp_mkdir_p($upload_dir); } if (!is_writable($upload_dir)) { throw new E_InsufficientWriteAccessException(FALSE, $upload_dir, FALSE); } // Save the image if ($image_id = $this->object->_image_mapper->save($image)) { try { // Try writing the image if (!@file_exists($upload_dir)) { wp_mkdir_p($upload_dir); } $fp = fopen($abs_filename, 'w'); fwrite($fp, $data); fclose($fp); if ($settings->imgBackup) { $this->object->backup_image($image); } if ($settings->imgAutoResize) { $this->object->generate_image_clone($abs_filename, $abs_filename, $this->object->get_image_size_params($image_id, 'full')); } // Ensure that fullsize dimensions are added to metadata array $dimensions = getimagesize($abs_filename); $full_meta = array('width' => $dimensions[0], 'height' => $dimensions[1]); if (!isset($image->meta_data) or is_string($image->meta_data) && strlen($image->meta_data) == 0) { $image->meta_data = array(); } $image->meta_data = array_merge($image->meta_data, $full_meta); $image->meta_data['full'] = $full_meta; // Generate a thumbnail for the image $this->object->generate_thumbnail($image); // Set gallery preview image if missing $this->object->get_registry()->get_utility('I_Gallery_Mapper')->set_preview_image($gallery, $image_id, TRUE); // Notify other plugins that an image has been added do_action('ngg_added_new_image', $image); // delete dirsize after adding new images delete_transient('dirsize_cache'); // Seems redundant to above hook. Maintaining for legacy purposes do_action('ngg_after_new_images_added', $gallery_id, array($image->{$image_key})); } catch (Exception $ex) { throw new E_InsufficientWriteAccessException(FALSE, $abs_filename, FALSE, $ex); } } else { throw new E_InvalidEntityException(); } } else { throw new E_EntityNotFoundException(); } @ini_set('memory_limit', $memory_limit . 'M'); return $retval; }