0) { if ('delete_orphans' == $photo_deletion_mode) { $query = ' SELECT DISTINCT(image_id) FROM '.IMAGE_CATEGORY_TABLE.' WHERE image_id IN ('.implode(',', $image_ids_linked).') AND category_id NOT IN ('.implode(',', $ids).') ;'; $image_ids_not_orphans = query2array($query, null, 'image_id'); $image_ids_to_delete = array_diff($image_ids_linked, $image_ids_not_orphans); } if ('force_delete' == $photo_deletion_mode) { $image_ids_to_delete = $image_ids_linked; } delete_elements($image_ids_to_delete, true); } } // destruction of the links between images and this category $query = ' DELETE FROM '.IMAGE_CATEGORY_TABLE.' WHERE category_id IN ( '.wordwrap(implode(', ', $ids), 80, "\n").') ;'; pwg_query($query); // destruction of the access linked to the category $query = ' DELETE FROM '.USER_ACCESS_TABLE.' WHERE cat_id IN ( '.wordwrap(implode(', ', $ids), 80, "\n").') ;'; pwg_query($query); $query = ' DELETE FROM '.GROUP_ACCESS_TABLE.' WHERE cat_id IN ( '.wordwrap(implode(', ', $ids), 80, "\n").') ;'; pwg_query($query); // destruction of the category $query = ' DELETE FROM '.CATEGORIES_TABLE.' WHERE id IN ( '.wordwrap(implode(', ', $ids), 80, "\n").') ;'; pwg_query($query); $query=' DELETE FROM '.OLD_PERMALINKS_TABLE.' WHERE cat_id IN ('.implode(',',$ids).')'; pwg_query($query); $query=' DELETE FROM '.USER_CACHE_CATEGORIES_TABLE.' WHERE cat_id IN ('.implode(',',$ids).')'; pwg_query($query); trigger_notify('delete_categories', $ids); pwg_activity('album', $ids, 'delete', array('photo_deletion_mode'=>$photo_deletion_mode)); } /** * Deletes all files (on disk) related to given image ids. * * @param int[] $ids * @return 0|int[] image ids where files were successfully deleted */ function delete_element_files($ids) { global $conf; if (count($ids) == 0) { return 0; } $new_ids = array(); $formats_of = array(); $query = ' SELECT image_id, ext FROM '.IMAGE_FORMAT_TABLE.' WHERE image_id IN ('.implode(',', $ids).') ;'; $result = pwg_query($query); while ($row = pwg_db_fetch_assoc($result)) { if (!isset($formats_of[ $row['image_id'] ])) { $formats_of[ $row['image_id'] ] = array(); } $formats_of[ $row['image_id'] ][] = $row['ext']; } $query = ' SELECT id, path, representative_ext FROM '.IMAGES_TABLE.' WHERE id IN ('.implode(',', $ids).') ;'; $result = pwg_query($query); while ($row = pwg_db_fetch_assoc($result)) { if (url_is_remote($row['path'])) { continue; } $files = array(); $files[] = get_element_path($row); if (!empty($row['representative_ext'])) { $files[] = original_to_representative( $files[0], $row['representative_ext']); } if (isset($formats_of[ $row['id'] ])) { foreach ($formats_of[ $row['id'] ] as $format_ext) { $files[] = original_to_format($files[0], $format_ext); } } $ok = true; if (!isset($conf['never_delete_originals'])) { foreach ($files as $path) { if (is_file($path) and !unlink($path)) { $ok = false; trigger_error('"'.$path.'" cannot be removed', E_USER_WARNING); break; } } } if ($ok) { delete_element_derivatives($row); $new_ids[] = $row['id']; } else { break; } } return $new_ids; } /** * Deletes elements from database. * It also deletes : * - all the comments related to elements * - all the links between categories/tags and elements * - all the favorites/rates associated to elements * - removes elements from caddie * * @param int[] $ids * @param bool $physical_deletion * @return int number of deleted elements */ function delete_elements($ids, $physical_deletion=false) { if (count($ids) == 0) { return 0; } trigger_notify('begin_delete_elements', $ids); if ($physical_deletion) { $ids = delete_element_files($ids); if (count($ids)==0) { return 0; } } $ids_str = wordwrap(implode(', ', $ids), 80, "\n"); // destruction of the comments on the image $query = ' DELETE FROM '.COMMENTS_TABLE.' WHERE image_id IN ('. $ids_str .') ;'; pwg_query($query); // destruction of the links between images and categories $query = ' DELETE FROM '.IMAGE_CATEGORY_TABLE.' WHERE image_id IN ('. $ids_str .') ;'; pwg_query($query); // destruction of the formats $query = ' DELETE FROM '.IMAGE_FORMAT_TABLE.' WHERE image_id IN ('. $ids_str .') ;'; pwg_query($query); // destruction of the links between images and tags $query = ' DELETE FROM '.IMAGE_TAG_TABLE.' WHERE image_id IN ('. $ids_str .') ;'; pwg_query($query); // destruction of the favorites associated with the picture $query = ' DELETE FROM '.FAVORITES_TABLE.' WHERE image_id IN ('. $ids_str .') ;'; pwg_query($query); // destruction of the rates associated to this element $query = ' DELETE FROM '.RATE_TABLE.' WHERE element_id IN ('. $ids_str .') ;'; pwg_query($query); // destruction of the caddie associated to this element $query = ' DELETE FROM '.CADDIE_TABLE.' WHERE element_id IN ('. $ids_str .') ;'; pwg_query($query); // destruction of the image $query = ' DELETE FROM '.IMAGES_TABLE.' WHERE id IN ('. $ids_str .') ;'; pwg_query($query); // are the photo used as category representant? $query = ' SELECT id FROM '.CATEGORIES_TABLE.' WHERE representative_picture_id IN ('. $ids_str .') ;'; $category_ids = query2array($query, null, 'id'); if (count($category_ids) > 0) { update_category($category_ids); } trigger_notify('delete_elements', $ids); pwg_activity('photo', $ids, 'delete'); return count($ids); } /** * Deletes an user. * It also deletes all related data (accesses, favorites, permissions, etc.) * @todo : accept array input * * @param int $user_id */ function delete_user($user_id) { global $conf; $tables = array( // destruction of the access linked to the user USER_ACCESS_TABLE, // destruction of data notification by mail for this user USER_MAIL_NOTIFICATION_TABLE, // destruction of data RSS notification for this user USER_FEED_TABLE, // deletion of calculated permissions linked to the user USER_CACHE_TABLE, // deletion of computed cache data linked to the user USER_CACHE_CATEGORIES_TABLE, // destruction of the group links for this user USER_GROUP_TABLE, // destruction of the favorites associated with the user FAVORITES_TABLE, // destruction of the caddie associated with the user CADDIE_TABLE, // deletion of piwigo specific informations USER_INFOS_TABLE, USER_AUTH_KEYS_TABLE ); foreach ($tables as $table) { $query = ' DELETE FROM '.$table.' WHERE user_id = '.$user_id.' ;'; pwg_query($query); } // purge of sessions delete_user_sessions($user_id); // destruction of the user $query = ' DELETE FROM '.USERS_TABLE.' WHERE '.$conf['user_fields']['id'].' = '.$user_id.' ;'; pwg_query($query); trigger_notify('delete_user', $user_id); pwg_activity('user', $user_id, 'delete'); } /** * Deletes all tags linked to no photo */ function delete_orphan_tags() { $orphan_tags = get_orphan_tags(); if (count($orphan_tags) > 0) { $orphan_tag_ids = array(); foreach ($orphan_tags as $tag) { $orphan_tag_ids[] = $tag['id']; } delete_tags($orphan_tag_ids); } } /** * Get all tags (id + name) linked to no photo */ function get_orphan_tags() { $query = ' SELECT id, name FROM '.TAGS_TABLE.' LEFT JOIN '.IMAGE_TAG_TABLE.' ON id = tag_id WHERE tag_id IS NULL AND lastmodified < SUBDATE(NOW(), INTERVAL 1 DAY) ;'; return query2array($query); } /** * Verifies that the representative picture really exists in the db and * picks up a random representative if possible and based on config. * * @param 'all'|int|int[] $ids */ function update_category($ids = 'all') { global $conf; if ($ids=='all') { $where_cats = '1=1'; } elseif ( !is_array($ids) ) { $where_cats = '%s='.$ids; } else { if (count($ids) == 0) { return false; } $where_cats = '%s IN('.wordwrap(implode(', ', $ids), 120, "\n").')'; } // find all categories where the setted representative is not possible : // the picture does not exist $query = ' SELECT DISTINCT c.id FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i ON c.representative_picture_id = i.id WHERE representative_picture_id IS NOT NULL AND '.sprintf($where_cats, 'c.id').' AND i.id IS NULL ;'; $wrong_representant = query2array($query, null, 'id'); if (count($wrong_representant) > 0) { $query = ' UPDATE '.CATEGORIES_TABLE.' SET representative_picture_id = NULL WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 120, "\n").') ;'; pwg_query($query); } if (!$conf['allow_random_representative']) { // If the random representant is not allowed, we need to find // categories with elements and with no representant. Those categories // must be added to the list of categories to set to a random // representant. $query = ' SELECT DISTINCT id FROM '.CATEGORIES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id = category_id WHERE representative_picture_id IS NULL AND '.sprintf($where_cats, 'category_id').' ;'; $to_rand = query2array($query, null, 'id'); if (count($to_rand) > 0) { set_random_representant($to_rand); } } } /** * Checks and repairs IMAGE_CATEGORY_TABLE integrity. * Removes all entries from the table which correspond to a deleted image. */ function images_integrity() { $query = ' SELECT image_id FROM '.IMAGE_CATEGORY_TABLE.' LEFT JOIN '.IMAGES_TABLE.' ON id = image_id WHERE id IS NULL ;'; $orphan_image_ids = query2array($query, null, 'image_id'); if (count($orphan_image_ids) > 0) { $query = ' DELETE FROM '.IMAGE_CATEGORY_TABLE.' WHERE image_id IN ('.implode(',', $orphan_image_ids).') ;'; pwg_query($query); } } /** * Checks and repairs integrity on categories. * Removes all entries from related tables which correspond to a deleted category. */ function categories_integrity() { $related_columns = array( IMAGE_CATEGORY_TABLE.'.category_id', USER_ACCESS_TABLE.'.cat_id', GROUP_ACCESS_TABLE.'.cat_id', OLD_PERMALINKS_TABLE.'.cat_id', USER_CACHE_CATEGORIES_TABLE.'.cat_id', ); foreach ($related_columns as $fullcol) { list($table, $column) = explode('.', $fullcol); $query = ' SELECT '.$column.' FROM '.$table.' LEFT JOIN '.CATEGORIES_TABLE.' ON id = '.$column.' WHERE id IS NULL ;'; $orphans = array_unique(query2array($query, null, $column)); if (count($orphans) > 0) { $query = ' DELETE FROM '.$table.' WHERE '.$column.' IN ('.implode(',', $orphans).') ;'; pwg_query($query); } } } /** * Returns an array containing sub-directories which are potentially * a category. * Directories named ".svn", "thumbnail", "pwg_high" or "pwg_representative" * are omitted. * * @param string $basedir (eg: ./galleries) * @return string[] */ function get_fs_directories($path, $recursive = true) { global $conf; $dirs = array(); $path = rtrim($path, '/'); $exclude_folders = array_merge( $conf['sync_exclude_folders'], array( '.', '..', '.svn', 'thumbnail', 'pwg_high', 'pwg_representative', 'pwg_format', ) ); $exclude_folders = array_flip($exclude_folders); if (is_dir($path)) { if ($contents = opendir($path)) { while (($node = readdir($contents)) !== false) { if (is_dir($path.'/'.$node) and !isset($exclude_folders[$node])) { $dirs[] = $path.'/'.$node; if ($recursive) { $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node)); } } } closedir($contents); } } return $dirs; } /** * save the rank depending on given categories order * * The list of ordered categories id is supposed to be in the same parent * category * * @param array categories * @return void */ function save_categories_order($categories) { $current_rank_for_id_uppercat = array(); $current_rank = 0; $datas = array(); foreach ($categories as $category) { if (is_array($category)) { $id = $category['id']; $id_uppercat = $category['id_uppercat']; if (!isset($current_rank_for_id_uppercat[$id_uppercat])) { $current_rank_for_id_uppercat[$id_uppercat] = 0; } $current_rank = ++$current_rank_for_id_uppercat[$id_uppercat]; } else { $id = $category; $current_rank++; } $datas[] = array('id' => $id, 'rank' => $current_rank); } $fields = array('primary' => array('id'), 'update' => array('rank')); mass_updates(CATEGORIES_TABLE, $fields, $datas); update_global_rank(); } /** * Orders categories (update categories.rank and global_rank database fields) * so that rank field are consecutive integers starting at 1 for each child. */ function update_global_rank() { $query = ' SELECT id, id_uppercat, uppercats, `rank`, global_rank FROM '.CATEGORIES_TABLE.' ORDER BY id_uppercat, `rank`, name'; global $cat_map; // used in preg_replace callback $cat_map = array(); $current_rank = 0; $current_uppercat = ''; $result = pwg_query($query); while ($row = pwg_db_fetch_assoc($result)) { if ($row['id_uppercat'] != $current_uppercat) { $current_rank = 0; $current_uppercat = $row['id_uppercat']; } ++$current_rank; $cat = array( 'rank' => $current_rank, 'rank_changed' =>$current_rank!=$row['rank'], 'global_rank' => $row['global_rank'], 'uppercats' => $row['uppercats'], ); $cat_map[ $row['id'] ] = $cat; } $datas = array(); $cat_map_callback = function($m) use ($cat_map) { return $cat_map[$m[1]]["rank"]; }; foreach( $cat_map as $id=>$cat ) { $new_global_rank = preg_replace_callback( '/(\d+)/', $cat_map_callback, str_replace(',', '.', $cat['uppercats'] ) ); if ($cat['rank_changed'] or $new_global_rank !== $cat['global_rank']) { $datas[] = array( 'id' => $id, 'rank' => $cat['rank'], 'global_rank' => $new_global_rank, ); } } unset($cat_map); mass_updates( CATEGORIES_TABLE, array( 'primary' => array('id'), 'update' => array('rank', 'global_rank') ), $datas ); return count($datas); } /** * Change the **visible** property on a set of categories. * * @param int[] $categories * @param boolean|string $value * @param boolean $unlock_child optional default false */ function set_cat_visible($categories, $value, $unlock_child = false) { if ( ($value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) === null ) { trigger_error("set_cat_visible invalid param $value", E_USER_WARNING); return false; } // unlocking a category => all its parent categories become unlocked if ($value) { $cats = get_uppercat_ids($categories); if ($unlock_child) { $cats = array_merge($cats, get_subcat_ids($categories)); } $query = ' UPDATE '.CATEGORIES_TABLE.' SET visible = \'true\' WHERE id IN ('.implode(',', $cats).')'; pwg_query($query); } // locking a category => all its child categories become locked else { $subcats = get_subcat_ids($categories); $query = ' UPDATE '.CATEGORIES_TABLE.' SET visible = \'false\' WHERE id IN ('.implode(',', $subcats).')'; pwg_query($query); } } /** * Change the **status** property on a set of categories : private or public. * * @param int[] $categories * @param string $value */ function set_cat_status($categories, $value) { if (!in_array($value, array('public', 'private'))) { trigger_error("set_cat_status invalid param $value", E_USER_WARNING); return false; } // make public a category => all its parent categories become public if ($value == 'public') { $uppercats = get_uppercat_ids($categories); $query = ' UPDATE '.CATEGORIES_TABLE.' SET status = \'public\' WHERE id IN ('.implode(',', $uppercats).') ;'; pwg_query($query); } // make a category private => all its child categories become private if ($value == 'private') { $subcats = get_subcat_ids($categories); $query = ' UPDATE '.CATEGORIES_TABLE.' SET status = \'private\' WHERE id IN ('.implode(',', $subcats).')'; pwg_query($query); // We have to keep permissions consistant: a sub-album can't be // permitted to a user or group if its parent album is not permitted to // the same user or group. Let's remove all permissions on sub-albums if // it is not consistant. Let's take the following example: // // A1 permitted to U1,G1 // A1/A2 permitted to U1,U2,G1,G2 // A1/A2/A3 permitted to U3,G1 // A1/A2/A4 permitted to U2 // A1/A5 permitted to U4 // A6 permitted to U4 // A6/A7 permitted to G1 // // (we consider that it can be possible to start with inconsistant // permission, given that public albums can have hidden permissions, // revealed once the album returns to private status) // // The admin selects A2,A3,A4,A5,A6,A7 to become private (all but A1, // which is private, which can be true if we're moving A2 into A1). The // result must be: // // A2 permission removed to U2,G2 // A3 permission removed to U3 // A4 permission removed to U2 // A5 permission removed to U2 // A6 permission removed to U4 // A7 no permission removed // // 1) we must extract "top albums": A2, A5 and A6 // 2) for each top album, decide which album is the reference for permissions // 3) remove all inconsistant permissions from sub-albums of each top-album // step 1, search top albums $top_categories = array(); $parent_ids = array(); $query = ' SELECT id, name, id_uppercat, uppercats, global_rank FROM '.CATEGORIES_TABLE.' WHERE id IN ('.implode(',', $categories).') ;'; $all_categories = query2array($query); usort($all_categories, 'global_rank_compare'); foreach ($all_categories as $cat) { $is_top = true; if (!empty($cat['id_uppercat'])) { foreach (explode(',', $cat['uppercats']) as $id_uppercat) { if (isset($top_categories[$id_uppercat])) { $is_top = false; break; } } } if ($is_top) { $top_categories[$cat['id']] = $cat; if (!empty($cat['id_uppercat'])) { $parent_ids[] = $cat['id_uppercat']; } } } // step 2, search the reference album for permissions // // to find the reference of each top album, we will need the parent albums $parent_cats = array(); if (count($parent_ids) > 0) { $query = ' SELECT id, status FROM '.CATEGORIES_TABLE.' WHERE id IN ('.implode(',', $parent_ids).') ;'; $parent_cats= query2array($query, 'id'); } $tables = array( USER_ACCESS_TABLE => 'user_id', GROUP_ACCESS_TABLE => 'group_id' ); foreach ($top_categories as $top_category) { // what is the "reference" for list of permissions? The parent album // if it is private, else the album itself $ref_cat_id = $top_category['id']; if (!empty($top_category['id_uppercat']) and isset($parent_cats[ $top_category['id_uppercat'] ]) and 'private' == $parent_cats[ $top_category['id_uppercat'] ]['status']) { $ref_cat_id = $top_category['id_uppercat']; } $subcats = get_subcat_ids(array($top_category['id'])); foreach ($tables as $table => $field) { // what are the permissions user/group of the reference album $query = ' SELECT '.$field.' FROM '.$table.' WHERE cat_id = '.$ref_cat_id.' ;'; $ref_access = query2array($query, null, $field); if (count($ref_access) == 0) { $ref_access[] = -1; } // step 3, remove the inconsistant permissions from sub-albums $query = ' DELETE FROM '.$table.' WHERE '.$field.' NOT IN ('.implode(',', $ref_access).') AND cat_id IN ('.implode(',', $subcats).') ;'; pwg_query($query); } } } } /** * Returns all uppercats category ids of the given category ids. * * @param int[] $cat_ids * @return int[] */ function get_uppercat_ids($cat_ids) { if (!is_array($cat_ids) or count($cat_ids) < 1) { return array(); } $uppercats = array(); $query = ' SELECT uppercats FROM '.CATEGORIES_TABLE.' WHERE id IN ('.implode(',', $cat_ids).') ;'; $result = pwg_query($query); while ($row = pwg_db_fetch_assoc($result)) { $uppercats = array_merge($uppercats, explode(',', $row['uppercats'])); } $uppercats = array_unique($uppercats); return $uppercats; } /** */ function get_category_representant_properties($image_id, $size = NULL) { $query = ' SELECT id,representative_ext,path FROM '.IMAGES_TABLE.' WHERE id = '.$image_id.' ;'; $row = pwg_db_fetch_assoc(pwg_query($query)); if ($size == NULL) { $src = DerivativeImage::thumb_url($row); } else { $src = DerivativeImage::url($size, $row); } $url = get_root_url().'admin.php?page=photo-'.$image_id; return array( 'src' => $src, 'url' => $url ); } /** * Set a new random representant to the categories. * * @param int[] $categories */ function set_random_representant($categories) { $datas = array(); foreach ($categories as $category_id) { $query = ' SELECT image_id FROM '.IMAGE_CATEGORY_TABLE.' WHERE category_id = '.$category_id.' ORDER BY '.DB_RANDOM_FUNCTION.'() LIMIT 1 ;'; list($representative) = pwg_db_fetch_row(pwg_query($query)); $datas[] = array( 'id' => $category_id, 'representative_picture_id' => $representative, ); } mass_updates( CATEGORIES_TABLE, array( 'primary' => array('id'), 'update' => array('representative_picture_id') ), $datas ); } /** * Returns the fulldir for each given category id. * * @param int[] intcat_ids * @return string[] */ function get_fulldirs($cat_ids) { if (count($cat_ids) == 0) { return array(); } // caching directories of existing categories global $cat_dirs; // used in preg_replace callback $query = ' SELECT id, dir FROM '.CATEGORIES_TABLE.' WHERE dir IS NOT NULL ;'; $cat_dirs = query2array($query, 'id', 'dir'); // caching galleries_url $query = ' SELECT id, galleries_url FROM '.SITES_TABLE.' ;'; $galleries_url = query2array($query, 'id', 'galleries_url'); // categories : id, site_id, uppercats $query = ' SELECT id, uppercats, site_id FROM '.CATEGORIES_TABLE.' WHERE dir IS NOT NULL AND id IN ( '.wordwrap(implode(', ', $cat_ids), 80, "\n").') ;'; $categories = query2array($query); // filling $cat_fulldirs $cat_dirs_callback = function($m) use ($cat_dirs) { return $cat_dirs[$m[1]]; }; $cat_fulldirs = array(); foreach ($categories as $category) { $uppercats = str_replace(',', '/', $category['uppercats']); $cat_fulldirs[$category['id']] = $galleries_url[$category['site_id']]; $cat_fulldirs[$category['id']].= preg_replace_callback( '/(\d+)/', $cat_dirs_callback, $uppercats ); } unset($cat_dirs); return $cat_fulldirs; } /** * Returns an array with all file system files according to $conf['file_ext'] * * @deprecated 2.4 * * @param string $path * @param bool $recursive * @return array */ function get_fs($path, $recursive = true) { global $conf; // because isset is faster than in_array... if (!isset($conf['flip_picture_ext'])) { $conf['flip_picture_ext'] = array_flip($conf['picture_ext']); } if (!isset($conf['flip_file_ext'])) { $conf['flip_file_ext'] = array_flip($conf['file_ext']); } $fs['elements'] = array(); $fs['thumbnails'] = array(); $fs['representatives'] = array(); $subdirs = array(); if (is_dir($path)) { if ($contents = opendir($path)) { while (($node = readdir($contents)) !== false) { if ($node == '.' or $node == '..') continue; if (is_file($path.'/'.$node)) { $extension = get_extension($node); if (isset($conf['flip_picture_ext'][$extension])) { if (basename($path) == 'thumbnail') { $fs['thumbnails'][] = $path.'/'.$node; } elseif (basename($path) == 'pwg_representative') { $fs['representatives'][] = $path.'/'.$node; } else { $fs['elements'][] = $path.'/'.$node; } } elseif (isset($conf['flip_file_ext'][$extension])) { $fs['elements'][] = $path.'/'.$node; } } elseif (is_dir($path.'/'.$node) and $node != 'pwg_high' and $recursive) { $subdirs[] = $node; } } } closedir($contents); foreach ($subdirs as $subdir) { $tmp_fs = get_fs($path.'/'.$subdir); $fs['elements'] = array_merge($fs['elements'], $tmp_fs['elements']); $fs['thumbnails'] = array_merge($fs['thumbnails'], $tmp_fs['thumbnails']); $fs['representatives'] = array_merge($fs['representatives'], $tmp_fs['representatives']); } } return $fs; } /** * Synchronize base users list and related users list. * * Compares and synchronizes base users table (USERS_TABLE) with its child * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each * base user must be present in child tables, users in child tables not * present in base table must be deleted. */ function sync_users() { global $conf; $query = ' SELECT '.$conf['user_fields']['id'].' AS id FROM '.USERS_TABLE.' ;'; $base_users = query2array($query, null, 'id'); $query = ' SELECT user_id FROM '.USER_INFOS_TABLE.' ;'; $infos_users = query2array($query, null, 'user_id'); // users present in $base_users and not in $infos_users must be added $to_create = array_diff($base_users, $infos_users); if (count($to_create) > 0) { create_user_infos($to_create); } // users present in user related tables must be present in the base user // table $tables = array( USER_MAIL_NOTIFICATION_TABLE, USER_FEED_TABLE, USER_INFOS_TABLE, USER_ACCESS_TABLE, USER_CACHE_TABLE, USER_CACHE_CATEGORIES_TABLE, USER_GROUP_TABLE ); foreach ($tables as $table) { $query = ' SELECT DISTINCT user_id FROM '.$table.' ;'; $to_delete = array_diff( query2array($query, null, 'user_id'), $base_users ); if (count($to_delete) > 0) { $query = ' DELETE FROM '.$table.' WHERE user_id in ('.implode(',', $to_delete).') ;'; pwg_query($query); } } } /** * Updates categories.uppercats field based on categories.id + categories.id_uppercat */ function update_uppercats() { $query = ' SELECT id, id_uppercat, uppercats FROM '.CATEGORIES_TABLE.' ;'; $cat_map = query2array($query, 'id'); $datas = array(); foreach ($cat_map as $id => $cat) { $upper_list = array(); $uppercat = $id; while ($uppercat) { $upper_list[] = $uppercat; $uppercat = $cat_map[$uppercat]['id_uppercat']; } $new_uppercats = implode(',', array_reverse($upper_list)); if ($new_uppercats != $cat['uppercats']) { $datas[] = array( 'id' => $id, 'uppercats' => $new_uppercats ); } } $fields = array('primary' => array('id'), 'update' => array('uppercats')); mass_updates(CATEGORIES_TABLE, $fields, $datas); } /** * Update images.path field base on images.file and storage categories fulldirs. */ function update_path() { $query = ' SELECT DISTINCT(storage_category_id) FROM '.IMAGES_TABLE.' WHERE storage_category_id IS NOT NULL ;'; $cat_ids = query2array($query, null, 'storage_category_id'); $fulldirs = get_fulldirs($cat_ids); foreach ($cat_ids as $cat_id) { $query = ' UPDATE '.IMAGES_TABLE.' SET path = '.pwg_db_concat(array("'".$fulldirs[$cat_id]."/'",'file')).' WHERE storage_category_id = '.$cat_id.' ;'; pwg_query($query); } } /** * Change the parent category of the given categories. The categories are * supposed virtual. * * @param int[] $category_ids * @param int $new_parent (-1 for root) */ function move_categories($category_ids, $new_parent = -1) { global $page; if (count($category_ids) == 0) { return; } $new_parent = $new_parent < 1 ? 'NULL' : $new_parent; $categories = array(); $query = ' SELECT id, id_uppercat, status, uppercats FROM '.CATEGORIES_TABLE.' WHERE id IN ('.implode(',', $category_ids).') ;'; $result = pwg_query($query); while ($row = pwg_db_fetch_assoc($result)) { $categories[$row['id']] = array( 'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'], 'status' => $row['status'], 'uppercats' => $row['uppercats'] ); } // is the movement possible? The movement is impossible if you try to move // a category in a sub-category or itself if ('NULL' != $new_parent) { $query = ' SELECT uppercats FROM '.CATEGORIES_TABLE.' WHERE id = '.$new_parent.' ;'; list($new_parent_uppercats) = pwg_db_fetch_row(pwg_query($query)); foreach ($categories as $category) { // technically, you can't move a category with uppercats 12,125,13,14 // into a new parent category with uppercats 12,125,13,14,24 if (preg_match('/^'.$category['uppercats'].'(,|$)/', $new_parent_uppercats)) { $page['errors'][] = l10n('You cannot move an album in its own sub album'); return; } } } $tables = array( USER_ACCESS_TABLE => 'user_id', GROUP_ACCESS_TABLE => 'group_id' ); $query = ' UPDATE '.CATEGORIES_TABLE.' SET id_uppercat = '.$new_parent.' WHERE id IN ('.implode(',', $category_ids).') ;'; pwg_query($query); update_uppercats(); update_global_rank(); // status and related permissions management if ('NULL' == $new_parent) { $parent_status = 'public'; } else { $query = ' SELECT status FROM '.CATEGORIES_TABLE.' WHERE id = '.$new_parent.' ;'; list($parent_status) = pwg_db_fetch_row(pwg_query($query)); } if ('private' == $parent_status) { set_cat_status(array_keys($categories), 'private'); } $page['infos'][] = l10n_dec( '%d album moved', '%d albums moved', count($categories) ); pwg_activity('album', $category_ids, 'move', array('parent'=>$new_parent)); } /** * Create a virtual category. * * @param string $category_name * @param int $parent_id * @param array $options * - boolean commentable * - boolean visible * - string status * - string comment * - boolean inherit * @return array ('info', 'id') or ('error') */ function create_virtual_category($category_name, $parent_id=null, $options=array()) { global $conf, $user; // is the given category name only containing blank spaces ? if (preg_match('/^\s*$/', $category_name)) { return array('error' => l10n('The name of an album must not be empty')); } $rank = 0; if ('last' == $conf['newcat_default_position']) { //what is the current higher rank for this parent? $query = ' SELECT MAX(`rank`) AS max_rank FROM '. CATEGORIES_TABLE .' WHERE id_uppercat '.(empty($parent_id) ? 'IS NULL' : '= '.$parent_id).' ;'; $row = pwg_db_fetch_assoc(pwg_query($query)); if (is_numeric($row['max_rank'])) { $rank = $row['max_rank'] + 1; } } $insert = array( 'name' => $category_name, 'rank' => $rank, 'global_rank' => 0, ); // is the album commentable? if (isset($options['commentable']) and is_bool($options['commentable'])) { $insert['commentable'] = $options['commentable']; } else { $insert['commentable'] = $conf['newcat_default_commentable']; } $insert['commentable'] = boolean_to_string($insert['commentable']); // is the album temporarily locked? (only visible by administrators, // whatever permissions) (may be overwritten if parent album is not // visible) if (isset($options['visible']) and is_bool($options['visible'])) { $insert['visible'] = $options['visible']; } else { $insert['visible'] = $conf['newcat_default_visible']; } $insert['visible'] = boolean_to_string($insert['visible']); // is the album private? (may be overwritten if parent album is private) if (isset($options['status']) and 'private' == $options['status']) { $insert['status'] = 'private'; } else { $insert['status'] = $conf['newcat_default_status']; } // any description for this album? if (isset($options['comment'])) { $insert['comment'] = $conf['allow_html_descriptions'] ? $options['comment'] : strip_tags($options['comment']); } if (!empty($parent_id) and is_numeric($parent_id)) { $query = ' SELECT id, uppercats, global_rank, visible, status FROM '.CATEGORIES_TABLE.' WHERE id = '.$parent_id.' ;'; $parent = pwg_db_fetch_assoc(pwg_query($query)); $insert['id_uppercat'] = $parent['id']; $insert['global_rank'] = $parent['global_rank'].'.'.$insert['rank']; // at creation, must a category be visible or not ? Warning : if the // parent category is invisible, the category is automatically create // invisible. (invisible = locked) if ('false' == $parent['visible']) { $insert['visible'] = 'false'; } // at creation, must a category be public or private ? Warning : if the // parent category is private, the category is automatically create // private. if ('private' == $parent['status']) { $insert['status'] = 'private'; } $uppercats_prefix = $parent['uppercats'].','; } else { $uppercats_prefix = ''; } // we have then to add the virtual category single_insert(CATEGORIES_TABLE, $insert); $inserted_id = pwg_db_insert_id(CATEGORIES_TABLE); single_update( CATEGORIES_TABLE, array('uppercats' => $uppercats_prefix.$inserted_id), array('id' => $inserted_id) ); update_global_rank(); if ('private' == $insert['status'] and !empty($insert['id_uppercat']) and ((isset($options['inherit']) and $options['inherit']) or $conf['inheritance_by_default']) ) { $query = ' SELECT group_id FROM '.GROUP_ACCESS_TABLE.' WHERE cat_id = '.$insert['id_uppercat'].' ;'; $granted_grps = query2array($query, null, 'group_id'); $inserts = array(); foreach ($granted_grps as $granted_grp) { $inserts[] = array( 'group_id' => $granted_grp, 'cat_id' => $inserted_id ); } mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $inserts); $query = ' SELECT user_id FROM '.USER_ACCESS_TABLE.' WHERE cat_id = '.$insert['id_uppercat'].' ;'; $granted_users = query2array($query, null, 'user_id'); add_permission_on_category($inserted_id, $granted_users); } elseif ('private' == $insert['status']) { add_permission_on_category($inserted_id, array_unique(array_merge(get_admins(), array($user['id'])))); } trigger_notify('create_virtual_category', array_merge(array('id'=>$inserted_id), $insert)); pwg_activity('album', $inserted_id, 'add'); return array( 'info' => l10n('Album added'), 'id' => $inserted_id, ); } /** * Set tags to an image. * Warning: given tags are all tags associated to the image, not additionnal tags. * * @param int[] $tags * @param int $image_id */ function set_tags($tags, $image_id) { set_tags_of( array($image_id=>$tags) ); } /** * Add new tags to a set of images. * * @param int[] $tags * @param int[] $images */ function add_tags($tags, $images) { if (count($tags) == 0 or count($images) == 0) { return; } $taglist_before = get_image_tag_ids($images); // we can't insert twice the same {image_id,tag_id} so we must first // delete lines we'll insert later $query = ' DELETE FROM '.IMAGE_TAG_TABLE.' WHERE image_id IN ('.implode(',', $images).') AND tag_id IN ('.implode(',', $tags).') ;'; pwg_query($query); $inserts = array(); foreach ($images as $image_id) { foreach ( array_unique($tags) as $tag_id) { $inserts[] = array( 'image_id' => $image_id, 'tag_id' => $tag_id, ); } } mass_inserts( IMAGE_TAG_TABLE, array_keys($inserts[0]), $inserts ); $taglist_after = get_image_tag_ids($images); $images_to_update = compare_image_tag_lists($taglist_before, $taglist_after); update_images_lastmodified($images_to_update); invalidate_user_cache_nb_tags(); } /** * Delete tags and tags associations. * * @param int[] $tag_ids */ function delete_tags($tag_ids) { if (is_numeric($tag_ids)) { $tag_ids = array($tag_ids); } if (!is_array($tag_ids)) { return false; } // we need the list of impacted images, to update their lastmodified $query = ' SELECT image_id FROM '.IMAGE_TAG_TABLE.' WHERE tag_id IN ('.implode(',', $tag_ids).') ;'; $image_ids = query2array($query, null, 'image_id'); $query = ' DELETE FROM '.IMAGE_TAG_TABLE.' WHERE tag_id IN ('.implode(',', $tag_ids).') ;'; pwg_query($query); $query = ' DELETE FROM '.TAGS_TABLE.' WHERE id IN ('.implode(',', $tag_ids).') ;'; pwg_query($query); trigger_notify("delete_tags", $tag_ids); pwg_activity('tag', $tag_ids, 'delete'); update_images_lastmodified($image_ids); invalidate_user_cache_nb_tags(); } /** * Returns a tag id from its name. If nothing found, create a new tag. * * @param string $tag_name * @return int */ function tag_id_from_tag_name($tag_name) { global $page; $tag_name = trim($tag_name); if (isset($page['tag_id_from_tag_name_cache'][$tag_name])) { return $page['tag_id_from_tag_name_cache'][$tag_name]; } // search existing by exact name $query = ' SELECT id FROM '.TAGS_TABLE.' WHERE name = \''.$tag_name.'\' ;'; if (count($existing_tags = query2array($query, null, 'id')) == 0) { $url_name = trigger_change('render_tag_url', $tag_name); // search existing by url name $query = ' SELECT id FROM '.TAGS_TABLE.' WHERE url_name = \''.$url_name.'\' ;'; if (count($existing_tags = query2array($query, null, 'id')) == 0) { // search by extended description (plugin sub name) $sub_name_where = trigger_change('get_tag_name_like_where', array(), $tag_name); if (count($sub_name_where)) { $query = ' SELECT id FROM '.TAGS_TABLE.' WHERE '.implode(' OR ', $sub_name_where).' ;'; $existing_tags = query2array($query, null, 'id'); } if (count($existing_tags) == 0) {// finally create the tag mass_inserts( TAGS_TABLE, array('name', 'url_name'), array( array( 'name' => $tag_name, 'url_name' => $url_name, ) ) ); $page['tag_id_from_tag_name_cache'][$tag_name] = pwg_db_insert_id(TAGS_TABLE); invalidate_user_cache_nb_tags(); return $page['tag_id_from_tag_name_cache'][$tag_name]; } } } $page['tag_id_from_tag_name_cache'][$tag_name] = $existing_tags[0]; return $page['tag_id_from_tag_name_cache'][$tag_name]; } /** * Set tags of images. Overwrites all existing associations. * * @param array $tags_of - keys are image ids, values are array of tag ids */ function set_tags_of($tags_of) { if (count($tags_of) > 0) { $taglist_before = get_image_tag_ids(array_keys($tags_of)); global $logger; $logger->debug('taglist_before', $taglist_before); $query = ' DELETE FROM '.IMAGE_TAG_TABLE.' WHERE image_id IN ('.implode(',', array_keys($tags_of)).') ;'; pwg_query($query); $inserts = array(); foreach ($tags_of as $image_id => $tag_ids) { foreach (array_unique($tag_ids) as $tag_id) { $inserts[] = array( 'image_id' => $image_id, 'tag_id' => $tag_id, ); } } if (count($inserts)) { mass_inserts( IMAGE_TAG_TABLE, array_keys($inserts[0]), $inserts ); } $taglist_after = get_image_tag_ids(array_keys($tags_of)); global $logger; $logger->debug('taglist_after', $taglist_after); $images_to_update = compare_image_tag_lists($taglist_before, $taglist_after); global $logger; $logger->debug('$images_to_update', $images_to_update); update_images_lastmodified($images_to_update); invalidate_user_cache_nb_tags(); } } /** * Get list of tag ids for each image. Returns an empty list if the image has * no tags. * * @since 2.9 * @param array $image_ids * @return associative array, image_id => list of tag ids */ function get_image_tag_ids($image_ids) { if (!is_array($image_ids) and is_int($image_ids)) { $images_ids = array($image_ids); } if (count($image_ids) == 0) { return array(); } $query = ' SELECT image_id, tag_id FROM '.IMAGE_TAG_TABLE.' WHERE image_id IN ('.implode(',', $image_ids).') ;'; $tags_of = array_fill_keys($image_ids, array()); $image_tags = query2array($query); foreach ($image_tags as $image_tag) { $tags_of[ $image_tag['image_id'] ][] = $image_tag['tag_id']; } return $tags_of; } /** * Compare the list of tags, for each image. Returns image_ids where tag list has changed. * * @since 2.9 * @param array $taglist_before - for each image_id (key), list of tag ids * @param array $taglist_after - for each image_id (key), list of tag ids * @return array - image_ids where the list has changed */ function compare_image_tag_lists($taglist_before, $taglist_after) { $images_to_update = array(); foreach ($taglist_after as $image_id => $list_after) { sort($list_after); $list_before = isset($taglist_before[$image_id]) ? $taglist_before[$image_id] : array(); sort($list_before); if ($list_after != $list_before) { $images_to_update[] = $image_id; } } return $images_to_update; } /** * Instead of associating images to categories, add them in the lounge, waiting for take-off. * * @since 12 * @param array $images - list of image ids * @param array $categories - list of category ids */ function fill_lounge($images, $categories) { $inserts = array(); foreach ($categories as $category_id) { foreach ($images as $image_id) { $inserts[] = array( 'image_id' => $image_id, 'category_id' => $category_id, ); } } if (count($inserts)) { mass_inserts( LOUNGE_TABLE, array_keys($inserts[0]), $inserts, array('ignore'=>true) ); } } /** * Move images from the lounge to the categories they were intended for. * * @since 12 * @param boolean $invalidate_user_cache * @return int number of images moved */ function empty_lounge($invalidate_user_cache=true) { global $logger; if (isset($conf['empty_lounge_running'])) { list($running_exec_id, $running_exec_start_time) = explode('-', $conf['empty_lounge_running']); if (time() - $running_exec_start_time > 60) { $logger->debug(__FUNCTION__.', exec='.$running_exec_id.', timeout stopped by another call to the function'); conf_delete_param('empty_lounge_running'); } } $exec_id = generate_key(4); $logger->debug(__FUNCTION__.(isset($_REQUEST['method']) ? ' (API:'.$_REQUEST['method'].')' : '').', exec='.$exec_id.', begins'); // if lounge is already being emptied, skip $query = ' INSERT IGNORE INTO '.CONFIG_TABLE.' SET param="empty_lounge_running" , value="'.$exec_id.'-'.time().'" ;'; pwg_query($query); list($empty_lounge_running) = pwg_db_fetch_row(pwg_query('SELECT value FROM '.CONFIG_TABLE.' WHERE param = "empty_lounge_running"')); list($running_exec_id,) = explode('-', $empty_lounge_running); if ($running_exec_id != $exec_id) { $logger->debug(__FUNCTION__.', exec='.$exec_id.', skip'); return; } $logger->debug(__FUNCTION__.', exec='.$exec_id.' wins the race and gets the token!'); $max_image_id = 0; $query = ' SELECT image_id, category_id FROM '.LOUNGE_TABLE.' ORDER BY category_id ASC, image_id ASC ;'; $rows = query2array($query); $images = array(); foreach ($rows as $idx => $row) { if ($row['image_id'] > $max_image_id) { $max_image_id = $row['image_id']; } $images[] = $row['image_id']; if (!isset($rows[$idx+1]) or $rows[$idx+1]['category_id'] != $row['category_id']) { // if we're at the end of the loop OR if category changes associate_images_to_categories($images, array($row['category_id'])); $images = array(); } } $query = ' DELETE FROM '.LOUNGE_TABLE.' WHERE image_id <= '.$max_image_id.' ;'; pwg_query($query); if ($invalidate_user_cache) { invalidate_user_cache(); } conf_delete_param('empty_lounge_running'); $logger->debug(__FUNCTION__.', exec='.$exec_id.', ends'); trigger_notify('empty_lounge', $rows); return $rows; } /** * Associate a list of images to a list of categories. * The function will not duplicate links and will preserve ranks. * * @param int[] $images * @param int[] $categories */ function associate_images_to_categories($images, $categories) { if (count($images) == 0 or count($categories) == 0) { return false; } // get existing associations $query = ' SELECT image_id, category_id FROM '.IMAGE_CATEGORY_TABLE.' WHERE image_id IN ('.implode(',', $images).') AND category_id IN ('.implode(',', $categories).') ;'; $result = pwg_query($query); $existing = array(); while ($row = pwg_db_fetch_assoc($result)) { $existing[ $row['category_id'] ][] = $row['image_id']; } // get max rank of each categories $query = ' SELECT category_id, MAX(`rank`) AS max_rank FROM '.IMAGE_CATEGORY_TABLE.' WHERE `rank` IS NOT NULL AND category_id IN ('.implode(',', $categories).') GROUP BY category_id ;'; $current_rank_of = query2array( $query, 'category_id', 'max_rank' ); // associate only not already associated images $inserts = array(); foreach ($categories as $category_id) { if (!isset($current_rank_of[$category_id])) { $current_rank_of[$category_id] = 0; } if (!isset($existing[$category_id])) { $existing[$category_id] = array(); } foreach ($images as $image_id) { if (!in_array($image_id, $existing[$category_id])) { $rank = ++$current_rank_of[$category_id]; $inserts[] = array( 'image_id' => $image_id, 'category_id' => $category_id, 'rank' => $rank, ); } } } if (count($inserts)) { mass_inserts( IMAGE_CATEGORY_TABLE, array_keys($inserts[0]), $inserts ); update_category($categories); } } /** * Dissociate a list of images from a category. * * @param int[] $images * @param int $categories */ function dissociate_images_from_category($images, $category) { // physical links must not be broken, so we must first retrieve image_id // which create virtual links with the category to "dissociate from". $query = ' SELECT id FROM '.IMAGE_CATEGORY_TABLE.' INNER JOIN '.IMAGES_TABLE.' ON image_id = id WHERE category_id ='.$category.' AND id IN ('.implode(',', $images).') AND ( category_id != storage_category_id OR storage_category_id IS NULL ) ;'; $dissociables = array_from_query($query, 'id'); if (!empty($dissociables)) { $query = ' DELETE FROM '.IMAGE_CATEGORY_TABLE.' WHERE category_id = '.$category.' AND image_id IN ('.implode(',', $dissociables).') '; pwg_query($query); } return count($dissociables); } /** * Dissociate images from all old categories except their storage category and * associate to new categories. * This function will preserve ranks. * * @param int[] $images * @param int[] $categories */ function move_images_to_categories($images, $categories) { if (count($images) == 0) { return false; } // let's first break links with all old albums but their "storage album" $query = ' DELETE '.IMAGE_CATEGORY_TABLE.'.* FROM '.IMAGE_CATEGORY_TABLE.' JOIN '.IMAGES_TABLE.' ON image_id=id WHERE id IN ('.implode(',', $images).') '; if (is_array($categories) and count($categories) > 0) { $query.= ' AND category_id NOT IN ('.implode(',', $categories).') '; } $query.= ' AND (storage_category_id IS NULL OR storage_category_id != category_id) ;'; pwg_query($query); if (is_array($categories) and count($categories) > 0) { associate_images_to_categories($images, $categories); } } /** * Associate images associated to a list of source categories to a list of * destination categories. * * @param int[] $sources * @param int[] $destinations */ function associate_categories_to_categories($sources, $destinations) { if (count($sources) == 0) { return false; } $query = ' SELECT image_id FROM '.IMAGE_CATEGORY_TABLE.' WHERE category_id IN ('.implode(',', $sources).') ;'; $images = query2array($query, null, 'image_id'); associate_images_to_categories($images, $destinations); } /** * Refer main Piwigo URLs (currently PHPWG_DOMAIN domain) * * @return string[] */ function pwg_URL() { $urls = array( 'HOME' => PHPWG_URL, 'WIKI' => PHPWG_URL.'/doc', 'DEMO' => PHPWG_URL.'/demo', 'FORUM' => PHPWG_URL.'/forum', 'BUGS' => PHPWG_URL.'/bugs', 'EXTENSIONS' => PHPWG_URL.'/ext', ); return $urls; } /** * Invalidates cached data (permissions and category counts) for all users. */ function invalidate_user_cache($full = true) { global $persistent_cache; if ($full) { $query = ' TRUNCATE TABLE '.USER_CACHE_CATEGORIES_TABLE.';'; pwg_query($query); $query = ' TRUNCATE TABLE '.USER_CACHE_TABLE.';'; pwg_query($query); } else { $query = ' UPDATE '.USER_CACHE_TABLE.' SET need_update = \'true\';'; pwg_query($query); } $persistent_cache->purge(true); conf_delete_param('count_orphans'); trigger_notify('invalidate_user_cache', $full); } /** * Invalidates cached tags counter for all users. */ function invalidate_user_cache_nb_tags() { global $user; unset($user['nb_available_tags']); $query = ' UPDATE '.USER_CACHE_TABLE.' SET nb_available_tags = NULL'; pwg_query($query); } /** * Adds the caracter set to a create table sql query. * All CREATE TABLE queries must call this function * * @param string $query * @return string */ function create_table_add_character_set($query) { defined('DB_CHARSET') or fatal_error('create_table_add_character_set DB_CHARSET undefined'); if ('DB_CHARSET'!='') { if ( version_compare(pwg_get_db_version(), '4.1.0', '<') ) { return $query; } $charset_collate = " DEFAULT CHARACTER SET ".DB_CHARSET; if (DB_COLLATE!='') { $charset_collate .= " COLLATE ".DB_COLLATE; } if ( is_array($query) ) { foreach( $query as $id=>$q) { $q=trim($q); $q=trim($q, ';'); if (preg_match('/^CREATE\s+TABLE/i',$q)) { $q.=$charset_collate; } $q .= ';'; $query[$id] = $q; } } else { $query=trim($query); $query=trim($query, ';'); if (preg_match('/^CREATE\s+TABLE/i',$query)) { $query.=$charset_collate; } $query .= ';'; } } return $query; } /** * Returns access levels as array used on template with html_options functions. * * @param int $MinLevelAccess * @param int $MaxLevelAccess * @return array */ function get_user_access_level_html_options($MinLevelAccess = ACCESS_FREE, $MaxLevelAccess = ACCESS_CLOSED) { $tpl_options = array(); for ($level = $MinLevelAccess; $level <= $MaxLevelAccess; $level++) { $tpl_options[$level] = l10n(sprintf('ACCESS_%d', $level)); } return $tpl_options; } /** * returns a list of templates currently available in template-extension. * Each .tpl file is extracted from template-extension. * * @param string $start (internal use) * @return string[] */ function get_extents($start='') { if ($start == '') { $start = './template-extension'; } $dir = opendir($start); $extents = array(); while (($file = readdir($dir)) !== false) { if ( $file == '.' or $file == '..' or $file == '.svn') continue; $path = $start . '/' . $file; if (is_dir($path)) { $extents = array_merge($extents, get_extents($path)); } elseif ( !is_link($path) and file_exists($path) and get_extension($path) == 'tpl' ) { $extents[] = substr($path, 21); } } return $extents; } /** * Create a new tag. * * @param string $tag_name * @return array ('id', info') or ('error') */ function create_tag($tag_name) { // clean the tag, no html/js allowed in tag name $tag_name = strip_tags($tag_name); // does the tag already exists? $query = ' SELECT id FROM '.TAGS_TABLE.' WHERE name = \''.$tag_name.'\' ;'; $existing_tags = query2array($query, null, 'id'); if (count($existing_tags) == 0) { single_insert( TAGS_TABLE, array( 'name' => $tag_name, 'url_name' => trigger_change('render_tag_url', $tag_name), ) ); $inserted_id = pwg_db_insert_id(TAGS_TABLE); return array( 'info' => l10n('Tag "%s" was added', stripslashes($tag_name)), 'id' => $inserted_id, ); } else { return array( 'error' => l10n('Tag "%s" already exists', stripslashes($tag_name)) ); } } /** * Is the category accessible to the (Admin) user ? * Note : if the user is not authorized to see this category, category jump * will be replaced by admin cat_modify page * * @param int $category_id * @return bool */ function cat_admin_access($category_id) { global $user; // $filter['visible_categories'] and $filter['visible_images'] // are not used because it's not necessary (filter <> restriction) if (in_array($category_id, @explode(',', $user['forbidden_categories']))) { return false; } return true; } /** * Retrieve data from external URL. * * @param string $src * @param string|Ressource $dest - can be a file ressource or string * @param array $get_data - data added to request url * @param array $post_data - data transmitted with POST * @param string $user_agent * @param int $step (internal use) * @return bool */ function fetchRemote($src, &$dest, $get_data=array(), $post_data=array(), $user_agent='Piwigo', $step=0) { global $conf; // Try to retrieve data from local file? if (!url_is_remote($src)) { $content = @file_get_contents($src); if ($content !== false) { is_resource($dest) ? @fwrite($dest, $content) : $dest = $content; return true; } else { return false; } } // After 3 redirections, return false if ($step > 3) return false; // Initialization $method = empty($post_data) ? 'GET' : 'POST'; $request = empty($post_data) ? '' : http_build_query($post_data, '', '&'); if (!empty($get_data)) { $src .= strpos($src, '?') === false ? '?' : '&'; $src .= http_build_query($get_data, '', '&'); } // Initialize $dest is_resource($dest) or $dest = ''; // Try curl to read remote file // TODO : remove all these @ if (function_exists('curl_init') && function_exists('curl_exec')) { $ch = @curl_init(); if (isset($conf['use_proxy']) && $conf['use_proxy']) { @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 0); @curl_setopt($ch, CURLOPT_PROXY, $conf['proxy_server']); if (isset($conf['proxy_auth']) && !empty($conf['proxy_auth'])) { @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $conf['proxy_auth']); } } @curl_setopt($ch, CURLOPT_URL, $src); @curl_setopt($ch, CURLOPT_HEADER, 1); @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent); @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); if ($method == 'POST') { @curl_setopt($ch, CURLOPT_POST, 1); @curl_setopt($ch, CURLOPT_POSTFIELDS, $request); } $content = @curl_exec($ch); $header_length = @curl_getinfo($ch, CURLINFO_HEADER_SIZE); $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE); @curl_close($ch); if ($content !== false and $status >= 200 and $status < 400) { if (preg_match('/Location:\s+?(.+)/', substr($content, 0, $header_length), $m)) { return fetchRemote($m[1], $dest, array(), array(), $user_agent, $step+1); } $content = substr($content, $header_length); is_resource($dest) ? @fwrite($dest, $content) : $dest = $content; return true; } } // Try file_get_contents to read remote file if (ini_get('allow_url_fopen')) { $opts = array( 'http' => array( 'method' => $method, 'user_agent' => $user_agent, ) ); if ($method == 'POST') { $opts['http']['content'] = $request; } $context = @stream_context_create($opts); $content = @file_get_contents($src, false, $context); if ($content !== false) { is_resource($dest) ? @fwrite($dest, $content) : $dest = $content; return true; } } // Try fsockopen to read remote file $src = parse_url($src); $host = $src['host']; $path = isset($src['path']) ? $src['path'] : '/'; $path .= isset($src['query']) ? '?'.$src['query'] : ''; if (($s = @fsockopen($host,80,$errno,$errstr,5)) === false) { return false; } $http_request = $method." ".$path." HTTP/1.0\r\n"; $http_request .= "Host: ".$host."\r\n"; if ($method == 'POST') { $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; $http_request .= "Content-Length: ".strlen($request)."\r\n"; } $http_request .= "User-Agent: ".$user_agent."\r\n"; $http_request .= "Accept: */*\r\n"; $http_request .= "\r\n"; $http_request .= $request; fwrite($s, $http_request); $i = 0; $in_content = false; while (!feof($s)) { $line = fgets($s); if (rtrim($line,"\r\n") == '' && !$in_content) { $in_content = true; $i++; continue; } if ($i == 0) { if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/',rtrim($line,"\r\n"), $m)) { fclose($s); return false; } $status = (integer) $m[2]; if ($status < 200 || $status >= 400) { fclose($s); return false; } } if (!$in_content) { if (preg_match('/Location:\s+?(.+)$/',rtrim($line,"\r\n"),$m)) { fclose($s); return fetchRemote(trim($m[1]),$dest,array(),array(),$user_agent,$step+1); } $i++; continue; } is_resource($dest) ? @fwrite($dest, $line) : $dest .= $line; $i++; } fclose($s); return true; } /** * Returns the groupname corresponding to the given group identifier if exists. * * @param int $group_id * @return string|false */ function get_groupname($group_id) { $query = ' SELECT name FROM `'.GROUPS_TABLE.'` WHERE id = '.intval($group_id).' ;'; $result = pwg_query($query); if (pwg_db_num_rows($result) > 0) { list($groupname) = pwg_db_fetch_row($result); } else { return false; } return $groupname; } function delete_groups($group_ids) { if (count($group_ids) == 0) { trigger_error('There is no group to delete', E_USER_WARNING); return false; } if (preg_match('/^group:(\d+)$/', conf_get_param('email_admin_on_new_user', 'undefined'), $matches)) { foreach ($group_ids as $group_id) { if ($group_id == $matches[1]) { conf_update_param('email_admin_on_new_user', 'all', true); } } } $group_id_string = implode(',', $group_ids); // destruction of the access linked to the group $query = ' DELETE FROM '. GROUP_ACCESS_TABLE .' WHERE group_id IN ('. $group_id_string .') ;'; pwg_query($query); // destruction of the users links for this group $query = ' DELETE FROM '. USER_GROUP_TABLE .' WHERE group_id IN ('. $group_id_string .') ;'; pwg_query($query); $query = ' SELECT id, name FROM `'. GROUPS_TABLE .'` WHERE id IN ('. $group_id_string .') ;'; $group_list = query2array($query, 'id', 'name'); $groupids = array_keys($group_list); // destruction of the group $query = ' DELETE FROM `'. GROUPS_TABLE .'` WHERE id IN ('. $group_id_string .') ;'; pwg_query($query); trigger_notify('delete_group', $groupids); pwg_activity('group', $groupids, 'delete'); return $group_list; } /** * Returns the username corresponding to the given user identifier if exists. * * @param int $user_id * @return string|false */ function get_username($user_id) { global $conf; $query = ' SELECT '.$conf['user_fields']['username'].' FROM '.USERS_TABLE.' WHERE '.$conf['user_fields']['id'].' = '.intval($user_id).' ;'; $result = pwg_query($query); if (pwg_db_num_rows($result) > 0) { list($username) = pwg_db_fetch_row($result); } else { return false; } return stripslashes($username); } /** * Get url on piwigo.org for newsletter subscription * * @param string $language (unused) * @return string */ function get_newsletter_subscribe_base_url($language='en_UK') { return PHPWG_URL.'/announcement/subscribe/'; } /** * Return admin menu id for accordion. * * @param string $menu_page * @return int */ function get_active_menu($menu_page) { global $page; if (isset($page['active_menu'])) { return $page['active_menu']; } switch ($menu_page) { case 'photo': case 'photos_add': case 'rating': case 'tags': case 'batch_manager': return 0; case 'album': case 'cat_list': case 'albums': case 'cat_options': case 'cat_search': case 'permalinks': return 1; case 'user_list': case 'user_perm': case 'group_list': case 'group_perm': case 'notification_by_mail': case 'user_activity'; return 2; case 'site_manager': case 'site_update': case 'stats': case 'history': case 'maintenance': case 'comments': case 'updates': return 3; case 'configuration': case 'derivatives': case 'extend_for_templates': case 'menubar': case 'themes': case 'theme': case 'languages': return 4; default: return -1; } } /** * Get tags list from SQL query (ids are surrounded by ~~, for get_tag_ids()). * * @param string $query * @param boolean $only_user_language - if true, only local name is returned for * multilingual tags (if ExtendedDescription plugin is active) * @return array[] ('id', 'name') */ function get_taglist($query, $only_user_language=true) { $result = pwg_query($query); $taglist = array(); $altlist = array(); while ($row = pwg_db_fetch_assoc($result)) { $raw_name = $row['name']; $name = trigger_change('render_tag_name', $raw_name, $row); $taglist[] = array( 'name' => $name, 'id' => '~~'.$row['id'].'~~', ); if (!$only_user_language) { $alt_names = trigger_change('get_tag_alt_names', array(), $raw_name); foreach( array_diff( array_unique($alt_names), array($name) ) as $alt) { $altlist[] = array( 'name' => $alt, 'id' => '~~'.$row['id'].'~~', ); } } } usort($taglist, 'tag_alpha_compare'); if (count($altlist)) { usort($altlist, 'tag_alpha_compare'); $taglist = array_merge($taglist, $altlist); } return $taglist; } /** * Get tags ids from a list of raw tags (existing tags or new tags). * * In $raw_tags we receive something like array('~~6~~', '~~59~~', 'New * tag', 'Another new tag') The ~~34~~ means that it is an existing * tag. We added the surrounding ~~ to permit creation of tags like "10" * or "1234" (numeric characters only) * * @param string|string[] $raw_tags - array or comma separated string * @param boolean $allow_create * @return int[] */ function get_tag_ids($raw_tags, $allow_create=true) { $tag_ids = array(); if (!is_array($raw_tags)) { $raw_tags = explode(',',$raw_tags); } foreach ($raw_tags as $raw_tag) { if (preg_match('/^~~(\d+)~~$/', $raw_tag, $matches)) { $tag_ids[] = $matches[1]; } elseif ($allow_create) { // we have to create a new tag $tag_ids[] = tag_id_from_tag_name(strip_tags($raw_tag)); } } return $tag_ids; } /** * Returns the argument_ids array with new sequenced keys based on related * names. Sequence is not case sensitive. * Warning: By definition, this function breaks original keys. * * @param int[] $elements_ids * @param string[] $name - names of elements, indexed by ids * @return int[] */ function order_by_name($element_ids, $name) { $ordered_element_ids = array(); foreach ($element_ids as $k_id => $element_id) { $key = strtolower($name[$element_id]) .'-'. $name[$element_id] .'-'. $k_id; $ordered_element_ids[$key] = $element_id; } ksort($ordered_element_ids); return $ordered_element_ids; } /** * Grant access to a list of categories for a list of users. * * @param int[] $category_ids * @param int[] $user_ids */ function add_permission_on_category($category_ids, $user_ids) { if (!is_array($category_ids)) { $category_ids = array($category_ids); } if (!is_array($user_ids)) { $user_ids = array($user_ids); } // check for emptiness if (count($category_ids) == 0 or count($user_ids) == 0) { return; } // make sure categories are private and select uppercats or subcats $cat_ids = get_uppercat_ids($category_ids); if (isset($_POST['apply_on_sub'])) { $cat_ids = array_merge($cat_ids, get_subcat_ids($category_ids)); } $query = ' SELECT id FROM '.CATEGORIES_TABLE.' WHERE id IN ('.implode(',', $cat_ids).') AND status = \'private\' ;'; $private_cats = query2array($query, null, 'id'); if (count($private_cats) == 0) { return; } $inserts = array(); foreach ($private_cats as $cat_id) { foreach ($user_ids as $user_id) { $inserts[] = array( 'user_id' => $user_id, 'cat_id' => $cat_id ); } } mass_inserts( USER_ACCESS_TABLE, array('user_id','cat_id'), $inserts, array('ignore'=>true) ); } /** * Returns the list of admin users. * * @param boolean $include_webmaster * @return int[] */ function get_admins($include_webmaster=true) { $status_list = array('admin'); if ($include_webmaster) { $status_list[] = 'webmaster'; } $query = ' SELECT user_id FROM '.USER_INFOS_TABLE.' WHERE status in (\''.implode("','", $status_list).'\') ;'; return query2array($query, null, 'user_id'); } /** * Delete all derivative files for one or several types * * @param 'all'|int[] $types */ function clear_derivative_cache($types='all') { if ($types === 'all') { $types = ImageStdParams::get_all_types(); $types[] = IMG_CUSTOM; } elseif (!is_array($types)) { $types = array($types); } for ($i=0; $i1) { $pattern .= '(' . implode('|',$types) . ')'; } else { $pattern .= $types[0]; } $pattern.='\.[a-zA-Z0-9]{3,4}$#'; if ($contents = @opendir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR)) { while (($node = readdir($contents)) !== false) { if ($node != '.' and $node != '..' and is_dir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node)) { clear_derivative_cache_rec(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node, $pattern); } } closedir($contents); } } /** * Used by clear_derivative_cache() * @ignore */ function clear_derivative_cache_rec($path, $pattern) { $rmdir = true; $rm_index = false; if ($contents = opendir($path)) { while (($node = readdir($contents)) !== false) { if ($node == '.' or $node == '..') continue; if (is_dir($path.'/'.$node)) { $rmdir &= clear_derivative_cache_rec($path.'/'.$node, $pattern); } else { if (preg_match($pattern, $node)) { unlink($path.'/'.$node); } elseif ($node=='index.htm') { $rm_index = true; } else { $rmdir = false; } } } closedir($contents); if ($rmdir) { if ($rm_index) { unlink($path.'/index.htm'); } clearstatcache(); @rmdir($path); } return $rmdir; } } /** * Deletes derivatives of a particular element * * @param array $infos ('path'[, 'representative_ext']) * @param 'all'|int $type */ function delete_element_derivatives($infos, $type='all') { $path = $infos['path']; if (!empty($infos['representative_ext'])) { $path = original_to_representative( $path, $infos['representative_ext']); } if (substr_compare($path, '../', 0, 3)==0) { $path = substr($path, 3); } $dot = strrpos($path, '.'); if ($type=='all') { $pattern = '-*'; } else { $pattern = '-'.derivative_to_url($type).'*'; } $path = substr_replace($path, $pattern, $dot, 0); if ( ($glob=glob(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$path)) !== false) { foreach( $glob as $file) { @unlink($file); } } } /** * Returns an array containing sub-directories, excluding ".svn" * * @param string $directory * @return string[] */ function get_dirs($directory) { $sub_dirs = array(); if ($opendir = opendir($directory)) { while ($file = readdir($opendir)) { if ($file != '.' and $file != '..' and is_dir($directory.'/'.$file) and $file != '.svn') { $sub_dirs[] = $file; } } closedir($opendir); } return $sub_dirs; } /** * Recursively delete a directory. * * @param string $path * @param string $trash_path, try to move the directory to this path if it cannot be delete */ function deltree($path, $trash_path=null) { if (is_dir($path)) { $fh = opendir($path); while ($file = readdir($fh)) { if ($file != '.' and $file != '..') { $pathfile = $path . '/' . $file; if (is_dir($pathfile)) { deltree($pathfile, $trash_path); } else { @unlink($pathfile); } } } closedir($fh); if (@rmdir($path)) { return true; } elseif (!empty($trash_path)) { if (!is_dir($trash_path)) { @mkgetdir($trash_path, MKGETDIR_RECURSIVE|MKGETDIR_DIE_ON_ERROR|MKGETDIR_PROTECT_HTACCESS); } while ($r = $trash_path . '/' . md5(uniqid(rand(), true))) { if (!is_dir($r)) { @rename($path, $r); break; } } } else { return false; } } } /** * Returns keys to identify the state of main tables. A key consists of the * last modification timestamp and the total of items (separated by a _). * Additionally returns the hash of root path. * Used to invalidate LocalStorage cache on admin pages. * * @param string|string[] list of keys to retrieve (categories,groups,images,tags,users) * @return string[] */ function get_admin_client_cache_keys($requested=array()) { $tables = array( 'categories' => CATEGORIES_TABLE, 'groups' => GROUPS_TABLE, 'images' => IMAGES_TABLE, 'tags' => TAGS_TABLE, 'users' => USER_INFOS_TABLE ); if (!is_array($requested)) { $requested = array($requested); } if (empty($requested)) { $requested = array_keys($tables); } else { $requested = array_intersect($requested, array_keys($tables)); } $keys = array( '_hash' => md5(get_absolute_root_url()), ); foreach ($requested as $item) { $query = ' SELECT CONCAT( UNIX_TIMESTAMP(MAX(lastmodified)), "_", COUNT(*) ) FROM `'. $tables[$item] .'` ;'; list($keys[$item]) = pwg_db_fetch_row(pwg_query($query)); } return $keys; } /** * Return the list of image ids where md5sum is null * * @return int[] image_ids */ function get_photos_no_md5sum() { $query = ' SELECT id FROM '.IMAGES_TABLE.' WHERE md5sum is null ;'; return query2array($query, null, 'id'); } /** * Compute and add the md5sum of image ids (where md5sum is null) * @param int[] list of image ids and there paths * @return int number of md5sum added */ function add_md5sum($ids) { $query = ' SELECT id, path FROM '.IMAGES_TABLE.' WHERE id IN ('.implode(', ',$ids).') ;'; $path_for_id = query2array($query, 'id', 'path'); $updates = array(); foreach ($path_for_id as $id => $path) { $updates[] = array( 'id' => $id, 'md5sum' => md5_file(PHPWG_ROOT_PATH.$path), ); } mass_updates( IMAGES_TABLE, array( 'primary' => array('id'), 'update' => array('md5sum') ), $updates ); return count($path_for_id); } function count_orphans() { if (is_null(conf_get_param('count_orphans'))) { // we don't care about the list of image_ids, we only care about the number // of orphans, so let's use a faster method than calling count(get_orphans()) $query = ' SELECT COUNT(*) FROM '.IMAGES_TABLE.' ;'; list($image_counter_all) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(DISTINCT(image_id)) FROM '.IMAGE_CATEGORY_TABLE.' ;'; list($image_counter_in_categories) = pwg_db_fetch_row(pwg_query($query)); $counter = $image_counter_all - $image_counter_in_categories; conf_update_param('count_orphans', $counter, true); } return conf_get_param('count_orphans'); } /** * Return the list of image ids associated to no album * * @return int[] $image_ids */ function get_orphans() { // exclude images in the lounge $query = ' SELECT image_id FROM '.LOUNGE_TABLE.' ;'; $lounged_ids = query2array($query, null, 'image_id'); $query = ' SELECT id FROM '.IMAGES_TABLE.' LEFT JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id WHERE category_id is null'; if (count($lounged_ids) > 0) { $query .= ' AND id NOT IN ('.implode(',', $lounged_ids).')'; } $query.= ' ORDER BY id ASC ;'; return query2array($query, null, 'id'); } /** * save the rank depending on given images order * * The list of ordered images id is supposed to be in the same parent * category * * @param int category_id * @param int[] images * @return void */ function save_images_order($category_id, $images) { $current_rank = 0; $datas = array(); foreach ($images as $id) { $datas[] = array( 'category_id' => $category_id, 'image_id' => $id, 'rank' => ++$current_rank, ); } $fields = array( 'primary' => array('image_id', 'category_id'), 'update' => array('rank') ); mass_updates(IMAGE_CATEGORY_TABLE, $fields, $datas); } /** * Force update on images.lastmodified column. Useful when modifying the tag * list. * * @since 2.9 * @param array $image_ids */ function update_images_lastmodified($image_ids) { if (!is_array($image_ids) and is_int($image_ids)) { $images_ids = array($image_ids); } if (count($image_ids) == 0) { return; } $query = ' UPDATE '.IMAGES_TABLE.' SET lastmodified = NOW() WHERE id IN ('.implode(',', $image_ids).') ;'; pwg_query($query); } /** * Get a more human friendly representation of big numbers. Like 17.8k instead of 17832 * * @since 2.9 * @param float $numbers */ function number_format_human_readable($numbers) { $readable = array("", "k", "M"); $index = 0; $numbers = empty($numbers) ? 0 : $numbers; while ($numbers >= 1000) { $numbers /= 1000; $index++; if ($index > count($readable) - 1) { $index--; break; } } $decimals = 1; if ('' == $readable[$index]) { $decimals = 0; } return number_format($numbers, $decimals).$readable[$index]; } /** * Get infos related to an image * * @since 2.9 * @param int $image_id * @param bool $die_on_missing */ function get_image_infos($image_id, $die_on_missing=false) { if (!is_numeric($image_id)) { fatal_error('['.__FUNCTION__.'] invalid image identifier '.htmlentities($image_id)); } $query = ' SELECT * FROM '.IMAGES_TABLE.' WHERE id = '.$image_id.' ;'; $images = query2array($query); if (count($images) == 0) { if ($die_on_missing) { fatal_error("photo ".$image_id." does not exist"); } return null; } return $images[0]; } /** * Return each cache image sizes. * * @since 12 * @param string $path_to_file */ function get_cache_size_derivatives($path) { $msizes = array(); //final res $subdirs = array(); //sous-rep if (is_dir($path)) { if ($contents = opendir($path)) { while (($node = readdir($contents)) !== false) { if ($node == '.' or $node == '..') continue; if (is_file($path.'/'.$node)) { if ($split = explode('-' ,$node)) { $size_code = substr(end($split), 0, 2); @$msizes[$size_code] += filesize($path.'/'.$node); } } elseif (is_dir($path.'/'.$node)) { $tmp_msizes = get_cache_size_derivatives($path.'/'.$node); foreach ($tmp_msizes as $size_key => $value) { @$msizes[$size_key] += $value; } } } } closedir($contents); } return $msizes; } /** * Displays a header warning if we find missing photos on a random sample. * * @since 13.4.0 */ function fs_quick_check() { global $page, $conf; if ($conf['fs_quick_check_period'] == 0) { return; } if (isset($page[__FUNCTION__.'_already_called'])) { return; } $page[__FUNCTION__.'_already_called'] = true; conf_update_param('fs_quick_check_last_check', date('c')); $query = ' SELECT id FROM '.IMAGES_TABLE.' WHERE date_available < \'2022-12-08 00:00:00\' AND path LIKE \'./upload/%\' LIMIT 5000 ;'; $issue1827_ids = query2array($query, null, 'id'); shuffle($issue1827_ids); $issue1827_ids = array_slice($issue1827_ids, 0, 50); $query = ' SELECT id FROM '.IMAGES_TABLE.' LIMIT 5000 ;'; $random_image_ids = query2array($query, null, 'id'); shuffle($random_image_ids); $random_image_ids = array_slice($random_image_ids, 0, 50); $fs_quick_check_ids = array_unique(array_merge($issue1827_ids, $random_image_ids)); if (count($fs_quick_check_ids) < 1) { return; } $query = ' SELECT id, path FROM '.IMAGES_TABLE.' WHERE id IN ('.implode(',', $fs_quick_check_ids).') ;'; $fsqc_paths = query2array($query, 'id', 'path'); foreach ($fsqc_paths as $id => $path) { if (!file_exists($path)) { global $template; $template->assign( 'header_msgs', array( l10n('Some photos are missing from your file system. Details provided by plugin Check Uploads'), ) ); return; } } } /** * Return latest news from piwigo.org. * * @since 13 */ function get_piwigo_news() { global $lang_info; $news = null; $cache_path = PHPWG_ROOT_PATH.conf_get_param('data_location').'cache/piwigo_latest_news-'.$lang_info['code'].'.cache.php'; if (!is_file($cache_path) or filemtime($cache_path) < strtotime('24 hours ago')) { $url = PHPWG_URL.'/ws.php?method=porg.news.getLatest&format=json'; if (fetchRemote($url, $content)) { $all_news = array(); $porg_news_getLatest = json_decode($content, true); if (isset($porg_news_getLatest['result'])) { $topic = $porg_news_getLatest['result']; $news = array( 'id' => $topic['topic_id'], 'subject' => $topic['subject'], 'posted_on' => $topic['posted_on'], 'posted' => format_date($topic['posted_on']), 'url' => $topic['url'], ); } if (mkgetdir(dirname($cache_path))) { file_put_contents($cache_path, serialize($news)); } } else { return array(); } } if (is_null($news)) { $news = unserialize(file_get_contents($cache_path)); } return $news; } function get_graphics_library() { global $conf; include_once(PHPWG_ROOT_PATH.'admin/include/image.class.php'); $library = pwg_image::get_library(); switch (pwg_image::get_library()) { case 'imagick': $img = new Imagick(); $version = $img->getVersion(); if (preg_match('/ImageMagick \d+\.\d+\.\d+-?\d*/', $version['versionString'], $match)) { $library.= '/'.$match[0]; } break; case 'ext_imagick': exec($conf['ext_imagick_dir'].'convert -version', $returnarray); if (preg_match('/Version: ImageMagick (\d+\.\d+\.\d+-?\d*)/', $returnarray[0], $match)) { $library.= '/'.$match[1]; } break; case 'gd': $gd_info = gd_info(); $library.= '/'.@$gd_info['GD Version']; break; } return $library; } function get_graphics_library_label() { list($library_code, $library_version) = explode('/', get_graphics_library()); $label_for_lib = array( 'imagick' => 'ImageMagick', 'ext_imagick' => 'External ImageMagick', 'gd' => 'GD', ); return $label_for_lib[$library_code].' '.$library_version; } function get_pwg_general_statitics() { $stats = array(); $query = ' SELECT COUNT(*) FROM '.IMAGES_TABLE.' ;'; list($stats['nb_photos']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*) FROM '.CATEGORIES_TABLE.' ;'; list($stats['nb_categories']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*) FROM '.TAGS_TABLE.' ;'; list($stats['nb_tags']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*) FROM '.IMAGE_TAG_TABLE.' ;'; list($stats['nb_image_tag']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*) FROM '.USERS_TABLE.' ;'; list($stats['nb_users']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*) FROM '.USER_INFOS_TABLE.' WHERE status IN (\'webmaster\', \'admin\') ;'; list($stats['nb_admins']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*) FROM `'.GROUPS_TABLE.'` ;'; list($stats['nb_groups']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*) FROM '.RATE_TABLE.' ;'; list($stats['nb_rates']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT SUM(nb_pages) FROM '.HISTORY_SUMMARY_TABLE.' WHERE month IS NULL ;'; list($stats['nb_views']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT SUM(filesize) FROM '.IMAGES_TABLE.' ;'; list($stats['disk_usage']) = pwg_db_fetch_row(pwg_query($query)); $query = ' SELECT COUNT(*), SUM(filesize) FROM '.IMAGE_FORMAT_TABLE.' ;'; list($stats['nb_formats'], $stats['formats_disk_usage']) = pwg_db_fetch_row(pwg_query($query)); $stats['disk_usage'] += $stats['formats_disk_usage']; return $stats; } function get_installation_date() { $candidate = null; // Piwigo first beta versions were created in septembre 2001, so it's not possible // to have an installation prior to this "origin of times" $piwigo_origins = '2001-09-01 00:00:00'; $query = ' SELECT registration_date FROM '.USER_INFOS_TABLE.' WHERE user_id = 2 ;'; $users = query2array($query); if (count($users) > 0) { $candidate = $users[0]['registration_date']; } if (empty($candidate) or strtotime($candidate) < strtotime($piwigo_origins)) { $query = ' SELECT MIN(registration_date) AS min_registration_date FROM '.USER_INFOS_TABLE.' WHERE registration_date > \''.$piwigo_origins.'\' ;'; $users = query2array($query); if (count($users) > 0) { $candidate = $users[0]['min_registration_date']; } } if (empty($candidate) or strtotime($candidate) < strtotime($piwigo_origins)) { // let's find another candidate $query = ' SELECT date_available FROM '.IMAGES_TABLE.' ORDER BY id ASC LIMIT 1 ;'; $images = query2array($query); if (count($images) > 0) { $candidate = $images[0]['date_available']; } } return $candidate; }