<?php
/* wppa-utils.php
* Package: wp-photo-album-plus
*
* Contains low-level utility routines
* Version 8.0.07.007
*
*/

if ( ! defined( 'ABSPATH' ) ) die( "Can't load this file directly" );

global $wppa_supported_photo_extensions;
$wppa_supported_photo_extensions = array( 'jpg', 'jpeg', 'png', 'gif' );

global $wppa_supported_document_extensions;
$wppa_supported_document_extensions = array( 'pdf' );

// Get url in wppa dir
function wppa_url( $arg ) {
	return WPPA_URL . '/' . $arg;
}

// get url of thumb
function wppa_get_thumb_url( $id, $fix_poster_ext = true, $system = 'flat', $x = '0', $y = '0', $use_thumbfile = false ) {
global $blog_id;

	// Does photo exist?
	$thumb = wppa_cache_thumb( $id );
	if ( ! $thumb ) return '';

	// Set owner if required
	wppa_set_owner_to_name( $id );

	$thumb = wppa_cache_thumb( $id );

	// If thumbratio not default, do not use the cdn version
	if ( wppa_opt( 'thumb_aspect' ) != '0:0:none' ) {
		$use_thumbfile = true;
	}

	// If 360 or flat pano, do not use the cdn version
	if ( wppa_get_photo_item( $id, 'panorama' ) ) {
		$use_thumbfile = true;
	}

	// If in the cloud...
	$is_old = wppa_too_old_for_cloud( $id );
	if ( wppa_cdn( 'front' ) &&
		! wppa_is_multi( $id ) &&
		! $is_old &&
		! wppa_is_stereo( $id ) &&
		! wppa_is_pdf( $id ) &&
		! $use_thumbfile ) {

		// Only when size is given !! To prevent download of the fullsize image
		if ( $x && $y ) {

			switch ( wppa_cdn( 'front' ) ) {
				case 'local':
					$url = wppa_cdn_url( $id, $x, $y );
					if ( $url ) {
						return $url;
					}
					break;

				case 'cloudinary':
					$transform	= explode( ':', wppa_opt( 'thumb_aspect' ) );
					$t 			= 'limit';
					if ( $transform['2'] == 'clip' ) $t = 'fill';
					if ( $transform['2'] == 'padd' ) $t = 'pad,b_black';
					$q 			= wppa_opt( 'jpeg_quality' );
					$sizespec 	= ( $x && $y ) ? 'w_'.$x.',h_'.$y.',c_'.$t.',q_'.$q.'/' : '';
					$prefix 	= ( is_multisite() && ! WPPA_MULTISITE_GLOBAL ) ? $blog_id.'-' : '';
					$s = is_ssl() ? 's' : '';
					$url = 'http'.$s.'://res.cloudinary.com/'.wppa_get_option('wppa_cdn_cloud_name').'/image/upload/'.$sizespec.$prefix.$thumb['id'].'.'.$thumb['ext'];
					return $url;
					break;

				default:
					break;
			}
		}
	}

	// If the thumbnail file does not exist, try to make it
	if ( ! wppa_is_file( wppa_get_thumb_path( $id ) ) ) {
		wppa_create_thumbnail( $id );
	}

	if ( wppa_get_option('wppa_file_system') == 'flat' ) $system = 'flat';	// Have been converted, ignore argument
	if ( wppa_get_option('wppa_file_system') == 'tree' ) $system = 'tree';	// Have been converted, ignore argument

	if ( $system == 'tree' ) {
		$result = WPPA_UPLOAD_URL . '/thumbs/' . wppa_expand_id( $thumb['id'] ) . '.' . $thumb['ext'];
	}
	else {
		$result = WPPA_UPLOAD_URL . '/thumbs/' . $thumb['id'] . '.' . $thumb['ext'];
	}

	if ( $fix_poster_ext ) {
		$result = wppa_fix_poster_ext( $result, $thumb['id'] );
	}

	$result .= '?ver=' . wppa_get_option( 'wppa_thumb_version', '1' );

	return $result;
}

// Bump thumbnail version number
function wppa_bump_thumb_rev() {
	wppa_update_option('wppa_thumb_version', wppa_get_option('wppa_thumb_version', '1') + '1');
}

// get path of thumb
function wppa_get_thumb_path( $id, $fix_poster_ext = true, $system = 'flat' ) {

	$thumb = wppa_cache_thumb( $id );
	if ( ! $thumb ) {
		return false;
	}

	if ( wppa_get_option( 'wppa_file_system' ) == 'flat' ) $system = 'flat';	// Has been converted, ignore argument
	if ( wppa_get_option( 'wppa_file_system' ) == 'tree' ) $system = 'tree';	// Has been converted, ignore argument

	if ( $system == 'tree' ) {
		$result = WPPA_UPLOAD_PATH.'/thumbs/'.wppa_expand_id($thumb['id'], true).'.'.$thumb['ext'];
	}
	else {
		$result = WPPA_UPLOAD_PATH.'/thumbs/'.$thumb['id'].'.'.$thumb['ext'];
	}

	if ( $fix_poster_ext ) {
		$result = wppa_fix_poster_ext( $result, $thumb['id'] );
	}

	return $result;
}

// get url of a full sized image
function wppa_get_photo_url( $id, $fix_poster_ext = true, $system = 'flat', $x = '0', $y = '0' ) {
global $blog_id;
global $wppa_supported_stereo_types;

	// Does photo exist?
	$thumb = wppa_cache_thumb( $id );
	if ( ! $thumb ) return '';

 	// Set owner if required
	wppa_set_owner_to_name( $id );

	// Must re-get cached thumb
	$thumb = wppa_cache_thumb( $id );

	// Feed returns thumb
	if ( is_feed() ) {
		return wppa_get_thumb_url( $id, true, $system );
	}

	// If in the cloud...
	$for_sm = wppa( 'for_sm' ); 				// Social media do not accept cloud images
	$is_old = wppa_too_old_for_cloud( $id );
	if ( wppa_cdn( 'front' ) && ! wppa_is_multi( $id ) && ! $is_old && ! wppa_is_stereo( $id ) && ! $for_sm && ! $thumb['magickstack'] && ! wppa_is_panorama( $id ) && ! wppa_is_pdf( $id ) ) {
		if ( $x && $y ) {		// Only when size is given
			switch ( wppa_cdn( 'front' ) ) {
				case 'local':
					$url = wppa_cdn_url( $id, $x, $y );
					if ( $url ) {
						return $url;
					}
					break;

				case 'cloudinary':
					$x = round($x);
					$y = round($y);
					$prefix 	= ( is_multisite() && ! WPPA_MULTISITE_GLOBAL ) ? $blog_id.'-' : '';
					$t 			= wppa_switch( 'enlarge') ? 'fit' : 'limit';
					$q 			= wppa_opt( 'jpeg_quality' );
					$sizespec 	= ( $x && $y ) ? 'w_'.$x.',h_'.$y.',c_'.$t.',q_'.$q.'/' : '';
					$s = is_ssl() ? 's' : '';
					$url = 'http'.$s.'://res.cloudinary.com/'.wppa_get_option('wppa_cdn_cloud_name').'/image/upload/'.$sizespec.$prefix.$thumb['id'].'.'.$thumb['ext'];
					return $url;
					break;

				default:
					break;
			}
		}
	}

	// Stereo?
	if ( wppa_is_stereo( $id ) ) {

		// Get type from cookie
		$st = isset( $_COOKIE["stereotype"] ) ? $_COOKIE["stereotype"] : 'color';
		if ( ! in_array( $st, $wppa_supported_stereo_types ) ) {
			$st = '_flat';
		}

		// Get glass from cookie
		$sg = 'rc';
		if ( isset( $_COOKIE["stereoglass"] ) && $_COOKIE["stereoglass"] == 'greenmagenta' ) {
			$sg = 'gm';
		}

		// Create the file if not present
		if ( ! is_file( wppa_get_stereo_path( $id, $st, $sg ) ) ) {
			wppa_create_stereo_image( $id, $st, $sg );
		}

		// Build the url
		if ( $st == '_flat' ) {
			$url = WPPA_UPLOAD_URL . '/stereo/' . $id . '-' . $st . '.jpg' . '?ver=' . wppa_get_option( 'wppa_photo_version', '1' );
		}
		else {
			$url = WPPA_UPLOAD_URL . '/stereo/' . $id . '-' . $st . '-' . $sg . '.jpg' . '?ver=' . wppa_get_option( 'wppa_photo_version', '1' );
		}

		// Done
		return $url;
	}

	if ( wppa_get_option('wppa_file_system') == 'flat' ) $system = 'flat';	// Have been converted, ignore argument
	if ( wppa_get_option('wppa_file_system') == 'tree' ) $system = 'tree';	// Have been converted, ignore argument

	if ( $system == 'tree' ) {
		$result = WPPA_UPLOAD_URL . '/' . wppa_expand_id( $thumb['id'] ) . '.' . $thumb['ext'];
	}
	else {
		$result = WPPA_UPLOAD_URL . '/' . $thumb['id'] . '.' . $thumb['ext'];
	}

	if ( $fix_poster_ext ) {
		$result = wppa_fix_poster_ext( $result, $thumb['id'] );
	}

	// Social media do not like querystrings
	if ( ! wppa( 'no_ver' ) ) {
		$result .= '?ver=' . wppa_get_option( 'wppa_photo_version', '1' );
	}

	return $result;
}

// Bump Fullsize photo version number
function wppa_bump_photo_rev() {
	wppa_update_option('wppa_photo_version', wppa_get_option('wppa_photo_version', '1') + '1');
}

// Bump Download counter
function wppa_bump_dlcount( $id ) {
	$c = wppa_get_photo_item( $id, 'dlcount' );
	$c++;
	wppa_update_photo( array( 'id' => $id, 'dlcount' => $c ) );
}

// get path of a full sized image
function wppa_get_photo_path( $id, $fix_poster_ext = true, $system = 'flat' ) {

	$thumb = wppa_cache_thumb( $id );
	if ( ! $thumb ) {
		return false;
	}

	if ( wppa_get_option( 'wppa_file_system' ) == 'flat' ) $system = 'flat';	// Have been converted, ignore argument
	if ( wppa_get_option( 'wppa_file_system' ) == 'tree' ) $system = 'tree';	// Have been converted, ignore argument

	if ( $system == 'tree' ) {
		$result = WPPA_UPLOAD_PATH . '/' . wppa_expand_id( $thumb['id'], true ) . '.' . $thumb['ext'];
	}
	else {
		$result = WPPA_UPLOAD_PATH . '/' . $thumb['id'] . '.' . $thumb['ext'];
	}
	if ( $fix_poster_ext ) {
		$result = wppa_fix_poster_ext( $result, $thumb['id'] );
	}
	return $result;
}

// Expand id to subdir chain for new file structure
function wppa_expand_id( $xid, $makepath = false ) {

	$result = '';
	$id = $xid;
	$len = strlen( $id );
	while ( $len > '2' ) {
		$result .= substr( $id, '0', '2' ) . '/';
		$id = substr( $id, '2' );
		$len = strlen( $id );
		if ( $makepath ) {
			$path = WPPA_UPLOAD_PATH . '/' . $result;
			if ( ! wppa_is_dir( $path ) ) wppa_mktree( $path );
			$path = WPPA_UPLOAD_PATH . '/thumbs/' . $result;
			if ( ! wppa_is_dir( $path ) ) wppa_mktree( $path );
		}
	}
	$result .= $id;
	return $result;
}

// Makes the html for the geo support for current theme and adds it to $wppa['geo']
function wppa_do_geo( $id, $location ) {
global $wppa;

	// Feature enabled?
	if ( ! wppa_switch( 'save_gpx' ) ) return;

	$temp 	= explode( '/', $location );
	$lat 	= $temp['2'];
	$lon 	= $temp['3'];

	$type 	= wppa_opt( 'gpx_implementation' );

	// Switch on implementation type
	switch ( $type ) {
		case 'external-plugin':
			$geo = str_replace( 'w#lon', $lon, str_replace( 'w#lat', $lat, wppa_opt( 'gpx_shortcode' ) ) );
			$geo = str_replace( 'w#ip', $_SERVER['REMOTE_ADDR'], $geo );
			$geo = str_replace( 'w#gmapikey', wppa_opt( 'map_apikey' ), $geo );

			$geo = do_shortcode( $geo );
			$wppa['geo'] .= '<div id="geodiv-' . wppa( 'mocc' ) . '-' . $id . '" style="display:none;">' . $geo . '</div>';
			break;
		case 'wppa-plus-embedded':
			if ( $wppa['geo'] == '' ) { 	// First
				$wppa['geo'] = '
<div id="map-canvas-' . wppa( 'mocc' ).'" style="height:' . wppa_opt( 'map_height' ) . 'px; width:100%; padding:0; margin:0; font-size: 10px;" ></div>
<script>
	if ( typeof ( _wppaLat ) == "undefined" ) { var _wppaLat = new Array();	var _wppaLon = new Array(); }
	if ( typeof ( _wppaRealId  ) == "undefined" ) { var _wppaRealId = new Array(); }
	_wppaLat[' . wppa( 'mocc' ) . '] = new Array(); _wppaLon[' . wppa( 'mocc' ) . '] = new Array();
</script>';
			}	// End first
			$wppa['geo'] .= '
<script>
	_wppaLat[' . wppa( 'mocc' ) . '][' . $id . '] = ' . $lat . ';
	_wppaLon[' . wppa( 'mocc' ) . '][' . $id.'] = ' . $lon . ';
</script>';
			break;	// End native
		case 'none':
			break;
		default:
			wppa_log( 'err', 'Unimplemented gpx_implementation: ' . $type . ' in wppa_do_geo()' );
			break;
	}
}

// See if an album is in a separate tree
function wppa_is_separate( $id ) {

	if ( $id == '' ) return false;
	if ( ! wppa_is_int( $id ) ) return false;
	if ( $id == '-1' ) return true;
	if ( $id < '1' ) return false;
	$alb = wppa_get_parentalbumid( $id );

	return wppa_is_separate( $alb );
}

// Get the albums parent
function wppa_get_parentalbumid($id) {
static $prev_album_id;

	if ( ! wppa_is_int($id) || $id < '1' ) return '0';

	$album = wppa_cache_album($id);
	if ( $album === false ) {
		wppa_log( 'error', 'Album '.$id.' no longer exists, but is still set as a parent of '.$prev_album_id.'. Please correct this.' );
		return '-9';	// Album does not exist
	}
	$prev_album_id = $id;
	return $album['a_parent'];
}

function wppa_html( $str ) {
// It is assumed that the raw data contains html.
// Make sure it is decoded

	$result = html_entity_decode( $str );
	return $result;
}


// get a photos album id
function wppa_get_album_id_by_photo_id( $id ) {

	if ( ! is_numeric($id) || $id < '1' ) wppa_dbg_msg('Invalid arg wppa_get_album_id_by_photo_id('.$id.')', 'red');
	$thumb = wppa_cache_thumb($id);
	return $thumb['album'];
}

function wppa_get_rating_count_by_id($id) {

	if ( ! is_numeric($id) || $id < '1' ) wppa_dbg_msg('Invalid arg wppa_get_rating_count_by_id('.$id.')', 'red');
	$thumb = wppa_cache_thumb($id);
	return $thumb['rating_count'];
}

function wppa_get_rating_by_id($id, $opt = '') {
global $wpdb;

	if ( ! is_numeric($id) || $id < '1' ) wppa_dbg_msg('Invalid arg wppa_get_rating_by_id('.$id.', '.$opt.')', 'red');
	$thumb = wppa_cache_thumb( $id );
	$rating = $thumb['mean_rating'];
	if ( $rating ) {
		$i = wppa_opt( 'rating_prec' );
		$j = $i + '1';
		$val = sprintf('%'.$j.'.'.$i.'f', $rating);
		if ($opt == 'nolabel') $result = $val;
		else $result = sprintf(__('Rating: %s', 'wp-photo-album-plus'), $val);
	}
	else $result = '';
	return $result;
}

function wppa_get_rating_total_by_id($id) {
global $wpdb;

	if ( ! is_numeric($id) || $id < '1' ) wppa_dbg_msg('Invalid arg wppa_get_rating_total_by_id('.$id.')', 'red');

	$result = $wpdb->get_var( $wpdb->prepare( "SELECT SUM(value) FROM $wpdb->wppa_rating WHERE photo = %d", $id ) );
	return $result;
}

function wppa_get_my_rating_by_id($id, $opt = '') {
global $wpdb;

	if ( ! is_numeric($id) || $id < '1' ) wppa_dbg_msg('Invalid arg wppa_get_my_rating_by_id('.$id.', '.$opt.')', 'red');

	$my_ratings = $wpdb->get_results( $wpdb->prepare( "SELECT value FROM $wpdb->wppa_rating WHERE photo = %d AND user = %s", $id, wppa_get_user() ), ARRAY_A );
	if ( $my_ratings ) {
		$rating = 0;
		foreach ( $my_ratings as $r ) {
			$rating += $r['value'];
		}
		$rating /= count( $my_ratings );
	}
	else {
		$rating = '0';
	}
	if ( $rating ) {
		$i = wppa_opt( 'rating_prec' );
		$j = $i + '1';
		$val = sprintf('%'.$j.'.'.$i.'f', $rating);
		if ($opt == 'nolabel') $result = $val;
		else $result = sprintf(__('Rating: %s', 'wp-photo-album-plus'), $val);
	}
	else $result = '0';
	return $result;
}

function wppa_switch( $xkey ) {
global $wppa_opt;

	// Are we initialized?
	if ( ! isset( $wppa_opt[$xkey] ) ) {
		if ( ! defined( 'WPPA_UPLOAD' ) ) {
			wppa_dump( 'wppa_switch('.$xkey.') requested before init constants' );
		}
		else {
			wppa_initialize_runtime();
		}
	}

	// Old style?
	if ( substr( $xkey, 0, 5 ) == 'wppa_' ) {
		wppa_log( 'Dbg', $xkey . ' used as old style switch', true );
		$key = $xkey;
	}
	else {
		$key = 'wppa_' . $xkey;
	}

	if ( isset( $wppa_opt[$key] ) ) {
		if ( $wppa_opt[$key] == 'yes' ) return true;
		elseif ( $wppa_opt[$key] == 'no' ) return false;
		else wppa_log( 'Dbg', '$wppa_opt['.$key.'] is not a yes/no setting', true );
		return $wppa_opt[$key]; // Return the right value afterall
	}

	wppa_log( 'Dbg', '$wppa_opt['.$key.'] is not a setting', true );

	return false;
}

function wppa_opt( $xkey ) {
global $wppa_opt;

	// Are we initialized?
	if ( ! isset( $wppa_opt[$xkey] ) ) {
		if ( ! defined( 'WPPA_UPLOAD' ) ) {
			wppa_dump( 'wppa_opt('.$xkey.') requested before init constants' );
		}
		else {
			wppa_initialize_runtime();
		}
	}

	// Old style?
	if ( substr( $xkey, 0, 5 ) == 'wppa_' ) {
		wppa_log( 'Dbg', $xkey . ' used as old style option', true );
		$key = $xkey;
	}
	else {
		$key = 'wppa_' . $xkey;
	}

	if ( isset( $wppa_opt[$key] ) ) {
		if ( $wppa_opt[$key] == 'yes' || $wppa_opt[$key] == 'no' ) {
			wppa_log( 'Dbg', '$wppa_opt['.$key.'] is a yes/no setting, not a value', true );
			return ( $wppa_opt[$key] == 'yes' ); // Return the right value afterall
		}
		return trim( $wppa_opt[$key] );
	}

	wppa_log( 'Dbg', '$wppa_opt['.$key.'] is not a setting', true );

	return false;
}

// Getter / setter of runtime parameter
function wppa( $key, $newval = 'nil' ) {
global $wppa;

	// Array defined?
	if ( ! is_array( $wppa ) ) {
		wppa_reset_occurrance();
	}

	// Invalid key?
	if ( ! isset( $wppa[$key] ) ) {

		// If index not exists: add it and report error if its not wppa-ajax and return false.
		if ( ! in_array( $key, array_keys( $wppa ) ) ) {
			if ( $key != 'wppa-ajax' ) {
				wppa_log( 'Err', '$wppa[\'' . $key . '\'] is not defined in reset_occurrance', true );
			}
			$wppa[$key] = false;
			return false;
		}

		// Exists but NULL, Not fatal
		else {
			wppa_log( 'War', '$wppa[\'' . $key . '\'] has value NULL', true );

			// NULL is illegal, replace it by false, to prevent many equal errormessages
			$wppa[$key] = false;
		}
	}

	// Existing key, Get old value
	$oldval = $wppa[$key];

	// New value supplied?
	if ( $newval !== 'nil' ) {
		$wppa[$key] = $newval;
	}

	// If mocc requested and in_widget, add 100 (fixes caching conflicts)
	if ( $key == 'mocc' && $wppa['in_widget'] ) {
		$oldval += 100;
	}

	return $oldval;
}

// Add (concat) value to runtime parameter
function wppa_add( $key, $newval ) {
global $wppa;

	// Array defined?
	if ( empty( $wppa ) ) {
		wppa_reset_occurrance();
	}

	// Valid key?
	if ( isset( $wppa[$key] ) ) {

		// Get old value
		$oldval = $wppa[$key];

		// Add new value
		$wppa[$key] .= $newval;
	}

	// Invalid key
	else {
		wppa_log( 'Err', '$wppa[\''.$key.'\'] is not defined', true );
		return false;
	}

	return $oldval;
}

function wppa_display_root( $id ) {
	$all = __('All albums', 'wp-photo-album-plus' );
	if ( ! $id || $id == '-2' ) return $all;
	$album = wppa_cache_album( $id );
	if ( ! $album ) return '';
	$albums = array();
	$albums[] = $album;
	$albums = wppa_add_paths( $albums );
	return $albums[0]['name'];
}

function wppa_add_paths( $albums ) {

	if ( is_array( $albums ) ) foreach ( array_keys( $albums ) as $index ) {
		$tempid = $albums[$index]['id'];
		$albums[$index]['name'] = __( stripslashes( $albums[$index]['name'] ) );	// Translate name
		while ( $tempid > '0' ) {
			$tempid = wppa_get_parentalbumid($tempid);
			if ( $tempid > '0' ) {
				$albums[$index]['name'] = wppa_get_album_name($tempid).' > '.$albums[$index]['name'];
			}
			elseif ( $tempid == '-1' ) $albums[$index]['name'] = '-s- '.$albums[$index]['name'];
		}
	}
	return $albums;
}

// Sort an array on a column, keeping the indexes
function wppa_array_sort( $array, $on, $order = SORT_ASC ) {

    $new_array = array();
    $sortable_array = array();

    if ( count( $array ) > 0 ) {
        foreach ( $array as $k => $v ) {
            if ( is_array( $v ) ) {
                foreach ( $v as $k2 => $v2 ) {
                    if ( $k2 == $on ) {
                        $sortable_array[$k] = $v2;
                    }
                }
            } else {
                $sortable_array[$k] = $v;
            }
        }

        switch ( $order ) {
            case SORT_DESC:
                arsort( $sortable_array );
            break;
            default: 	// case SORT_ASC:
                asort( $sortable_array );
            break;
       }

        foreach ( $sortable_array as $k => $v ) {
            $new_array[$k] = $array[$k];
        }
    }

    return $new_array;
}

function wppa_get_taglist( $translate = false ) {

	// Get the taglist if exists
	$result = WPPA_MULTISITE_GLOBAL ? get_site_option( 'wppa_taglist', 'nil' ) : wppa_get_option( 'wppa_taglist', 'nil' );

	// If absent, create it. Will get the extracted version
	if ( $result == 'nil' ) {
		$result = wppa_create_taglist(); 	// Is already extracted
	}

	// Not just created, extract the ids, if any
	else {
		if ( is_array( $result ) ) foreach ( array_keys( $result ) as $tag ) {
			$result[$tag]['ids'] = wppa_index_string_to_array( $result[$tag]['ids'] );
		}
	}

	// Translate keys ?
	if ( is_array( $result ) && $translate ) {
		$translated = array();
		foreach ( $result as $item ) {

			$translated[ ucfirst( __( $item['tag'] ) ) ] = $item;
		}
		$result = $translated;
		ksort( $result );
	}
	return $result;
}

function wppa_clear_taglist() {

	$result = WPPA_MULTISITE_GLOBAL ? update_site_option( 'wppa_taglist', 'nil' ) : update_option( 'wppa_taglist', 'nil' );
	$result = WPPA_MULTISITE_GLOBAL ? get_site_option( 'wppa_taglist', 'nil' ) : wppa_get_option( 'wppa_taglist', 'nil' );
	if ( $result != 'nil' ) {
		wppa_log( 'Warning', 'Could not clear taglist' ) ;
	}
}

function wppa_create_taglist() {
global $wpdb;

	// Init
	$time 	= time();
	$total 	= 0;

	// Exclude seps?
	$sep = str_replace( '.', ',', wppa_expand_enum( wppa_alb_to_enum_children( '-1' ) ) );
	if ( wppa_switch( 'excl_sep' ) && $sep ) {
		$alb_clause = "album > 0 AND album NOT IN (" . $sep . ")";
	}
	else {
		$alb_clause = "album > 0";
	}

	// Get the existing tags raw
	$raw_tagcol = $wpdb->get_col( "SELECT DISTINCT tags FROM $wpdb->wppa_photos
								   WHERE status NOT IN ('pending','scheduled')
								   AND $alb_clause
								   AND tags <> ''" );
	$raw_tags 	= implode( ',', $raw_tagcol ) . wppa_opt( 'minimum_tags' );
	$san_tags 	= trim( wppa_sanitize_tags( $raw_tags ), ',' );
	$tag_arr 	= explode( ',', $san_tags );

	// Process all existing tags
	if ( count( $tag_arr ) ) {
		$result = array();
		foreach( $tag_arr as $tag )  {
			$result[$tag]['tag'] = $tag;
			$result[$tag]['ids'] = $wpdb->get_col( $wpdb->prepare( "SELECT id FROM $wpdb->wppa_photos
																	WHERE status NOT IN ('pending','scheduled')
																	AND $alb_clause
																	AND tags LIKE %s", '%' . str_replace( "'", "\'", ',' . $wpdb->esc_like( $tag ) . ',' ) . '%' ) );
			$result[$tag]['count'] = count( $result[$tag]['ids'] );
			$total += $result[$tag]['count'];
		}
	}
	else {
		$result = false;
	}

	// If any tags found, calculate fractions
	$tosave = array();
	if ( is_array( $result ) ) {
		foreach ( array_keys( $result ) as $key ) {
			$result[$key]['fraction'] = $total ? sprintf( '%4.2f', $result[$key]['count'] / $total ) : '0.00';
		}
		$tosave = $result;

		// Convert the arrays to compressed enumerations
		foreach ( array_keys( $tosave ) as $key ) {
			$tosave[$key]['ids'] = wppa_index_array_to_string( $tosave[$key]['ids'] );
		}
	}

	// Save the new taglist
	$bret = WPPA_MULTISITE_GLOBAL ? update_site_option( 'wppa_taglist', $tosave ) : update_option( 'wppa_taglist', $tosave );
//	if ( ! $bret ) {	// No change also rturns false
//		wppa_log( 'Err', 'Unable to save taglist' );
//	}

	$dtime = time() - $time;
	$mem = memory_get_peak_usage( true );
	wppa_log( 'dbg', "Creating taglist took $dtime seconds and $mem bytes memory" );

	// And return the result
	return $result;
}

function wppa_get_catlist() {

	$result = WPPA_MULTISITE_GLOBAL ? get_site_option( 'wppa_catlist', 'nil' ) : wppa_get_option( 'wppa_catlist', 'nil' );
	if ( $result == 'nil' ) {
		$result = wppa_create_catlist();
	}
	else {
		foreach ( array_keys($result) as $cat ) {
			$result[$cat]['ids'] = wppa_index_string_to_array($result[$cat]['ids']);
		}
	}
	return $result;
}

function wppa_clear_catlist() {

	$result = WPPA_MULTISITE_GLOBAL ? update_site_option( 'wppa_catlist', 'nil' ) : update_option( 'wppa_catlist', 'nil' );
	$result = WPPA_MULTISITE_GLOBAL ? get_site_option( 'wppa_catlist', 'nil' ) : wppa_get_option( 'wppa_catlist', 'nil' );
	if ( $result != 'nil' ) {
		wppa_log( 'Warning', 'Could not clear catlist' ) ;
	}
}

function wppa_create_catlist() {
global $wpdb;

	$result = false;
	$total = '0';
	$albums = $wpdb->get_results("SELECT id, cats FROM $wpdb->wppa_albums WHERE cats <> ''", ARRAY_A);
	if ( $albums ) foreach ( $albums as $album ) {
		$cats = explode(',', $album['cats']);
		if ( $cats ) foreach ( $cats as $cat ) {
			if ( $cat ) {
				if ( ! isset($result[$cat]) ) {	// A new cat
					$result[$cat]['cat'] = $cat;
					$result[$cat]['count'] = '1';
					$result[$cat]['ids'][] = $album['id'];
				}
				else {							// An existing cat
					$result[$cat]['count']++;
					$result[$cat]['ids'][] = $album['id'];
				}
			}
			$total++;
		}
	}
	$tosave = array();
	if ( is_array($result) ) {
		foreach ( array_keys($result) as $key ) {
			$result[$key]['fraction'] = sprintf('%4.2f', $result[$key]['count'] / $total);
		}
		$result = wppa_array_sort($result, 'cat');
		$tosave = $result;
		foreach ( array_keys($tosave) as $key ) {
			$tosave[$key]['ids'] = wppa_index_array_to_string($tosave[$key]['ids']);
		}
	}
	$bret = WPPA_MULTISITE_GLOBAL ? update_site_option( 'wppa_catlist', $tosave ) : update_option( 'wppa_catlist', $tosave );
	if ( ! $bret ) {
		wppa_log( 'Err', 'Unable to save catlist' );
	}
	return $result;
}

function wppa_update_option( $option, $value ) {
global $wppa_opt;

	// Update the option
	update_option( $option, $value );

	// Update the local cache
	$wppa_opt[$option] = $value;

	// Too early?
	if ( ! defined( 'WPPA_UPLOAD_PATH' ) ) {
		return;
	}

	// Remove init.js files, they will be auto re-created
	$files = wppa_glob( WPPA_UPLOAD_PATH . '/dynamic/wppa-init.*.js' );
	if ( $files ) {
		foreach ( $files as $file ) {
			wppa_unlink ( $file );
		}
	}

	// Remove dynamic css files, they will be auto re-created
	if ( is_file ( WPPA_UPLOAD_PATH . '/dynamic/wppa-dynamic.css' ) ) {
		wppa_unlink ( WPPA_UPLOAD_PATH . '/dynamic/wppa-dynamic.css' );
	}
}

function wppa_album_exists( $id ) {
global $wpdb;
static $existing_albums;

	if ( ! wppa_is_int( $id ) ) {
		return false;
	}

	// If existing albums cache not filled yet, fill it.
	if ( ! $existing_albums ) {
		$existing_albums = $wpdb->get_col( "SELECT id FROM $wpdb->wppa_albums" );
	}

	return in_array( $id, $existing_albums );
}

function wppa_photo_exists( $id ) {
global $wpdb;

	if ( ! wppa_is_int( $id ) ) {
		return false;
	}
	return $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->wppa_photos WHERE id = %s", $id ) );
}

function wppa_albumphoto_exists($alb, $photo) {
global $wpdb;
	return $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->wppa_photos WHERE album = %s AND filename = %s", $alb, $photo));
}

function wppa_dislike_check($photo) {
global $wpdb;

	$count = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->wppa_rating WHERE photo = %s AND value = -1", $photo ));

	if ( wppa_opt( 'dislike_mail_every' ) > '0') {		// Feature enabled?
		if ( $count % wppa_opt( 'dislike_mail_every' ) == '0' ) {	// Mail the admin
			$to        = get_bloginfo('admin_email');
			$subj 	   = __('Notification of inappropriate image', 'wp-photo-album-plus');
			$cont['0'] = sprintf(__('Photo %s has been marked as inappropriate by %s different visitors.', 'wp-photo-album-plus'), $photo, $count);
			$cont['1'] = '<a href="'.get_admin_url().'admin.php?page=wppa_admin_menu&tab=pmod&photo='.$photo.'" >'.__('Manage photo', 'wp-photo-album-plus').'</a>';
			wppa_send_mail( array( 'to' 	=> $to,
								   'subj' 	=> $subj,
								   'cont' 	=> $cont,
								   'photo' 	=> $photo,
								   ));
		}
	}

	if ( wppa_opt( 'dislike_set_pending' ) > '0') {		// Feature enabled?
		if ( $count == wppa_opt( 'dislike_set_pending' ) ) {
			$wpdb->query($wpdb->prepare( "UPDATE $wpdb->wppa_photos SET status = 'pending' WHERE id = %s", $photo ));
			$to        = get_bloginfo('admin_email');
			$subj 	   = __('Notification of inappropriate image', 'wp-photo-album-plus');
			$cont['0'] = sprintf(__('Photo %s has been marked as inappropriate by %s different visitors.', 'wp-photo-album-plus'), $photo, $count);
			$cont['0'] .= "\n".__('The status has been changed to \'pending\'.', 'wp-photo-album-plus');
			$cont['1'] = '<a href="'.get_admin_url().'admin.php?page=wppa_admin_menu&tab=pmod&photo='.$photo.'" >'.__('Manage photo', 'wp-photo-album-plus').'</a>';
			wppa_send_mail( array( 'to' 	=> $to,
								   'subj' 	=> $subj,
								   'cont' 	=> $cont,
								   'photo' 	=> $photo,
								   ));
		}
	}

	if ( wppa_opt( 'dislike_delete' ) > '0') {			// Feature enabled?
		if ( $count == wppa_opt( 'dislike_delete' ) ) {
			$to        = get_bloginfo('admin_email');
			$subj 	   = __('Notification of inappropriate image', 'wp-photo-album-plus');
			$cont['0'] = sprintf(__('Photo %s has been marked as inappropriate by %s different visitors.', 'wp-photo-album-plus'), $photo, $count);
			$cont['0'] .= "\n".__('It has been deleted.', 'wp-photo-album-plus');
			$cont['1'] = '';//<a href="'.get_admin_url().'admin.php?page=wppa_admin_menu&tab=pmod&photo='.$photo.'" >'.__('Manage photo').'</a>';
			wppa_send_mail( array( 'to' 	=> $to,
								   'subj' 	=> $subj,
								   'cont' 	=> $cont,
								   'photo' 	=> $photo,
								   ));
			wppa_delete_photo($photo);
		}
	}
}


// Get number of dislikes for a given photo id
function wppa_dislike_get( $id ) {
global $wpdb;

	$count = $wpdb->get_var( $wpdb->prepare( 	"SELECT COUNT(*) " .
												"FROM $wpdb->wppa_rating " .
												"WHERE photo = %s " .
												"AND value = -1",
												$id
											)
							);
	return $count;
}

// Get number of pending ratings for a given photo id
function wppa_pendrat_get( $id ) {
global $wpdb;

	$count = $wpdb->get_var( $wpdb->prepare( 	"SELECT COUNT(*) " .
												"FROM $wpdb->wppa_rating " .
												"WHERE photo = %s AND " .
												"status = 'pending'",
												$id
											)
							);
	return $count;
}



function wppa_get_imgalt( $id, $lb = false ) {

	// Get photo data
	$thumb = wppa_cache_thumb( $id );

	// Get raw image alt data
	switch ( wppa_opt( 'alt_type' ) ) {
		case 'fullname':
			$result = wppa_get_photo_name( $id );
			break;
		case 'namenoext':
			$result = wppa_strip_ext( wppa_get_photo_name( $id ) );
			break;
		case 'custom':
			$result = $thumb['alt'];
			break;
		default:
			$result = $id;
			break;
	}

	// Default if empty result
	if ( ! $result ) {
		$result = '0';
	}

	// Format for use in lightbox or direct use html
	if ( $lb ) {
		$result = esc_attr( str_replace( '"', "'", $result ) );
	}
	else {
		$result = ' alt="' . esc_attr( $result ) . '" ';
	}

	return $result;
}


function wppa_is_time_up( $count = '' ) {
global $wppa_endtime;

	if ( ! $wppa_endtime ) {
		wppa_log( 'err', 'Zero endtime, set to 25 secs after now' );
		$wppa_endtime = time() + 55;
	}

	// Time up?
	if ( $wppa_endtime > time() ) {
		return false;	// No
	}

	// Time is up. If cron return silently true
	if ( wppa_is_cron() ) {
		return true;
	}

	// Not cron, leave a message optionally and retrun true
	if ( $count ) {
		if ( is_admin() ) {
			wppa_warning_message( sprintf( __( 'Time out after processing %s items.', 'wp-photo-album-plus' ), $count ) );
		}
		else {
			wppa_alert( sprintf( __( 'Time out after processing %s items. Please restart this operation', 'wp-photo-album-plus' ), $count ) );
		}
	}
	return true;
}

// Update photo modified timestamp
function wppa_update_modified( $photo ) {
global $wpdb;
	$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->wppa_photos SET modified = %s WHERE id = %s", time(), $photo ) );
}

function wppa_nl_to_txt($text) {
	return str_replace("\n", "\\n", $text);
}
function wppa_txt_to_nl($text) {
	return str_replace('\n', "\n", $text);
}

// Check query arg on tags, return value if valid
function wppa_vfy_arg( $arg, $txt = false ) {
	if ( wppa_get( $arg ) ) {
		if ( $txt ) {	// Text is allowed, but without tags
			$reason = ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? ': ' . $arg . ' contains tags.' : '';
			if ( wppa_get( $arg ) != strip_tags( wppa_get( $arg ) ) ) {
				wp_die( 'Security check failue ' . $reason );
			}
			return wppa_get( $arg );
		}
		else {
			$reason = ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? ': ' . $arg . ' is not numeric, its '.wppa_get( $arg ) : '';
			$value = wppa_get( $arg );

			if ( ! is_numeric( $value ) ) {
				wp_die( 'Security check failue ' . $reason );
			}
			return $value;
		}
	}
	else {
		return '';
	}
}

// Strip tags with content
function wppa_strip_tags($text, $key = '') {

	if ($key == 'all') {
		$text = preg_replace(	array	(	'@<a[^>]*?>.*?</a>@siu',				// unescaped <a> tag
											'@&lt;a[^>]*?&gt;.*?&lt;/a&gt;@siu',	// escaped <a> tag
											'@<table[^>]*?>.*?</table>@siu',
											'@<style[^>]*?>.*?</style>@siu',
											'@<div[^>]*?>.*?</div>@siu'
										),
								array	( ' ', ' ', ' ', ' ', ' '
										),
								$text );
		$text = str_replace(array('<br/>', '<br />'), ' ', $text);
		$text = strip_tags($text);
	}
	elseif ( $key == 'script' ) {
		$text = preg_replace('@<script[^>]*?>.*?</script>@siu', ' ', $text );
	}
	elseif ( $key == 'div' ) {
		$text = preg_replace('@<div[^>]*?>.*?</div>@siu', ' ', $text );
	}
	elseif ( $key == 'script&style' || $key == 'style&script' ) {
		$text = preg_replace(	array	(	'@<script[^>]*?>.*?</script>@siu',
											'@<style[^>]*?>.*?</style>@siu'
										),
								array	( ' ', ' '
										),
								$text );
	}
	else {
		$text = preg_replace(	array	(	'@<a[^>]*?>.*?</a>@siu',				// unescaped <a> tag
											'@&lt;a[^>]*?&gt;.*?&lt;/a&gt;@siu'		// escaped <a> tag
										),
								array	( ' ', ' '
										),
								$text );
	}
	return trim($text);
}

// set last album
function wppa_set_last_album( $id = '' ) {

	if ( wppa_is_int( $id ) ) {
		update_option( 'wppa_last_album_used-' . wppa_get_user( 'login' ), $id );
	}
}

// get last album
function wppa_get_last_album() {

	$album = wppa_get_option( 'wppa_last_album_used-' . wppa_get_user( 'login' ), '0' );
	if ( ! wppa_album_exists( $album ) ) {
		$album = false;
	}
    return $album;
}

// Combine margin or padding style
function wppa_combine_style($type, $top = '0', $left = '0', $right = '0', $bottom = '0') {
// echo $top.' '.$left.' '.$right.' '.$bottom.'<br />';
	$result = $type.':';			// Either 'margin:' or 'padding:'
	if ( $left == $right ) {
		if ( $top == $bottom ) {
			if ( $top == $left ) {	// All the same: one size fits all
				$result .= $top;
				if ( is_numeric($top) && $top > '0' ) $result .= 'px';
			}
			else {					// Top=Bot and Lft=Rht: two sizes
				$result .= $top;
				if ( is_numeric($top) && $top > '0' ) $result .= 'px '; else $result .= ' ';
				$result .= $left;
				if ( is_numeric($left) && $left > '0' ) $result .= 'px';
			}
		}
		else {						// Top, Lft=Rht, Bot: 3 sizes
			$result .= $top;
			if ( is_numeric($top) && $top > '0' ) $result .= 'px '; else $result .= ' ';
			$result .= $left;
			if ( is_numeric($left) && $left > '0' ) $result .= 'px '; else $result .= ' ';
			$result .= $bottom;
			if ( is_numeric($bottom) && $bottom > '0' ) $result .= 'px';
		}
	}
	else {							// Top, Rht, Bot, Lft: 4 sizes
		$result .= $top;
		if ( is_numeric($top) && $top > '0' ) $result .= 'px '; else $result .= ' ';
		$result .= $right;
		if ( is_numeric($right) && $right > '0' ) $result .= 'px '; else $result .= ' ';
		$result .= $bottom;
		if ( is_numeric($bottom) && $bottom > '0' ) $result .= 'px '; else $result .= ' ';
		$result .= $left;
		if ( is_numeric($left) && $left > '0' ) $result .= 'px';
	}
	$result .= ';';
	return $result;
}

// A temp routine to fix an old bug
function wppa_fix_source_extensions() {
global $wpdb;

	$start_time = time();
	$end = $start_time + '15';
	$count = '0';
	$start = wppa_get_option('wppa_sourcefile_fix_start', '0');
	if ( $start == '-1' ) return; // Done!

	$photos = $wpdb->get_results( 	"SELECT id, album, name, filename" .
										" FROM $wpdb->wppa_photos" .
										" WHERE filename <> ''  AND filename <> name AND id > " . $start .
										" ORDER BY id", ARRAY_A
								);
	if ( $photos ) {
		foreach ( $photos as $data ) {
			$faulty_sourcefile_name = wppa_opt( 'source_dir' ).'/album-'.$data['album'].'/'.preg_replace('/\.[^.]*$/', '', $data['filename']);
			if ( is_file($faulty_sourcefile_name) ) {
				$proper_sourcefile_name = wppa_opt( 'source_dir' ).'/album-'.$data['album'].'/'.$data['filename'];
				wppa_rename($faulty_sourcefile_name, $proper_sourcefile_name);
				$count++;
			}
			if ( time() > $end ) {
				wppa_ok_message( 'Fixed ' . $count . ' faulty sourcefile names.' .
									' Last was ' . $data['id'] . '.' .
									' Not finished yet. I will continue fixing next time you enter this page. Sorry for the inconvenience.'
								);

				update_option('wppa_sourcefile_fix_start', $data['id']);
				return;
			}
		}
	}
	echo $count.' source file extensions repaired';
	update_option('wppa_sourcefile_fix_start', '-1');
}

// Delete a photo and all its attrs by id
function wppa_delete_photo( $photo ) {
global $wppa_supported_audio_extensions;
global $wppa_supported_video_extensions;
global $wpdb;

	// Sanitize arg
	$photo = strval( intval( $photo ) );
	$photoinfo = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->wppa_photos
												  WHERE id = %d", $photo ), ARRAY_A );

	// If still in use, refuse deletion
	$in_use = $wpdb->get_row( $wpdb->prepare( "SELECT ID, post_title FROM $wpdb->posts
											   WHERE post_content LIKE %s
											   AND post_status = 'publish' LIMIT 1", '%' . $wpdb->esc_like( 'photo="' . $photo . '"' ) . '%' ), ARRAY_A );

	if ( is_array( $in_use ) ) {

		if ( defined( 'DOING_AJAX' ) ) {
			echo
			'ER||0||' .
			'<span style="color:#ff0000;" >' .
				__( 'Could not delete photo', 'wp-photo-album-plus' ) .
			'</span>||' .
			__( 'Photo is still in use in post/page', 'wp-photo-album-plus' ) .
				' ' .
			$in_use['post_title'] .
			' (' . $in_use['ID'] . ')';
			wppa_exit();
		}
		else {
			wppa_error_message( __( 'Photo is still in use in post/page', 'wp-photo-album-plus' ) . ' ' . $in_use['post_title'] . ' (' . $in_use['ID'] . ')' );
			return false;
		}
	}

	// Get album
	$album = $photoinfo['album'];

	// Really delete only as cron job
	if ( ! wppa_is_cron() ) {
		if ( $album > '0' ) {
			$newalb = - ( $album + '9' );
			wppa_update_photo( array( 'id' => $photo, 'album' => $newalb, 'modified' => time() ) );
			wppa_mark_treecounts( $album );
			wppa_schedule_cleanup( 'now' );
			wppa_clear_taglist();
		}
		return;
	}

	// Restore orig album #
	$album = - ( $album + '9' );

	// Delete multimedia files
	if ( wppa_is_multi( $photo ) ) {
		$mmfile = wppa_strip_ext( wppa_get_photo_path( $photo, false ) );
		$allsup = array_merge( $wppa_supported_audio_extensions, $wppa_supported_video_extensions );
		foreach( $allsup as $mmext ) {
			if ( is_file( $mmfile . '.' . $mmext ) ) {
				wppa_unlink( $mmfile . '.' . $mmext );
			}
		}
	}

	// If still a photo with the same name exists in the original album, do not delete the source
	$still_exists = $wpdb->get_var( $wpdb->prepare(
		"SELECT COUNT(*) FROM $wpdb->wppa_photos
		 WHERE ( filename = %s OR filename = %s )
		 AND album = %s",
		$photoinfo['filename'],
		wppa_strip_ext( $photoinfo['filename'] ) . '.xxx', 	// May be a multimedia iten
		$album
		) );

	if ( ! $still_exists ) {

		// Delete sourcefile
		wppa_delete_source( $photoinfo['filename'], $album );
	}

	// Delete fullsize image
	$file = wppa_get_photo_path( $photo );
	if ( is_file( $file ) ) wppa_unlink( $file );

	// Delete thumbnail image
	$file = wppa_get_thumb_path( $photo );
	if ( is_file( $file ) ) wppa_unlink( $file );

	// Delete index
	wppa_index_remove('photo', $photo);

	// Delete db entries
	$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->wppa_photos WHERE id = %d", $photo ) );
	$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->wppa_rating WHERE photo = %s", $photo ) );
	$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->wppa_comments WHERE photo = %s", $photo ) );
	$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->wppa_iptc WHERE photo = %s", $photo ) );
	$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->wppa_exif WHERE photo = %s", $photo ) );
	wppa_invalidate_treecounts( $album );
	wppa_flush_upldr_cache( 'photoid', $photo );

	// Clear taglist to trigger recreata
	wppa_clear_taglist();

	// Delete from cloud
	if ( wppa_cdn( 'admin' ) == 'cloudinary' ) {
		wppa_delete_from_cloudinary( $photo );
	}
	elseif ( wppa_cdn( 'admin' ) == 'local' ) {
		wppa_cdn_delete( $photo );
	}
}

function wppa_microtime($txt = '') {
static $old;

	$new = microtime(true);
	if ( $old ) {
		$delta = $new - $old;
		$old = $new;
		$msg = sprintf('%s took %7.3f s.', $txt, $delta);
		wppa_dbg_msg($msg, 'green', true);
	}
	else $old = $new;
}

function wppa_sanitize_cats( $value ) {
	return wppa_sanitize_tags( $value );
}
function wppa_sanitize_tags( $value, $keepsemi = false, $keephash = false ) {

	// Sanitize
	$value = sanitize_text_field( $value );
//	$value = strip_tags( $value );					// Security

	$value = str_replace( 	array( 					// Remove funny chars
									'"',
							//		'\'',
									'\\',
									'@',
									'?',
									'|',
								 ),
							'',
							$value
						);
	if ( ! $keephash ) {
		$value = str_replace( '#', '', $value );
	}

	$value = stripslashes($value);					// ...

	// Find separator
	$sep = ',';										// Default seperator
	if ( $keepsemi ) {								// ';' allowed
		if ( strpos($value, ';') !== false ) {		// and found at least one ';'
			$value = str_replace(',', ';', $value);	// convert all separators to ';'
			$sep = ';';
		}											// ... a mix is not permitted
	}
	else {
		$value = str_replace(';', ',', $value);		// Convert all seps to default separator ','
	}

	$temp = explode( $sep, $value );
	if ( is_array($temp) ) {

		// Trim
		foreach ( array_keys( $temp ) as $idx ) {
			$temp[$idx] = trim( $temp[$idx] );
		}

		// Capitalize single words within tags
		// Can not use wppa_switch because its used in wppa_get()
//		if ( wppa_switch( 'capitalize_tags' ) ) {
		if ( get_option( 'wppa_capitalize_tags', 'yes' ) == 'yes' ) {
			foreach ( array_keys($temp) as $idx ) {
				if ( strlen( $temp[$idx] ) > '1' ) {
					$words = explode( ' ', $temp[$idx] );
					foreach( array_keys($words) as $i ) {
						$words[$i] = ucfirst( strtolower( $words[$i] ) );
					}
					$temp[$idx] = implode(' ', $words);
				}
			}
		}

		// Capitalize exif tags
		foreach ( array_keys( $temp ) as $idx ) {
			if ( substr( $temp[$idx], 0, 2 ) == 'E#' ) {
				$temp[$idx] = strtoupper( $temp[$idx] );
			}
		}

		// Capitalize GPX and HD tags
		foreach ( array_keys( $temp ) as $idx ) {
			if ( in_array( $temp[$idx], array( 'Gpx', 'Hd' ) ) ) {
				$temp[$idx] = strtoupper( $temp[$idx] );
			}
		}

		// Sort
		asort( $temp );

		// Remove dups and recombine
		$value = '';
		$first = true;
		$previdx = '';
		foreach ( array_keys($temp) as $idx ) {
			if ( strlen( $temp[$idx] ) > '1' ) {

				// Remove duplicates
				if ( $temp[$idx] ) {
					if ( $first ) {
						$first = false;
						$value .= $temp[$idx];
						$previdx = $idx;
					}
					elseif ( $temp[$idx] !=  $temp[$previdx] ) {
						$value .= $sep.$temp[$idx];
						$previdx = $idx;
					}
				}
			}
		}
	}

	if ( $sep == ',' && $value != '' ) {
		$value = $sep . $value . $sep;
	}
	return $value;
}

// Does the same as wppa_index_string_to_array() but with format validation and error reporting
function wppa_series_to_array($xtxt) {
	if ( is_array( $xtxt ) ) return false;
	$txt = str_replace(' ', '', $xtxt);					// Remove spaces
	if ( strpos($txt, '.') === false ) return false;	// Not an enum/series, the only legal way to return false
	if ( strpos($txt, '...') !== false ) {
		wppa_stx_err('Max 2 successive dots allowed. '.$txt);
		return false;
	}
	if ( substr($txt, 0, 1) == '.' ) {
		wppa_stx_err('Missing starting number. '.$txt);
		return false;
	}
	if ( substr($txt, -1) == '.' ) {
		wppa_stx_err('Missing ending number. '.$txt);
		return false;
	}
	$t = str_replace(array('.','0','1','2','3','4','5','6','7','8','9'), '',$txt);
	if ( $t ) {
		wppa_stx_err('Illegal character(s): "'.$t.'" found. '.$txt);
		return false;
	}

	// Trim leading '0.'
	if ( substr( $txt, 0, 2 ) == '0.' ) {
		$txt = substr( $txt, 2 );
	}

	$temp = explode('.', $txt);
	$tempcopy = $temp;

	foreach ( array_keys($temp) as $i ) {
		if ( ! $temp[$i] ) { 							// found a '..'
			if ( $temp[$i-'1'] >= $temp[$i+'1'] ) {
				wppa_stx_err('Start > end. '.$txt);
				return false;
			}
			for ( $j=$temp[$i-'1']+'1'; $j<$temp[$i+'1']; $j++ ) {
				$tempcopy[] = $j;
			}
		}
		else {
			if ( ! is_numeric($temp[$i] ) ) {
				wppa_stx_err('A enum or range token must be a number. '.$txt);
				return false;
			}
		}
	}
	$result = $tempcopy;
	foreach ( array_keys($result) as $i ) {
		if ( ! $result[$i] ) unset($result[$i]);
	}
	return $result;
}
function wppa_stx_err($msg) {
	echo 'Syntax error in album specification. '.$msg;
	wppa_log( 'err', $msg, true );
}


function wppa_get_og_desc( $id, $short = false ) {

	if ( $short ) {
		$result = 	strip_shortcodes( wppa_strip_tags( wppa_html( wppa_get_photo_desc( $id ) ), 'all' ) );
		if ( ! $result ) {
			$result = str_replace( '&amp;', __( 'and', 'wp-photo-album-plus'), get_bloginfo( 'name' ) );
		}
	}
	else {
		$result = 	sprintf( __('See this image on %s', 'wp-photo-album-plus'), str_replace( '&amp;', __( 'and', 'wp-photo-album-plus'), get_bloginfo( 'name' ) ) ) .
					': ' .
					strip_shortcodes( wppa_strip_tags( wppa_html( wppa_get_photo_desc( $id ) ), 'all' ) );
	}

	$result = 	apply_filters( 'wppa_get_og_desc', $result );

	return $result;
}

// There is no php routine to test if a string var is an integer, like '3': yes, and '3.7' and '3..7': no.
// is_numeric('3.7') returns true
// intval('3..7') == '3..7' returns true
// is_int('3') returns false
// so we make it ourselves
function wppa_is_int( $var ) {
	if ( is_array( $var ) ) {
		return false;
	}
	return ( strval(intval($var)) == strval($var) );
}

// return true if $var only contains digits and points
function wppa_is_enum( $var ) {
	return '' === str_replace( array( '0','1','2','3','4','5','6','7','8','9','.' ), '', $var );
}

// Log a wppa message.
// We use wppa_get_option() here to prevent wppa_switch() to generate messages itsself.
// Also, we do not use the wppa filesystem function wrappers, to prevent recursive error logging
function wppa_log( $xtype, $msg, $trace = false, $listuri = false ) {
global $wppa_session;
global $wppa_log_file;
global $wppa_current_shortcode;
static $busy;
static $last_msg;
static $last_type;
static $repeat_count;

	// Do not log during plugin activation or update
	if ( strpos( $_SERVER['REQUEST_URI'], '/wp-admin/plugins.php' ) !== false ) {
		return;
	}
	if ( strpos( $_SERVER['REQUEST_URI'], '/wp-admin/update-core.php' ) !== false ) {
		return;
	}

	// Do not log logdisplays being updated
	if ( wppa_get( 'slug' ) == 'wppa_list_errorlog' ) {
		return;
	}

	// Test for recursive logging
	if ( $busy ) {
		update_option( 'wppa_recursive_log', $xtype . ' ' . $msg );
		return;
	}
	$busy = true;

	// Init
	$err = false;

	// Sanitize type
	$t = strtolower( substr( $xtype, 0, 1 ) );
	$u = strtolower( substr( $xtype, 1, 1 ) );
	switch ( $t ) {
		case 'a':
			$type = '{span style="color:blue;" }Ajax{/span}';
			break;
		case 'c':
			switch ( $u ) {
				case 'r':	// Cron
					if ( wppa_get_option( 'wppa_log_cron' ) == 'no' ) {
						$busy = false;
						return;
					}
					$type = '{span style="color:blue;" }Cron{/span}';
					break;
				case 'o':	// Comments
					if ( wppa_get_option( 'wppa_log_comments' ) == 'no' ) {
						$busy = false;
						return;
					}
					$type = '{span style="color:cyan;" }Com{/span}';
					break;
				case 'l': 	// Client
					$type = '{span style="color:red;" }Client{/span}';
					break;
				default: 		// Should never get here
					$busy = false;
					return;
					break;
			}
			break;
		case 'd':
			if ( wppa_get_option( 'wppa_log_debug' ) == 'no' ) {
				$busy = false;
				return;
			}
			$type = '{span style="color:gray;" }Dbg{/span}';
			break;
		case 'e':
			switch( $u ) {
				case 'r':
					$type = '{span style="color:red;" }Err{/span}';
					$err = true;
					break;
				case 'm':
					if ( wppa_get_option( 'wppa_log_email' ) == 'no' ) {
						$busy = false;
						return;
					}
					$type = '{span style="color:blue;" }Eml{/span}';
					break;
				default: 		// Should never get here
					$busy = false;
					return;
					break;
			}
			break;
		case 'f':
			switch ( $u ) {
				case 's':
					if ( wppa_get_option( 'wppa_log_fso' ) == 'no' ) {
						$busy = false;
						return;
					}
					$type = '{span style="color:blue;" }Fso{/span}';
					break;
				case 'i':
					$type = 'Fix';
					break;
				default: 		// Should never get here
					$busy = false;
					return;
					break;
			}
			break;
		case 'o':
			$type = 'Obs';
			break;
		case 't':
			if ( wppa_get_option( 'wppa_log_tim' ) == 'no' ) {
				$busy = false;
				return;
			}
			$type = '{span style="color:darkgreen;" }Tim{/span}';
			break;
		case 'u':
			$type = 'Upl';
			break;
		case 'w':
			$type = '{span style="color:orange;" }War{/span}';
			break;
		default:
			$type = 'Misc';
	}

	// Log debug messages only if WP_DEBUG is defined as true
	if ( $type == 'Dbg' ) {
		if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
			$busy = false;
			return;
		}
	}

	// Get existing log if it exists
	if ( $wppa_log_file && wppa_is_file( $wppa_log_file, false ) ) {

		$contents = wppa_get_contents_array( $wppa_log_file, false ); // Do not log error on read

		if ( is_array( $contents ) ) {

			// See if max size exceeded, if so, remove 10 items at the start
			if ( count( $contents ) > 1000 ) {
				$contents = array_slice( $contents, 10 );
			}
		}
		else {
			$contents = array();
		}
	}
	else {
		$contents = array();
	}

	// Check for repeated msg;
	$rep_msg = '';
	if ( ! $last_msg ) {
		$last_msg = $msg;
		$last_type = $xtype;
		$repeat_count = 0;
	}
	else {
		if ( $last_msg == $msg ) {
			$repeat_count++;
			$busy = false;
			return;
		}
		else {
			if ( $repeat_count ) {
				$rep_msg = 'Last message repeated ' . $repeat_count . ' times';
				$last_msg = '';
			}
		}
	}

	// Write repeat message
	if ( $rep_msg ) {
		array_push( $contents, '{b}'.$type.'{/b}: on:'.wppa_local_date( 'd.m.Y H:i:s', time()).': '.wppa_get_user().': '.$rep_msg. "\n" );
	}

	// Write log message
	$msg = strip_tags( $msg );
	$msg = wppa_nl2sp( $msg );
	if ( $err && $wppa_current_shortcode ) {
		$msg .= ' related shortcode: ' . $wppa_current_shortcode;
	}
	array_push( $contents, '{b}'.$type.'{/b}: on:'.wppa_local_date( 'd.m.Y H:i:s', time()).': '.wppa_get_user().' ('.getmypid().'): '.$msg. "\n" );

	// Log stacktrace 20 levels
	if ( $trace ) {

		$data = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 20 );
		$traceline = '';
		if ( is_array( $data ) ) {
			$i = 1;
			while ( $i < count( $data ) ) {
				$traceline .= ( $i > 1 ? '&lt;- ' : '' ) .
				( isset( $data[$i]['file'] ) ? basename( $data[$i]['file'] ) . ':' : '' ) .
				( isset( $data[$i]['line'] ) ? $data[$i]['line'] . ' ' : '' ) .
				( isset( $data[$i]['function'] ) ? $data[$i]['function'] . '() ' : '' );
				$i++;
			}
		}
		array_push( $contents, $traceline . "\n" );
	}

	// Trace on or error? log url
	if ( $trace || strtolower( substr( $xtype, 0, 2 ) ) == 'er' ) {
		array_push( $contents, '{b}url{/b}: '.$_SERVER['REQUEST_URI']."\n" );
	}

	// Add uri history
	if ( $listuri ) {
		array_push( $contents, 'Uri history:'."\n" );
		if ( is_array( $wppa_session ) ) {
			foreach ( $wppa_session['uris'] as $uri ) {
				array_push( $contents, $uri . "\n" );
			}
			array_push( $contents, "\n\n" );
		}
	}

	// Done
	$txt = implode( '', $contents );
	if ( $wppa_log_file ) {
		wppa_put_contents( $wppa_log_file, $txt, false );
	}
	else {
		update_option( 'wppa_last_error', $txt );
	}
	$busy = false;
}

function wppa_is_landscape($img_attr) {
	return ($img_attr[0] > $img_attr[1]);
}

function wppa_get_the_id() {

	$id = '0';
	if ( wppa( 'ajax' ) ) {
		if ( wppa_get( 'page_id' ) ) $id = wppa_get( 'page_id' );
		elseif ( wppa_get( 'p' ) ) $id = wppa_get( 'p' );
		elseif ( wppa_get( 'fromp' ) ) $id = wppa_get( 'fromp' );
	}
	if ( ! $id ) {
		$id = get_the_ID();
	}
	return $id;
}


function wppa_get_artmonkey_size_a( $photo ) {
global $wpdb;

	$data = wppa_cache_thumb( $photo );
	if ( $data ) {
		if ( wppa_switch( 'artmonkey_use_source' ) ) {
			if ( is_file( wppa_get_source_path( $photo ) ) ) {
				$source = wppa_get_source_path( $photo );
			}
			else {
				$source = wppa_get_photo_path( $photo );
			}
		}
		else {
			$source = wppa_get_photo_path( $photo );
		}
		$imgattr = @ getimagesize( $source );
		if ( is_array( $imgattr ) ) {
			$fs = wppa_get_filesize( $source );
			$result = array( 'x' => $imgattr['0'], 'y' => $imgattr['1'], 's' => $fs );
			return $result;
		}
	}
	return false;
}

function wppa_get_filesize( $file ) {

	if ( is_file( $file ) ) {
		$fs = wppa_filesize( $file );

		if ( $fs > 1024*1024 ) {
			$fs = sprintf('%4.2f Mb', $fs/(1024*1024));
		}
		else {
			$fs = sprintf('%4.2f Kb', $fs/1024);
		}
		return $fs;
	}

	return false;
}


function wppa_get_the_landing_page( $slug, $title ) {

	$page = wppa_opt( $slug );

	if ( $page != '-1' && ( ! $page || ! wppa_page_exists( $page ) ) ) {
		$page = wppa_create_page( $title );
		wppa_update_option( 'wppa_' . $slug, $page );
		wppa_opt( $slug, $page );
	}
	return $page;
}

function wppa_get_the_auto_page( $photo ) {
global $wpdb;

	if ( ! $photo ) return '0';					// No photo id, no page
	if ( ! wppa_is_int( $photo ) ) return '0';	// $photo not numeric

	$thumb = wppa_cache_thumb( $photo );		// Get photo info

	// Page exists ?
	if ( wppa_page_exists( $thumb['page_id'] ) ) {
		return $thumb['page_id'];
	}

	// Create new page
	$page = wppa_create_page( $thumb['name'], '[wppa type="autopage"]' );

	// Store with photo data
	$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->wppa_photos SET page_id = ".$page." WHERE id = %d", $photo ) );

	// Update cache
	$thumb['page_id'] = $page;

	return $page;
}

function wppa_remove_the_auto_page( $photo ) {

	if ( ! $photo ) return '0';					// No photo id, no page
	if ( ! wppa_is_int( $photo ) ) return '0';	// $photo not numeric

	$thumb = wppa_cache_thumb( $photo );		// Get photo info

	// Page exists ?
	if ( wppa_page_exists( $thumb['page_id'] ) ) {
		wp_delete_post( $thumb['page_id'], true );
		wppa_update_photo( array( 'id' => $photo, 'page_id' => '0' ) );
	}
}

function wppa_create_page( $title, $shortcode = '[wppa type="landing"]' ) {

	$my_page = array(
				'post_title'    => $title,
				'post_content'  => $shortcode,
				'post_status'   => 'publish',
				'post_type'	  	=> 'page'
			);

	$page = wp_insert_post( $my_page );
	return $page;
}

// Check if a published page exists
function wppa_page_exists( $id ) {
global $wpdb;
//static $pages_exist;

	// Check on valid input
	if ( ! $id ) return false;

	// Already found existing or non existing?
//	if ( isset( $pages_exist[$id] ) ) {
//		return $pages_exist[$id];
//	}

	// Do a query
	$iret = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM " .
											$wpdb->posts . " " .
											"WHERE post_type = 'page' " .
											"AND post_status = 'publish' " .
											"AND ID = %s", $id ) );

	// Save result
//	$pages_exist[$id] = ( $iret > 0 );

	return $iret; //$pages_exist[$id];
}

function wppa_get_photo_owner( $id ) {

	$thumb = wppa_cache_thumb( $id );
	return $thumb['owner'];
}

function wppa_cdn( $side ) {

	// What did we specify in the settings page?
	$cdn = wppa_opt( 'cdn_service' );

	// Check for fully configured and active
	switch ( $cdn ) {
		case 'local':
			break;

		case 'cloudinary':
		case 'cloudinarymaintenance':
			if ( wppa_opt( 'cdn_cloud_name' ) && wppa_opt( 'cdn_api_key' ) && wppa_opt( 'cdn_api_secret' ) ) {
				if ( $side == 'admin' ) {		// Admin: always return cloudinary
					$cdn = 'cloudinary';
				}
				elseif ( $side == 'front' ) {	// Front: NOT if in maintenance
					if ( $cdn == 'cloudinarymaintenance' ) {
						$cdn = false;
					}
				}
				else {
					wppa_dbg_msg( 'dbg', 'Wrong arg:'.$side.' in wppa_cdn()', 'red', 'force' );
					$cdn = false;
				}
			}
			else {
				wppa_dbg_msg( 'dbg', 'Incomplete configuration of Cloudinary', 'red', 'force' );
				$cdn = false;	// Incomplete configuration
			}
			break;

		default:
			$cdn = false;

	}

	return $cdn;
}

function wppa_get_source_path( $id ) {
global $blog_id;
global $wppa_supported_photo_extensions;

	// Source files can have uppercase extensions.
	$temp = array();
	foreach( $wppa_supported_photo_extensions as $ext ) {
		$temp[] = strtoupper( $ext );
	}
	$supext = array_merge( $wppa_supported_photo_extensions, $temp );

	$thumb = wppa_cache_thumb( $id );
	$album = $thumb['album'];

	// Trashed?
	if ( $album < '0' ) {
		$album = - ( $album + '9' );
	}

	$multi = is_multisite();
	if ( $multi && ! WPPA_MULTISITE_GLOBAL ) {
		$blog = '/blog-'.$blog_id;
	}
	else {
		$blog = '';
	}
	$source_path = wppa_opt( 'source_dir' ).$blog.'/album-'.$album.'/'.$thumb['filename'];
	if ( wppa_is_multi( $id ) ) {
		$path = wppa_strip_ext( $source_path );
		foreach ( $supext as $ext ) {
			$source = $path . '.' . $ext;
			if ( is_file( $source ) ) {
				return $source;
			}
		}
	}

	return $source_path;
}

// Get url of photo with highest available resolution.
// Not for display ( need not to download fast ) but for external services like Fotomoto
function wppa_get_hires_url( $id ) {

/*
	// video or audio? return the poster url
	if ( wppa_is_video( $id ) || wppa_has_audio( $id ) ) {
		$url = wppa_get_photo_url( $id );
		$temp = explode( '?', $url );
		$url = $temp['0'];
		return $url;
	}
*/
/*
	// Try CDN
	if ( wppa_cdn( 'front' ) && ! wppa_too_old_for_cloud( $id ) && ! wppa_is_panorama( $id ) && ! wppa_is_pdf( $id ) ) {
		switch ( wppa_cdn( 'front' ) ) {
			case 'cloudinary':
				$url = wppa_get_cloudinary_url( $id );
				break;
			default:
				$url = '';
		}
		if ( $url ) return $url;
	}
*/
	// Try the orientation corrected source url
	$source_path = wppa_get_o1_source_path( $id );
	if ( is_file( $source_path ) ) {

		// The source file is only http reacheable when it is down from wp-content
		if ( strpos( $source_path, WPPA_CONTENT_PATH ) !== false ) {
			return str_replace( WPPA_CONTENT_PATH, WPPA_CONTENT_URL, $source_path );
		}
	}

	// Try the source url
	$source_path = wppa_get_source_path( $id );
	if ( is_file( $source_path ) ) {

		// The source file is only http reacheable when it is down from ABSPATH
		if ( strpos( $source_path, WPPA_CONTENT_PATH ) !== false ) {
			return str_replace( WPPA_CONTENT_PATH, WPPA_CONTENT_URL, $source_path );
		}
	}

	// Try CDN
	if ( wppa_cdn( 'front' ) && ! wppa_too_old_for_cloud( $id ) && ! wppa_is_panorama( $id ) && ! wppa_is_pdf( $id ) ) {
		switch ( wppa_cdn( 'front' ) ) {
			case 'cloudinary':
				$url = wppa_get_cloudinary_url( $id );
				break;
			default:
				$url = '';
		}
		if ( $url ) return $url;
	}

	// The medium res url
	$hires_url = wppa_get_photo_url( $id );
	$temp = explode( '?', $hires_url );
	return $temp['0'];
}
function wppa_get_lores_url( $id ) {
	$lores_url = wppa_get_photo_url( $id );
	$temp = explode( '?', $lores_url );
	$lores_url = $temp['0'];
	return $lores_url;
}
function wppa_get_tnres_url( $id ) {
	$tnres_url = wppa_get_thumb_url( $id );
	$temp = explode( '?', $tnres_url );
	$tnres_url = $temp['0'];
	return $tnres_url;
}

// Get permalink to photo source file
function wppa_get_source_pl( $id ) {

	// Init
	$result = '';

	// If feature is enabled
	if ( wppa_opt( 'pl_dirname' ) ) {
		$source_path = wppa_fix_poster_ext( wppa_get_source_path( $id ), $id );
		if ( is_file( $source_path ) ) {
			$result = 	content_url() . '/' . 						// http://www.mysite.com/wp-content/
						wppa_opt( 'pl_dirname' ) . '/' .			// wppa-pl/
						wppa_get_album_name_for_pl( wppa_get_photo_item( $id, 'album' ) ) .
						'/' . basename( $source_path );					// My-Photo.jpg
		}
	}

	return $result;
}

function wppa_get_source_dir() {
global $blog_id;

	$multi = is_multisite();
//	$multi = true;	// debug
	if ( $multi && ! WPPA_MULTISITE_GLOBAL ) {
		$blog = '/blog-'.$blog_id;
	}
	else {
		$blog = '';
	}
	$source_dir = wppa_opt( 'source_dir' ).$blog;

	return $source_dir;
}

function wppa_get_source_album_dir( $alb ) {
global $blog_id;

	$multi = is_multisite();
//	$multi = true;	// debug
	if ( $multi && ! WPPA_MULTISITE_GLOBAL ) {
		$blog = '/blog-'.$blog_id;
	}
	else {
		$blog = '';
	}
	$source_album_dir = wppa_opt( 'source_dir' ).$blog.'/album-'.$alb;
	if ( ! wppa_is_dir( $source_album_dir ) ) {
		wppa_mkdir( $source_album_dir );
	}

	return $source_album_dir;
}


function wppa_set_default_name( $id, $filename_raw = '' ) {
global $wpdb;

	if ( ! $id || ! wppa_is_int( $id ) ) {
		wppa_log( 'Err', 'Missing id in wppa_set_default_name()', true );
		return;
	}

	wppa_cache_thumb( 'invalidate', $id );
	$thumb = wppa_cache_thumb( $id );

	$method 	= wppa_opt( 'newphoto_name_method' );
	$name 		= $thumb['filename']; 	// The default default
	$filename 	= $thumb['filename'];

	if ( ! $filename_raw ) {
		$filename_raw = wppa( 'unsanitized_filename' );
	}
	if ( ! $filename_raw ) {
		$filename_raw = $filename;
	}

	switch ( $method ) {
		case 'none':
			$name = '';
			break;
		case 'filename':
			if ( $filename_raw ) {
				$name = wppa_sanitize_photo_name( $filename_raw );
			}
			break;
		case 'noext':
			if ( $filename_raw ) {
				$name = wppa_sanitize_photo_name( $filename_raw );
			}
			$name = preg_replace('/\.[^.]*$/', '', $name);
			break;
		case 'noextspace':
			if ( $filename_raw ) {
				$name = wppa_sanitize_photo_name( $filename_raw );
			}
			$name = preg_replace('/\.[^.]*$/', '', $name);
			$name = str_replace( '-', ' ', $name );
			break;
		case '2#005':
			$tag = '2#005';
			$name = $wpdb->get_var( $wpdb->prepare( "SELECT description FROM $wpdb->wppa_iptc
													 WHERE photo = %d
													 AND tag = %s", $id, $tag ) );
			break;
		case '2#120':
			$tag = '2#120';
			$name = $wpdb->get_var( $wpdb->prepare( "SELECT description FROM $wpdb->wppa_iptc
													 WHERE photo = %d
													 AND tag = %s", $id, $tag ) );
			break;
		case 'Photo w#id':
			$name = __( 'Photo w#id', 'wp-photo-album-plus' );
			break;
		default:
			$name = '';
			break;
	}
	if ( ( $name ) || $method == 'none' ) {	// Update name
		wppa_update_photo( array( 'id' => $id, 'name' => $name ) );
//		$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->wppa_photos
//									   SET name = %s
//									   WHERE id = %d", $name, $id ) );

		wppa_cache_thumb( 'invalidate', $id );	// Invalidate cache
	}
	if ( ! wppa_switch( 'save_iptc') ) { 	// He doesn't want to keep the iptc data, so...
		$wpdb->query($wpdb->prepare( "DELETE FROM $wpdb->wppa_iptc
									  WHERE photo = %d", $id ) );
	}

	// In case owner must be set to name.
	wppa_set_owner_to_name( $id );
}

function wppa_set_default_tags( $id ) {
global $wpdb;

	$thumb 	= wppa_cache_thumb( $id );
	$album 	= wppa_cache_album( $thumb['album'] );
	$tags 	= wppa_sanitize_tags( str_replace( array( '\'', '"'), ',', wppa_filter_iptc( wppa_filter_exif( $album['default_tags'], $id ), $id ) ) );

	if ( wppa_switch( 'ipc025_to_tags' ) ) {
		$keywords = $wpdb->get_col( "SELECT description FROM $wpdb->wppa_iptc WHERE tag = '2#025' AND photo = $id" );
		if ( $keywords ) {
			foreach( $keywords as $word ) {
				$tags .= ',' . $word;
			}
			$tags = wppa_sanitize_tags( $tags );
		}
	}

	if ( $tags ) {
		wppa_update_photo( array( 'id' => $id, 'tags' => $tags ) );
		wppa_clear_taglist();
		wppa_cache_thumb( 'invalidate', $id );
	}
}

function wppa_set_default_custom( $id, $force = false ) {

	if ( ! wppa_switch( 'custom_fields' ) ) {
		return;
	}
	$custom = wppa_get_photo_item( $id, 'custom' );
	if ( $custom ) {
		$custom = wppa_unserialize( $custom );
	}
	else {
		$custom = array( '', '', '', '', '', '', '', '', '', '' );
	}
	$any = false;
	$i = 0;
	while ( $i < 10 ) {
		$data = $custom[$i];
		if ( ! $data || $force ) {
			$data = wppa_opt( 'custom_default_' . $i, '' );
			$new_data = wppa_filter_iptc( $data, $id );
			$new_data = wppa_filter_exif( $new_data, $id );
			$new_data = trim( $new_data, ', ' );
			if ( $new_data != $data ) {
				$custom[$i] = $new_data;
				$any = true;
			}
		}
		$i++;
	}
	if ( $any ) {
		wppa_update_photo( array( 'id' => $id, 'custom' => serialize( $custom ) ) );
	}
}

function wppa_test_for_medal( $id ) {
global $wpdb;

	$thumb = wppa_cache_thumb( $id );
	$status = $thumb['status'];

	if ( wppa_opt( 'medal_bronze_when' ) || wppa_opt( 'medal_silver_when' ) || wppa_opt( 'medal_gold_when' ) ) {
		$max_score = wppa_opt( 'rating_max' );

		$max_ratings = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->wppa_rating
														WHERE photo = %d AND value = %s
														AND status = %s", $id, $max_score, 'publish' ) );

		if ( $max_ratings >= wppa_opt( 'medal_gold_when' ) ) $status = 'gold';
		elseif ( $max_ratings >= wppa_opt( 'medal_silver_when' ) ) $status = 'silver';
		elseif ( $max_ratings >= wppa_opt( 'medal_bronze_when' ) ) $status = 'bronze';
	}

	if ( $status != $thumb['status'] ) {
		$thumb['status'] = $status;	// Update cache
		$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->wppa_photos SET status = %s WHERE id = %s", $status, $id ) );
	}
}

function wppa_get_the_bestof( $count, $period, $sortby, $what ) {
global $wpdb;

	// Phase 1, find the period we are talking about
	// find $start and $end
	switch ( $period ) {
		case 'lastweek':
			$start 	= wppa_get_timestamp( 'lastweekstart' );
			$end   	= wppa_get_timestamp( 'lastweekend' );
			break;
		case 'thisweek':
			$start 	= wppa_get_timestamp( 'thisweekstart' );
			$end   	= wppa_get_timestamp( 'thisweekend' );
			break;
		case 'lastmonth':
			$start 	= wppa_get_timestamp( 'lastmonthstart' );
			$end 	= wppa_get_timestamp( 'lastmonthend' );
			break;
		case 'thismonth':
			$start 	= wppa_get_timestamp( 'thismonthstart' );
			$end 	= wppa_get_timestamp( 'thismonthend' );
			break;
		case 'lastyear':
			$start 	= wppa_get_timestamp( 'lastyearstart' );
			$end 	= wppa_get_timestamp( 'lastyearend' );
			break;
		case 'thisyear':
			$start 	= wppa_get_timestamp( 'thisyearstart' );
			$end 	= wppa_get_timestamp( 'thisyearend' );
			break;
		default:
			return 'Unimplemented period: '.$period;
	}

	// Phase 2, get the ratings of the period
	// find $ratings, ordered by photo id
	$void = wppa_get_void_pids();
	if ( count ( $void ) ) {
		$void_clause = " AND photo NOT IN (" . implode( ',', $void ) . ") ";
	}
	else {
		$void_clause = "";
	}
	$ratings 	= $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->wppa_rating
													   WHERE timestamp >= %s
													   AND timestamp < %s" .
													   $void_clause .
													   "ORDER BY photo", $start, $end ), ARRAY_A );

	// Phase 3, set up an array with data we need
	// There are two methods: photo oriented and owner oriented, depending on

	// Each element reflects a photo ( key = photo id ) and is an array with items: maxratings, meanrating, ratings, totvalue.
	$ratmax	= wppa_opt( 'rating_max' );
	$data 	= array();
	foreach ( $ratings as $rating ) {
		$key = $rating['photo'];
		if ( ! isset( $data[$key] ) ) {
			$data[$key] = array();
			$data[$key]['ratingcount'] 		= '1';
			$data[$key]['maxratingcount'] 	= $rating['value'] == $ratmax ? '1' : '0';
			$data[$key]['totvalue'] 		= $rating['value'];
		}
		else {
			$data[$key]['ratingcount'] 		+= '1';
			$data[$key]['maxratingcount'] 	+= $rating['value'] == $ratmax ? '1' : '0';
			$data[$key]['totvalue'] 		+= $rating['value'];
		}
	}

	foreach ( array_keys( $data ) as $key ) {
		$thumb = wppa_cache_thumb( $key );
		$data[$key]['meanrating'] = $data[$key]['totvalue'] / $data[$key]['ratingcount'];
		$user = wppa_get_user_by( 'login', sanitize_user( $thumb['owner'] ) );
		if ( $user ) {
			$data[$key]['user'] = $user->display_name;
		}
		else { // user deleted
			$data[$key]['user'] = $thumb['owner'];
		}
		$data[$key]['owner'] = $thumb['owner'];
	}

	// Now we split into search for photos and search for owners

	if ( $what == 'photo' ) {

		// Pase 4, sort to the required sequence
		$data = wppa_array_sort( $data, $sortby, SORT_DESC );

	}
	else { 	// $what == 'owner'

		// Phase 4, combine all photos of the same owner
		wppa_array_sort( $data, 'user' );
		$temp = $data;
		$data = array();
		foreach ( array_keys( $temp ) as $key ) {
			if ( ! isset( $data[$temp[$key]['user']] ) ) {
				$data[$temp[$key]['user']]['photos'] 			= '1';
				$data[$temp[$key]['user']]['ratingcount'] 		= $temp[$key]['ratingcount'];
				$data[$temp[$key]['user']]['maxratingcount'] 	= $temp[$key]['maxratingcount'];
				$data[$temp[$key]['user']]['totvalue'] 			= $temp[$key]['totvalue'];
				$data[$temp[$key]['user']]['owner'] 			= $temp[$key]['owner'];
			}
			else {
				$data[$temp[$key]['user']]['photos'] 			+= '1';
				$data[$temp[$key]['user']]['ratingcount'] 		+= $temp[$key]['ratingcount'];
				$data[$temp[$key]['user']]['maxratingcount'] 	+= $temp[$key]['maxratingcount'];
				$data[$temp[$key]['user']]['totvalue'] 			+= $temp[$key]['totvalue'];
			}
		}
		foreach ( array_keys( $data ) as $key ) {
			$data[$key]['meanrating'] = $data[$key]['totvalue'] / $data[$key]['ratingcount'];
		}
		$data = wppa_array_sort( $data, $sortby, SORT_DESC );
	}

	// Phase 5, truncate to the desired length
	$c = '0';
	foreach ( array_keys( $data ) as $key ) {
		$c += '1';
		if ( $c > $count ) unset ( $data[$key] );
	}

	// Phase 6, return the result
	if ( count( $data ) ) {
		return $data;
	}
	else {
		return 	__('There are no ratings between', 'wp-photo-album-plus') .
				'<br />' .
				wppa_local_date( 'F j, Y, H:i s', $start ) .
				' ' . __('and', 'wp-photo-album-plus') .
				'<br />' .
				wppa_local_date( 'F j, Y, H:i s', $end ) .
				'.';
	}
}

// Retrieve the number of child albums ( if any )
function wppa_has_children( $alb ) {
global $wpdb;
static $childcounts;

	// See if done this alb earlier
	if ( isset( $childcounts[$alb] ) ) {
		$result = $childcounts[$alb];
	}
	else {
		$result = $wpdb->get_var( $wpdb->prepare( 	"SELECT COUNT(*) " .
													"FROM $wpdb->wppa_albums " .
													"WHERE a_parent = %s", $alb) );

		// Save result
		$childcounts[$alb] = $result;
	}

	return $result;
}

// Get an enumeration of all the (grand)children of some album spec.
// Album spec may be a number or an enumeration
function wppa_alb_to_enum_children( $xparents ) {

	$parents = wppa_expand_enum( $xparents );

	if ( strpos( $parents, '.' ) !== false ) {
		$albums = explode( '.', $parents );
	}
	else {
		$albums = array( $parents );
	}
	$result = '';
	foreach( $albums as $alb ) {
		if ( ! $alb ) $alb = '0';
		$result .= _wppa_alb_to_enum_children( $alb );
		$result = trim( $result, '.' ).'.';
	}
	$result = str_replace( '..', '.', $result );
	$result = trim( $result, '.' );

	return $result;
}

function _wppa_alb_to_enum_children( $alb ) {
global $wpdb;
static $child_list;

	// Init child list
	if ( ! $child_list ) {
		$child_list = get_option( 'wppa_child_list', array() );
	}

	// Done this one before?
	if ( isset( $child_list[$alb] ) ) {
		return wppa_expand_enum( $child_list[$alb] );
	}

	// Get the data
	$result = $alb;
	$children = $wpdb->get_results( $wpdb->prepare( "SELECT id FROM $wpdb->wppa_albums
													 WHERE a_parent = %s", $alb ), ARRAY_A );
	if ( $children ) foreach ( $children as $child ) {
		$result .= '.' . _wppa_alb_to_enum_children( $child['id'] );
		$result = trim( $result, '.' );
	}

	// Sequentialize
	if ( strpos( $result, '.' ) !== false ) {
		$r = explode( '.', $result );
		sort( $r );
		$result = implode( '.', $r );
	}

	// Store in cache
	$child_list[$alb] = wppa_compress_enum( $result );
	ksort( $child_list );
	update_option( 'wppa_child_list', $child_list );

	// Return requested data
	return $result;
}

// Remove from childlist
function wppa_childlist_remove( $alb ) {

	$any = false;

	$child_list = get_option( 'wppa_child_list', array() );
	foreach( array_keys( $child_list ) as $key ) {
		$line = '.' . wppa_expand_enum( $child_list[$key] ) . '.';
		if ( $key == $alb || strpos( $line, '.'.$alb.'.' ) !== false  ) {

			unset( $child_list[$key] );
			$any = true;
		}
	}
	if ( $any ) {
		update_option( 'wppa_child_list', $child_list );
	}
}

function wppa_compress_enum( $enum ) {
	$result = $enum;
	if ( strpos( $enum, '.' ) !== false ) {
		$result = explode( '.', $enum );
		sort( $result, SORT_NUMERIC );
		$old = '-99';
		foreach ( array_keys( $result ) as $key ) { 	// Remove dups
			if ( $result[$key] == $old ) unset ( $result[$key] );
			else $old = $result[$key];
		}
		$result = wppa_index_array_to_string( $result );
		$result = str_replace( ',', '.', $result );
	}
	$result = trim( $result, '.' );
	return $result;
}

function wppa_expand_enum( $enum ) {
	$result = $enum;
	$result = str_replace( '.', ',', $result );
	$result = str_replace( ',,', '..', $result );
	$result = wppa_index_string_to_array( $result );
	$result = implode( '.', $result );
	return $result;
}

// Compute avg rating and count and put it in photo data
function wppa_rate_photo( $id ) {
global $wpdb;

	// Likes only?
	if ( wppa_opt( 'rating_display_type' ) == 'likes' ) {

		// Get rating(like)count
		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*)
												  FROM $wpdb->wppa_rating
												  WHERE photo = %d
												  AND status = 'publish'", $id ) );

		// Update photo
		$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->wppa_photos
									   SET rating_count = %d, mean_rating = 0
									   WHERE id = %d". $count, $id ) );

		// Invalidate cache
		wppa_cache_photo( 'invalidate', $id );
	}
	else {

		// Get all ratings for this photo
		$ratings = $wpdb->get_results( $wpdb->prepare( "SELECT value
														FROM $wpdb->wppa_rating
														WHERE photo = %d
														AND status = 'publish'", $id ), ARRAY_A );

		// Init
		$the_value = '0';
		$the_count = '0';

		// Compute mean value and count
		if ( $ratings ) {

			foreach ( $ratings as $rating ) {

				if ( $rating['value'] == '-1' ) {
					$the_value += wppa_opt( 'dislike_value' );
				}
				else {
					$the_value += $rating['value'];
				}

				$the_count++;
			}
		}
		if ( $the_count ) $the_value /= $the_count;
		if ( wppa_opt( 'rating_max' ) == '1' ) $the_value = '0';
		if ( $the_value == '10' ) $the_value = '9.9999999';	// mean_rating is a text field. for sort order reasons we make 10 into 9.99999

		// Update photo
		$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->wppa_photos
									   SET mean_rating = %s, rating_count = %d
									   WHERE id = %d", $the_value, $the_count, $id ) );

		// Invalidate cache
		wppa_cache_photo( 'invalidate', $id );

		// Set status to a medaltype if appiliccable
		wppa_test_for_medal( $id );
	}
}

function wppa_strip_ext( $file ) {

	$strlen = strlen( $file );
	$dotpos = strrpos( $file, '.' );
	if ( $dotpos > ( $strlen - 6 ) ) {
		$result = substr( $file, 0, $dotpos );
	}
	else {
		$result = $file;
	}

	return $result; // preg_replace('/\.[^.]*$/', '', $file);
}

function wppa_get_ext( $file ) {

	$strlen = strlen( $file );
	$dotpos = strrpos( $file, '.' );
	if ( $dotpos > ( $strlen - 6 ) ) {
		$result = substr( $file, $dotpos + 1 );
	}
	else {
		$result = '';
	}

	return $result; // str_replace( wppa_strip_ext( $file ).'.', '', $file );
}

function wppa_encode_uri_component( $xstr ) {
	$str = $xstr;
	$illegal = array( '?', '&', '#', '/', '"', "'", ' ' );
	foreach ( $illegal as $char ) {
		$str = str_replace( $char, sprintf( '%%%X', ord($char) ), $str );
	}
	return $str;
}

function wppa_decode_uri_component( $xstr ) {
	$str = $xstr;
	$illegal = array( '?', '&', '#', '/', '"', "'", ' ' );
	foreach ( $illegal as $char ) {
		$str = str_replace( sprintf( '%%%X', ord($char) ), $char, $str );
		$str = str_replace( sprintf( '%%%x', ord($char) ), $char, $str );
	}
	return $str;
}

function wppa_force_numeric_else( $value, $default ) {
	if ( ! $value ) return $value;
	if ( ! wppa_is_int( $value ) ) return $default;
	return $value;
}

// Same as wp sanitize_file_name, except that it can be used for a pathname also.
// If a pathname: only the basename of the path is sanitized.
function wppa_sanitize_file_name( $file, $check_length = true ) {

	// Any sanitize required?
	if ( ! ( wppa_switch( 'remove_accents' ) || wppa_switch( 'sanitize_import' ) ) ) {
		return $file;
	}

	// Make sure its utf8
	if ( ! seems_utf8( $file ) ) {
		$file = utf8_encode( $file );
	}

	// Only accemts?
	if ( wppa_switch( 'remove_accents' ) ) {
		$file = remove_accents( $file );
	}

	// No furher sanitize?
	if ( ! wppa_switch( 'sanitize_import' ) ) {
		return $file;
	}

	$temp 	= explode( '/', $file );
	$cnt 	= count( $temp );
	$temp[$cnt - 1] = sanitize_file_name( $temp[$cnt - 1] );
	$maxlen = wppa_opt( 'max_filename_length' );
	if ( $maxlen && $check_length ) {
		if ( strpos( $temp[$cnt - 1], '.' ) !== false ) {
			$name = wppa_strip_ext( $temp[$cnt - 1] );
			$ext = str_replace( $name.'.', '', $temp[$cnt - 1] );
			if ( strlen( $name ) > $maxlen ) {
				$name = substr( $name, 0, $maxlen );
				$temp[$cnt - 1] = $name.'.'.$ext;
			}
		}
		else {
			if ( strlen( $temp[$cnt - 1] ) > $maxlen ) {
				$temp[$cnt - 1] = substr( $temp[$cnt - 1], 0, $maxlen );
			}
		}
	}
	$file 	= implode( '/', $temp );
	$file 	= trim ( $file );
	return $file;
}

// Create a html safe photo name from a filename. May be a pathname
function wppa_sanitize_photo_name( $file ) {
	$result = htmlspecialchars( strip_tags( stripslashes( basename( $file ) ) ) );
	$maxlen = wppa_opt( 'max_photoname_length' );
	if ( $maxlen && strlen( $result ) > $maxlen ) {
		$result = wppa_strip_ext( $result ); // First remove any possible file-extension
		if ( strlen( $result ) > $maxlen ) {
			$result = substr( $result, 0, $maxlen );	// Truncate
		}
	}
	return $result;
}

// Get meta keywords of a photo
function wppa_get_keywords( $id ) {
static $wppa_void_keywords;

	if ( ! $id ) return '';

	if ( empty ( $wppa_void_keywords ) ) {
		$wppa_void_keywords	= array( 	__('Not Defined', 'wp-photo-album-plus'),
										__('Manual', 'wp-photo-album-plus'),
										__('Program AE', 'wp-photo-album-plus'),
										__('Aperture-priority AE', 'wp-photo-album-plus'),
										__('Shutter speed priority AE', 'wp-photo-album-plus'),
										__('Creative (Slow speed)', 'wp-photo-album-plus'),
										__('Action (High speed)', 'wp-photo-album-plus'),
										__('Portrait', 'wp-photo-album-plus'),
										__('Landscape', 'wp-photo-album-plus'),
										__('Bulb', 'wp-photo-album-plus'),
										__('Average', 'wp-photo-album-plus'),
										__('Center-weighted average', 'wp-photo-album-plus'),
										__('Spot', 'wp-photo-album-plus'),
										__('Multi-spot', 'wp-photo-album-plus'),
										__('Multi-segment', 'wp-photo-album-plus'),
										__('Partial', 'wp-photo-album-plus'),
										__('Other', 'wp-photo-album-plus'),
										__('No Flash', 'wp-photo-album-plus'),
										__('Fired', 'wp-photo-album-plus'),
										__('Fired, Return not detected', 'wp-photo-album-plus'),
										__('Fired, Return detected', 'wp-photo-album-plus'),
										__('On, Did not fire', 'wp-photo-album-plus'),
										__('On, Fired', 'wp-photo-album-plus'),
										__('On, Return not detected', 'wp-photo-album-plus'),
										__('On, Return detected', 'wp-photo-album-plus'),
										__('Off, Did not fire', 'wp-photo-album-plus'),
										__('Off, Did not fire, Return not detected', 'wp-photo-album-plus'),
										__('Auto, Did not fire', 'wp-photo-album-plus'),
										__('Auto, Fired', 'wp-photo-album-plus'),
										__('Auto, Fired, Return not detected', 'wp-photo-album-plus'),
										__('Auto, Fired, Return detected', 'wp-photo-album-plus'),
										__('No flash function', 'wp-photo-album-plus'),
										__('Off, No flash function', 'wp-photo-album-plus'),
										__('Fired, Red-eye reduction', 'wp-photo-album-plus'),
										__('Fired, Red-eye reduction, Return not detected', 'wp-photo-album-plus'),
										__('Fired, Red-eye reduction, Return detected', 'wp-photo-album-plus'),
										__('On, Red-eye reduction', 'wp-photo-album-plus'),
										__('Red-eye reduction, Return not detected', 'wp-photo-album-plus'),
										__('On, Red-eye reduction, Return detected', 'wp-photo-album-plus'),
										__('Off, Red-eye reduction', 'wp-photo-album-plus'),
										__('Auto, Did not fire, Red-eye reduction', 'wp-photo-album-plus'),
										__('Auto, Fired, Red-eye reduction', 'wp-photo-album-plus'),
										__('Auto, Fired, Red-eye reduction, Return not detected', 'wp-photo-album-plus'),
										__('Auto, Fired, Red-eye reduction, Return detected', 'wp-photo-album-plus'),
										'album', 'albums', 'content', 'http',
										'source', 'wp', 'uploads', 'thumbs',
										'wp-content', 'wppa-source',
										'border', 'important', 'label', 'padding',
										'segment', 'shutter', 'style', 'table',
										'times', 'value', 'views', 'wppa-label',
										'wppa-value', 'weighted', 'wppa-pl',
										'datetime', 'exposureprogram', 'focallength', 'isospeedratings', 'meteringmode', 'model', 'photographer',
										str_replace( '/', '', site_url() )
									);

		// make a string
		$temp = implode( ',', $wppa_void_keywords );

		// Downcase
		$temp = strtolower( $temp );

		// Remove spaces and funny chars
		$temp = str_replace( array( ' ', '-', '"', "'", '\\', '>', '<', ',', ':', ';', '!', '?', '=', '_', '[', ']', '(', ')', '{', '}' ), ',', $temp );
		$temp = str_replace( ',,', ',', $temp );

		// Make array
		$wppa_void_keywords = explode( ',', $temp );

		// Sort array
		sort( $wppa_void_keywords );

		// Remove dups
		$start = 0;
		foreach ( array_keys( $wppa_void_keywords ) as $key ) {
			if ( $key > 0 ) {
				if ( $wppa_void_keywords[$key] == $wppa_void_keywords[$start] ) {
					unset ( $wppa_void_keywords[$key] );
				}
				else {
					$start = $key;
				}
			}
		}
	}

	$text 	= wppa_get_photo_name( $id )  .' ' . wppa_get_photo_desc( $id );
	$text 	= str_replace( array( '/', '-' ), ' ', $text );
	$words 	= wppa_index_raw_to_words( $text );
	foreach ( array_keys( $words ) as $key ) {
		if ( 	wppa_is_int( $words[$key] ) ||
				in_array( $words[$key], $wppa_void_keywords ) ||
				strlen( $words[$key] ) < 5 ) {
			unset ( $words[$key] );
		}
	}
	$result = implode( ', ', $words );
	return $result;
}

function wppa_is_orig ( $path ) {
	$file = basename( $path );
	$file = wppa_strip_ext( $file );
	$temp = explode( '-', $file );
	if ( ! is_array( $temp ) ) return true;
	$temp = $temp[ count( $temp ) -1 ];
	$temp = explode( 'x', $temp );
	if ( ! is_array( $temp ) ) return true;
	if ( count( $temp ) != 2 ) return true;
	if ( ! wppa_is_int( $temp[0] ) ) return true;
	if ( ! wppa_is_int( $temp[1] ) ) return true;
	return false;
}

function wppa_browser_can_html5() {

	if ( ! isset( $_SERVER["HTTP_USER_AGENT"] ) ) return false;

	$is_opera 	= strpos( $_SERVER["HTTP_USER_AGENT"], 'OPR' );
	$is_ie 		= strpos( $_SERVER["HTTP_USER_AGENT"], 'Trident' );
	$is_safari 	= strpos( $_SERVER["HTTP_USER_AGENT"], 'Safari' );
	$is_firefox = strpos( $_SERVER["HTTP_USER_AGENT"], 'Firefox' );

	if ( $is_opera ) 	return true;
	if ( $is_safari ) 	return true;
	if ( $is_firefox ) 	return true;

	if ( $is_ie ) {
		$tri_pos = strpos( $_SERVER["HTTP_USER_AGENT"], 'Trident/' );
		$tri_ver = substr( $_SERVER["HTTP_USER_AGENT"], $tri_pos+8, 3 );
		if ( $tri_ver >= 6.0 ) return true; // IE 10 or later
	}

	return false;
}

function wppa_get_comten_ids( $max_count = 0, $albums = array() ) {
global $wpdb;

	// Find real maxcount
	if ( ! $max_count ) {
		$max_count = wppa_opt( 'comten_count' );
	}

	// Find raw commented photo ids
	$photo_ids = $wpdb->get_col( $wpdb->prepare( "SELECT photo FROM $wpdb->wppa_comments
												  WHERE status = 'approved'
												  ORDER BY timestamp DESC LIMIT %d", 100 * $max_count ) );

	$result = array();

	// Get unique photo ids in possibly supplied albums
	if ( is_array( $photo_ids ) ) {
		foreach( $photo_ids as $ph ) {
			if ( empty( $albums ) || in_array( wppa_get_photo_item( $ph, 'album' ), $albums ) || ( count( $albums ) == 1 && $albums[0] == '0' ) ) {
				if ( ! in_array( $ph, $result ) ) {
					$result[] = $ph;
				}
			}
		}
	}

	// Remove void album photo items
	$result = array_diff( $result, wppa_get_void_pids() );

	// Clip to max count
	if ( count( $result ) > $max_count ) {
		$result = array_slice( $result, 0, $max_count );
	}

	return $result;
}

// Get array of void album ids
function wppa_get_void_aids() {
global $wpdb;

	// Find voided albums
	$void_albs = array();
	if ( ! wppa_user_is( 'administrator' ) ) {
		$void_albs = array_merge( $void_albs, $wpdb->get_col( "SELECT id FROM $wpdb->wppa_albums WHERE status = 'hidden'" ) );
	}
	if ( ! is_user_logged_in() ) {
		$void_albs = array_merge( $void_albs, $wpdb->get_col( "SELECT id FROM $wpdb->wppa_albums WHERE status = 'private'" ) );
	}

	return $void_albs;
}

// Get array of photo ids that reside in void albums
function wppa_get_void_pids() {
global $wpdb;

	$void_albs = wppa_get_void_aids();
	if ( ! count( $void_albs ) ) {
		return array();
	}

	$void_photos = $wpdb->get_col( "SELECT id FROM $wpdb->wppa_photos WHERE album IN (" . implode( ',', $void_albs ) . ")" );
	return $void_photos;
}

// Filter for Plugin CM Tooltip Glossary
function wppa_filter_glossary( $desc ) {
static $wppa_cmt;

	// Do we need this?
	if ( wppa_switch( 'use_CMTooltipGlossary' ) && class_exists( 'CMTooltipGlossaryFrontend' ) ) {

		// Class initialized?
		if ( empty( $wppa_cmt ) ) {
			$wppa_cmt = new CMTooltipGlossaryFrontend;
		}

		// Do we already start with a <p> ?
		$start_p = ( strpos( $desc, '<p' ) === 0 );

		// remove newlines, glossary converts them to <br />
		$desc = str_replace( array( "\n", "\r", "\t" ), '', $desc );
		$desc = $wppa_cmt->cmtt_glossary_parse( $desc, true );

		// Remove <p> and </p> that CMTG added around
		if ( ! $start_p ) {
			if ( substr( $desc, 0, 3 ) == '<p>' ) {
				$desc = substr( $desc, 3 );
			}
			if ( substr( $desc, -4 ) == '</p>' ) {
				$desc = substr( $desc, 0, strlen( $desc ) - 4 );
			}
		}
	}

	return $desc;
}

// Convert file extension to lowercase
function wppa_down_ext( $file ) {
	if ( strpos( $file, '.' ) === false ) return $file;	// no . found
	$dotpos = strrpos( $file, '.' );
	$file = substr( $file, 0, $dotpos ) . strtolower( substr( $file, $dotpos ) );
	return $file;
}

// See of a photo db entry is a multimedia entry
function wppa_is_multi( $id ) {

	if ( ! $id ) return false;			// No id

	$ext = wppa_get_photo_item( $id, 'ext' );
	return ( $ext == 'xxx' );
}

// If it just a photo?
function wppa_is_photo( $id ) {

	if ( ! $id ) return false;

	if ( wppa_is_multi( $id ) ) return false;
	if ( wppa_is_pdf( $id ) ) return false;

	return true;
}

// Is it a zoomable photo?
function wppa_is_zoomable( $id ) {

	if ( ! $id ) return false;

	if ( ! wppa_is_photo( $id ) ) return false;
	if ( wppa_is_panorama( $id ) ) return false;

	$album_zoom = wppa_get_album_item( wppa_get_photo_item( $id, 'album' ), 'zoomable' );

	if ( $album_zoom ) { // can be 'on', 'off', '' (=default: see global setting 'zoom_on' )
		return $album_zoom == 'on';
	}
	return ( wppa_switch( 'zoom_on' ) );
}

function wppa_fix_poster_ext( $fileorurl, $id ) {

	// Has it extension .xxx ?
	if ( substr( $fileorurl, -4 ) != '.xxx' &&
		 strpos( $fileorurl, '.xxx?ver' ) === false &&
		 substr( $fileorurl, -4 ) != '.pdf' &&
		 strpos( $fileorurl, '.pdf?ver' ) === false ) {
		return $fileorurl;
	}

	// Is it a pdf?
	if ( wppa_is_pdf( $id ) ) {

		// Url ?
		if ( strpos( $fileorurl, 'http://' ) !== false || strpos( $fileorurl, 'https://' ) !== false ) {
			return WPPA_UPLOAD_URL . '/'. 'documentstub.png';
		}

		// File
		else {
			$source_poster = str_replace( '.pdf', '.jpg', $fileorurl );
			if ( wppa_is_file( $source_poster ) ) {
				return $source_poster;
			}
			$source_poster = str_replace( '.pdf', '.png', $fileorurl );
			if ( wppa_is_file( $source_poster ) ) {
				return $source_poster;
			}
			else {
				return WPPA_UPLOAD_PATH . '/' . 'documentstub.png';
			}
		}
	}

	else {

		// Get available ext
		$poster_ext = wppa_geter_ext( $id );

		// If found, replace extension to ext of existing file
		if ( $poster_ext ) {
			return str_replace( '.xxx', '.'.$poster_ext, $fileorurl );
		}

		// Not found. If audio, return audiostub file or url
		if ( wppa_has_audio( $id ) ) {

			// Url ?
			if ( strpos( $fileorurl, 'http://' ) !== false || strpos( $fileorurl, 'https://' ) !== false ) {
				if ( wppa_switch( 'use_audiostub' ) ) {
					return WPPA_UPLOAD_URL . '/'. 'audiostub.jpg';
				}
				else {
					return WPPA_UPLOAD_URL . '/'. 'transparent.png';
				}
			}

			// File
			else {
				if ( wppa_switch( 'use_audiostub' ) ) {
					return WPPA_UPLOAD_PATH . '/' . 'audiostub.jpg';
				}
				else {
					return WPPA_UPLOAD_PATH . '/' . 'transparent.png';
				}
			}
		}

		// Not found. Is Video, return as jpg
		return str_replace( '.xxx', '.jpg', $fileorurl );
	}
}

function wppa_geter_ext( $id ) {
global $wppa_supported_photo_extensions;

	// Init
	$path 		= wppa_get_photo_path( $id, false );
	$raw_path 	= wppa_strip_ext( $path );

	// Find existing photofiles
	foreach ( $wppa_supported_photo_extensions as $ext ) {
		if ( is_file( $raw_path.'.'.$ext ) ) {
			return $ext;	// Found !
		}
	}

	// Not found.
	return false;
}

// Like wp sanitize_text_field, but also removes chars 0x00..0x07
function wppa_sanitize_text( $txt ) {
	$result = sanitize_text_field( $txt );
	$result = str_replace( array(chr(0), chr(1), chr(2), chr(3),chr(4), chr(5), chr(6), chr(7) ), '', $result );
	$result = trim( $result );
	return $result;
}

function wppa_is_mobile() {
// return true; // debug
	$result = false;
	$detect = new wppa_mobile_detect();
	if ( $detect->isMobile() ) {
		$result = true;
	}
	return $result;
}

function wppa_is_ipad() {
// return true; // debug
	$result = false;
	$detect = new wppa_mobile_detect();
	if ( $detect->isDevice( 'ipad' ) ) {
		$result = true;
	}
	return $result;
}

function wppa_is_iphoneoripad() {
// return true; // debug
	$result = false;
	$detect = new wppa_mobile_detect();
	if ( $detect->isDevice( 'iphone' ) || $detect->isDevice( 'ipad' ) ) {
		$result = true;
	}
	return $result;
}

function wppa_is_chrome() {
	return ( get_browser_name() == 'Chrome' );
}

function wppa_is_firefox() {
	return ( get_browser_name() == 'Firefox' );
}

function wppa_is_edge() {
	return ( get_browser_name() == 'Edge' );
}

function wppa_is_ie() {
	return ( get_browser_name() == 'Internet Explorer' );
}

function wppa_is_safari() {
	return ( get_browser_name() == 'Safari' );
}

function wppa_is_opera() {
	return ( get_browser_name() == 'Opera' );
}

function get_browser_name() {
	$user_agent = isset ( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : '';
	if ( $user_agent ) {
		if (strpos($user_agent, 'Opera') || strpos($user_agent, 'OPR/')) return 'Opera';
		elseif (strpos($user_agent, 'Edge')) return 'Edge';
		elseif (strpos($user_agent, 'Chrome')) return 'Chrome';
		elseif (strpos($user_agent, 'Safari')) return 'Safari';
		elseif (strpos($user_agent, 'Firefox')) return 'Firefox';
		elseif (strpos($user_agent, 'MSIE') || strpos($user_agent, 'Trident/7')) return 'Internet Explorer';
    }
    return 'Other';
}

// Like wp_nonce_field
// To prevent duplicate id's, we externally add an id number ( e.g. album ) and internally the mocc number.
function wppa_nonce_field( $action = -1, $name = "_wpnonce", $referer = true , $echo = true, $wppa_id = '0' ) {

	$name = esc_attr( $name );
	$nonce_field = 	'<input' .
						' type="hidden"' .
						' id="' . $name . '-' . $wppa_id . '-' . wppa( 'mocc' ) . '"' .
						' name="' . $name . '"' .
						' value="' . wp_create_nonce( $action ) . '"' .
						' />';

	if ( $referer ) {
		$nonce_field .= wp_referer_field( false );
	}

	if ( $echo ) {
		echo $nonce_field;
	}

	return $nonce_field;
}

// Like convert_smilies, but directe rendered to <img> tag to avoid performance bottleneck for emoji's when ajax on firefox
function wppa_convert_smilies( $text ) {
static $smilies;

	// Initialize
	if ( ! is_array( $smilies ) ) {
		$smilies = array(	";-)" 		=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f609.png" />',
							":|" 		=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f610.png" />',
							":x" 		=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f621.png" />',
							":twisted:" => '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f608.png" />',
							":shock:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f62f.png" />',
							":razz:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f61b.png" />',
							":oops:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f633.png" />',
							":o" 		=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f62e.png" />',
							":lol:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f606.png" />',
							":idea:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f4a1.png" />',
							":grin:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f600.png" />',
							":evil:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f47f.png" />',
							":cry:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f625.png" />',
							":cool:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f60e.png" />',
							":arrow:" 	=> '<img class="emoji" draggable="false" alt="?" src="http://s.w.org/images/core/emoji/72x72/27a1.png" />',
							":???:" 	=> '<img class="emoji" draggable="false" alt="??" src="http://s.w.org/images/core/emoji/72x72/1f615.png" />',
							":?:" 		=> '<img class="emoji" draggable="false" alt="?" src="http://s.w.org/images/core/emoji/72x72/2753.png" />',
							":!:" 		=> '<img class="emoji" draggable="false" alt="?" src="http://s.w.org/images/core/emoji/72x72/2757.png" />'
		);
	}

	// Perform
	$result = $text;
	foreach ( array_keys( $smilies ) as $key ) {
		$result = str_replace( $key, $smilies[$key], $result );
	}

	// Convert non-emoji's
	$result = convert_smilies( $result );

	// SSL?
	if ( is_ssl() ) {
		$result = str_replace( 'http://', 'https://', $result );
	}

	// Done
	return $result;
}

function wppa_is_virtual() {

	if ( wppa( 'is_topten' ) ) return true;
	if ( wppa( 'is_lasten' ) ) return true;
	if ( wppa( 'is_featen' ) ) return true;
	if ( wppa( 'is_comten' ) ) return true;
	if ( wppa( 'is_tag' ) ) return true;
	if ( wppa( 'is_related' ) ) return true;
	if ( wppa( 'is_upldr' ) ) return true;
	if ( wppa( 'is_cat' ) ) return true;
	if ( wppa( 'is_supersearch' ) ) return true;
	if ( wppa( 'src' ) ) return true;
	if ( wppa( 'supersearch' ) ) return true;
	if ( wppa( 'searchstring' ) ) return true;
	if ( wppa( 'calendar' ) ) return true;
	if ( wppa_get( 'vt' ) ) return true;
	if ( wppa( 'is_potdhis' ) ) return true;

	return false;
}

function wppa_too_old_for_cloud( $id ) {

	$thumb = wppa_cache_thumb( $id );

	$is_old = wppa_cdn( 'admin' ) && wppa_opt( 'max_cloud_life' ) && ( time() > ( $thumb['timestamp'] + wppa_opt( 'max_cloud_life' ) ) );

	return $is_old;
}

// Test if we are in a widget
// Returns wppa widget type if in a wppa widget
// Else: return true if in a widget, false if not in a widget
function wppa_in_widget() {

	if ( wppa( 'in_widget' ) ) {
		return wppa( 'in_widget' );
	}
	$stack = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS );
	if ( is_array( $stack ) ) foreach( $stack as $item ) {
		if ( isset( $item['class'] ) && $item['class'] == 'WP_Widget' ) {
			return true;
		}
	}
	return false;
}

function wppa_bump_mocc() {
global $wppa;

	$wppa['mocc'] += 1;
}

// This is a nice simple function
function wppa_out( $txt ) {
global $wppa;

	$wppa['out'] .= $txt;
}

function wppa_exit() {
	wppa_session_end();
	exit;
}

function wppa_sanitize_custom_field( $txt ) {

	if ( ! current_user_can( 'unfiltered_html' ) ) {
		$result = strip_tags( $txt );
	}
	else {
		$result = balanceTags( $txt );
	}
	return $result;
}

// See if a photo is in our admins choice zip
function  wppa_is_photo_in_zip( $id ) {
global $wpdb;

	// We do not zip pdfs
	if ( wppa_is_pdf( $id ) ) return false;

	// Verify existance of zips dir
	$zipsdir = WPPA_UPLOAD_PATH.'/zips/';
	if ( ! wppa_is_dir( $zipsdir ) ) return false;

	// Compose the users zip filename
	$zipfile = $zipsdir.wppa_get_user().'.zip';

	// Check file existance
	if ( ! is_file( $zipfile ) ) {
		return false;
	}

	// Find the photo data
	$data = wppa_cache_thumb( $id );
	$photo_file = wppa_fix_poster_ext( $data['filename'], $id );

	// Open zip
	$wppa_zip = new ZipArchive;
	$wppa_zip->open( $zipfile );
	if ( ! $wppa_zip ) {

		// Failed to open zip
		return false;
	}

	// Look photo up in zip
	for( $i = 0; $i < $wppa_zip->numFiles; $i++ ) {
		$stat = $wppa_zip->statIndex( $i );
		$file_name = $stat['name'];
		if ( $file_name == $photo_file ) {

			// Found
			$wppa_zip->close();
			return true;
		}
	}

	// Not found
	$wppa_zip->close();
	return false;
}

// Convert querystring to get and request vars
function wppa_convert_uri_to_get( $uri ) {

	// Make local copy of argument
	$temp = $uri;

	// See if a ? is in the string
	if ( strpos( $uri, '?' ) !== false ) {

		// Trim up to and including ?
		$temp = substr( $uri, strpos( $uri, '?' ) + 1 );
	}

	// explode uri
	$arr = explode( '&', $temp );

	// If args exist, process them
	if ( !empty( $arr ) ) {
		foreach( $arr as $item ) {
			$arg = explode( '=', $item );
			if ( ! isset( $arg[1] ) ) {
				$arg[1] = null;
			}
			else {
				$arg[1] = urldecode( $arg[1] );
			}
			$_GET[$arg[0]] = $arg[1];
			$_REQUEST[$arg[0]] = $arg[1];
		}
	}
}

// Set owner to login name if photo name is user display_name
// Return true if owner changed, return 0 if already set, return false if not a username
function wppa_set_owner_to_name( $id ) {
global $wpdb;
static $usercache;

	// Feature enabled?
	if ( wppa_switch( 'owner_to_name' ) ) {

		// Get photo data.
		$p = wppa_cache_thumb( $id );

		// Find user of whose display name equals photoname
		if ( isset( $usercache[$p['name']] ) ) {
			$user = $usercache[$p['name']];
		}
		else {
			$user = $wpdb->get_var( $wpdb->prepare( "SELECT user_login FROM ".$wpdb->users." WHERE display_name = %s", $p['name'] ) );
			if ( $user ) {
				$usercache[$p['name']] = $user;
			}
			else {
				$usercache[$p['name']] = false;	// NULL is equal to ! isset() !!!
			}
		}
		if ( $user ) {

			if ( $p['owner'] != $user ) {
				wppa_update_photo( array( 'id' => $id, 'owner' => $user ) );
				wppa_cache_thumb( 'invalidate', $id );
				return true;
			}
			else {
				return '0';
			}
		}
	}

	return false;
}

// Get my last vote for a certain photo
function wppa_get_my_last_vote( $id ) {
global $wpdb;

	if ( is_user_logged_in() ) {
		$result = $wpdb->get_var( $wpdb->prepare(  "SELECT value FROM $wpdb->wppa_rating
													WHERE photo = %d
													AND userid = %d
													ORDER BY id DESC
													LIMIT 1",
													$id,
													wppa_get_user_id()
												) );
	}
	else {
		$result = $wpdb->get_var( $wpdb->prepare(  "SELECT value FROM $wpdb->wppa_rating
													WHERE photo = %d
													AND ip = %s
													ORDER BY id DESC
													LIMIT 1",
													$id,
													$_SERVER['REMOTE_ADDR']
												) );
	}
	return $result;
}

// Get an svg image html
// @1: string: Name of the .svg file without extension
// @2: string: CSS height or empty, no ; required
// @3: bool: True if for lightbox. Use lightbox colors
// @4: bool: if true: add border
// @5: string: border radius in %: none
// @6: string: border radius in %: light
// @7: string: border radius in %: medium
// @8: string: border radius in %: heavy
function wppa_get_svghtml( $name, $height = false, $lightbox = false, $border = false, $none = '0', $light = '10', $medium = '20', $heavy = '50' ) {

	// Find the colors
	if ( $lightbox ) {
		$fillcolor 	= wppa_opt( 'ovl_svg_color' );
		$bgcolor 	= wppa_opt( 'ovl_svg_bg_color' );
	}
	else {
		$fillcolor 	= wppa_opt( 'svg_color' );
		$bgcolor 	= wppa_opt( 'svg_bg_color' );
	}
	if ( $fillcolor == '' ) $fillcolot = 'transparent';
	if ( $bgcolor == '' ) $bgcolor = 'transparent';

$border = false; // debug

	// Find the border radius
	switch( wppa_opt( 'icon_corner_style' ) ) {
		case 'light':
			$bradius = $light;
			break;
		case 'medium':
			$bradius = $medium;
			break;
		case 'heavy':
			$bradius = $heavy;
			break;
		default:
			$bradius = $none;
			break;
	}

	// Open svg tag
	$result = '
	<svg
		version="1.1"
		xmlns="http://www.w3.org/2000/svg"
		xmlns:xlink="http://www.w3.org/1999/xlink"
		x="0px"
		y="0px"
		viewBox="0 0 30 30"
		style="' .
			( $height ? 'height:' . $height . ';' : '' ) . '
			fill:' . $fillcolor . ';
			background-color:' . $bgcolor . ';
			text-decoration:none !important;
			vertical-align:middle;' .
			( $bradius ? 'border-radius:' . $bradius . '%;' : '' ) .
			( $border ? 'border:2px solid ' . $bgcolor . ';box-sizing:content-box;' : '' ) . '
			"
		xml:space="preserve"
		>';

		// Open g tag
		$result .= '
		<g>';

		switch ( $name ) {

			case 'Next-Button':
				$result .= 	'<path' .
								' d="M30,0H0V30H30V0z M20,20.5' .
									'c0,0.3-0.2,0.5-0.5,0.5S19,20.8,19,20.5v-4.2l-8.3,4.6c-0.1,0-0.2,0.1-0.2,0.1c-0.1,0-0.2,0-0.3-0.1c-0.2-0.1-0.2-0.3-0.2-0.4v-11' .
									'c0-0.2,0.1-0.4,0.3-0.4c0.2-0.1,0.4-0.1,0.5,0l8.2,5.5V9.5C19,9.2,19.2,9,19.5,9S20,9.2,20,9.5V20.5z"' .
							' />';
				break;
			case 'Prev-Button':
				$result .= 	'<path' .
								' d="M30,0H0V30H30V0z M20,20.5c0,0.2-0.1,0.4-0.3,0.4c-0.1,0-0.2,0.1-0.2,0.1c-0.1,0-0.2,0-0.3-0.1L11,15.4v5.1c0,0.3-0.2,0.5-0.5,0.5S10,20.8,10,20.5v-11' .
								'C10,9.2,10.2,9,10.5,9S11,9.2,11,9.5v4.2l8.3-4.6c0.2-0.1,0.3-0.1,0.5,0S20,9.3,20,9.5V20.5z"' .
							' />';
				break;
			case 'Backward-Button':
				$result .= 	'<path' .
								' d="M30,0H0V30H30V0z M23,20.5' .
									'c0,0.2-0.1,0.3-0.2,0.4c-0.2,0.1-0.3,0.1-0.5,0L16,17.4v3.1c0,0.2-0.1,0.4-0.3,0.4c-0.1,0-0.1,0.1-0.2,0.1c-0.1,0-0.2,0-0.3-0.1' .
									'l-8-6C7.1,14.8,7,14.6,7,14.5c0-0.2,0.1-0.3,0.2-0.4l8-5c0.2-0.1,0.3-0.1,0.5,0C15.9,9.2,16,9.3,16,9.5v3.1l6.3-3.6' .
									'c0.2-0.1,0.3-0.1,0.5,0C22.9,9.2,23,9.3,23,9.5V20.5z"' .
							' />';
				break;
			case 'Forward-Button':
				$result .= 	'<path' .
								' d="M30,0H0V30H30V0z' .
									'M22.8,15.9l-8,5c-0.2,0.1-0.3,0.1-0.5,0c-0.2-0.1-0.3-0.3-0.3-0.4v-3.1l-6.3,3.6C7.7,21,7.6,21,7.5,21c-0.1,0-0.2,0-0.3-0.1' .
									'C7.1,20.8,7,20.7,7,20.5v-11c0-0.2,0.1-0.3,0.2-0.4C7.4,9,7.6,9,7.7,9.1l6.3,3.6V9.5c0-0.2,0.1-0.4,0.3-0.4c0.2-0.1,0.4-0.1,0.5,0' .
									'l8,6c0.1,0.1,0.2,0.3,0.2,0.4C23,15.7,22.9,15.8,22.8,15.9z"' .
							' />';
				break;
			case 'Pause-Button':
				$result .= 	'<path' .
								' d="M30,0H0V30H30V0z M14,20.5' .
									'c0,0.3-0.2,0.5-0.5,0.5h-4C9.2,21,9,20.8,9,20.5v-11C9,9.2,9.2,9,9.5,9h4C13.8,9,14,9.2,14,9.5V20.5z M21,20.5' .
									'c0,0.3-0.2,0.5-0.5,0.5h-4c-0.3,0-0.5-0.2-0.5-0.5v-11C16,9.2,16.2,9,16.5,9h4C20.8,9,21,9.2,21,9.5V20.5z"' .
							' />';
				break;
			case 'Play-Button':
				$result .= 	'<path' .
								' d="M30,0H0V30H30V0z' .
									'M19.8,14.9l-8,5C11.7,20,11.6,20,11.5,20c-0.1,0-0.2,0-0.2-0.1c-0.2-0.1-0.3-0.3-0.3-0.4v-9c0-0.2,0.1-0.3,0.2-0.4' .
									'c0.1-0.1,0.3-0.1,0.5,0l8,4c0.2,0.1,0.3,0.2,0.3,0.4C20,14.7,19.9,14.8,19.8,14.9z"' .
							' />';
				break;
			case 'Stop-Button':
				$result .= 	'<path' .
								' d="M30,0H0V30H30V0z M21,20.5' .
									'c0,0.3-0.2,0.5-0.5,0.5h-11C9.2,21,9,20.8,9,20.5v-11C9,9.2,9.2,9,9.5,9h11C20.8,9,21,9.2,21,9.5V20.5z"' .
							'/>';
				break;
			case 'Eagle-1':
				$result .= 	'<path' .
								' d="M29.9,19.2c-0.1-0.1-0.2-0.2-0.4-0.2c-3.7,0-6.2-0.6-7.6-1.1c-0.1,0-0.1,0.1-0.2,0.1c-1.2,1.2-4,2.6-4.6,2.9' .
									'c-0.1,0-0.1,0.1-0.2,0.1c-0.2,0-0.4-0.1-0.4-0.3c-0.1-0.2,0-0.5,0.2-0.7c0.3-0.2,2.9-1.4,4.1-2.5l0,0c0.1-0.1,0.1-0.1,0.2-0.2' .
									'c0.7-0.7,2.5-0.5,3.3-0.3c0,0,0.1,0,0.1,0c0.2,0.1,0.4,0,0.5-0.2c0,0,0,0,0,0c0,0,0,0,0,0c0.1-0.2,0.1-0.3,0.2-0.5c0,0,0-0.1,0-0.1' .
									'c0-0.1,0.1-0.3,0.1-0.4c0,0,0-0.1,0-0.1c0-0.1,0-0.3,0-0.4c0,0,0-0.1,0-0.1c0-0.1-0.1-0.3-0.2-0.4c0,0,0,0,0-0.1' .
									'c-0.1-0.1-0.1-0.2-0.2-0.2c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1-0.1-0.2-0.1c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1-0.1-0.2-0.1' .
									'c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.1-0.1-0.2-0.1c0,0-0.1,0-0.1-0.1c-0.1,0-0.2-0.1-0.2-0.1c0,0-0.1,0-0.1,0c-0.4-0.1-0.7-0.2-1-0.2' .
									'c0-0.1-0.1-0.2-0.1-0.3c-0.1-0.2-0.2-0.3-0.3-0.5c-0.2-0.2-0.4-0.3-0.6-0.4C21,12.1,20.6,12,20,12c-0.3,0-0.6,0-0.8,0.1' .
									'c-0.1,0-0.1,0-0.2,0c-0.2,0-0.5,0.1-0.7,0.1c0,0-0.1,0-0.1,0c-0.2,0.1-0.5,0.1-0.7,0.2c0,0,0,0,0,0c-1.2,0.5-2.2,1.2-3,1.8' .
									'c-0.5,0.3-0.9,0.6-1.2,0.8c-0.2,0.1-0.5,0-0.7-0.2c-0.1-0.3,0-0.5,0.2-0.7c0.2-0.1,0.6-0.4,0.9-0.6c-1.6-0.6-4-2-4-5.4' .
									'c0-4.1,1.9-5.6,3.2-6.6c0.3-0.2,0.6-0.4,0.8-0.7C14,0.7,14,0.5,14,0.3S13.7,0,13.5,0C10.1,0,8.1,2,7,3.5v-1C7,2.3,6.9,2.1,6.7,2' .
									'C6.5,2,6.3,2,6.1,2.1C4.5,3.8,3.9,5.4,3.7,6.8L3.4,6.3C3.4,6.1,3.2,6,3.1,6S2.8,6,2.6,6.1C1.8,7,1.3,8,1.3,9c0,0.5,0.1,1,0.3,1.4' .
									'l-1-0.4c-0.2-0.1-0.3,0-0.5,0.1C0.1,10.2,0,10.3,0,10.5c0,2.7,0.5,4.4,1.4,5.2c0.1,0.1,0.2,0.1,0.3,0.2C1.4,16.4,1,17.4,1,18.5' .
									'c0,1.5,2.6,2.5,4.5,3c-1,0.4-2,1-2,2c0,0.5-1.6,1.2-3.1,1.5c-0.2,0-0.3,0.2-0.4,0.4c-0.1,0.2,0,0.4,0.2,0.5C0.4,26,4.9,30,8.5,30' .
									'C8.8,30,9,29.8,9,29.5c0-3.1,3.5-5,4.5-5.4c0.6,0.3,2,0.9,5,0.9c1.9,0,2.9-0.3,3.2-1l1.6,0.9c0.1,0,0.2,0.1,0.3,0.1' .
									'c3.4,0,4.3-1.1,4.4-1.2c0.1-0.2,0.1-0.5-0.1-0.6l-0.8-0.8c2.1-0.6,2.9-2.6,2.9-2.7C30,19.5,30,19.3,29.9,19.2z M20.5,14' .
									'c0.3,0,0.5,0.2,0.5,0.5S20.8,15,20.5,15S20,14.8,20,14.5S20.2,14,20.5,14z"' .
							' />';
				break;
			case 'Snail':
				$result .= 	'<path' .
								' d="M28.5,16.3L30,9.1c0.1-0.3-0.1-0.5-0.4-0.6c-0.3-0.1-0.5,0.1-0.6,0.4L27.6,16c0,0-0.1,0-0.1,0L27,10c0-0.3-0.3-0.5-0.5-0.5' .
									'C26.2,9.5,26,9.8,26,10l0.5,6.1c-0.4,0.1-0.7,0.2-1.1,0.3l0,0c-1.4,2-4.8,4.1-6.9,4.1c-1.9,0-3.8-0.1-5.2-1.1' .
									'c-0.1-0.1-0.2-0.2-0.2-0.4c0-0.1,0-0.3,0.2-0.4l1.2-1.1c1.5-1.9,1.6-4.7,1.6-5.5c0-1.8-1.2-5.5-5-5.5c-3.7,0-5,2.7-5,5' .
									'c0,2.7,2.1,3,3,3c1.5,0,3-1.3,3-2.5c0-1.1-0.4-1.5-1.5-1.5C9.4,10.5,9,10.9,9,12c0,0.3-0.2,0.5-0.5,0.5S8,12.3,8,12' .
									'c0-1.6,0.9-2.5,2.5-2.5c1.7,0,2.5,0.8,2.5,2.5c0,1.8-1.9,3.5-4,3.5c-1.9,0-4-1.1-4-4c0-3,1.9-6,6-6c4.1,0,6,3.8,6,6.5' .
									'c0,1.1-0.2,4-1.8,6.1l-0.8,0.7c1.2,0.5,2.6,0.6,4.1,0.6c1.8,0,5.2-2.3,6.2-3.9l0,0c0.3-0.7,0.3-1.6,0.3-2.7c0-0.3,0-0.5,0-0.8' .
									'c0-2-3-9.5-12-9.5C4.8,2.5,1,7.9,1,13c0,3,1.3,5.3,3.8,6.5c-0.5,0.4-1.4,1.1-2.6,1.6C0.1,21.8,0,24.9,0,25c0,0.2,0.1,0.4,0.3,0.4' .
									'c0.2,0.1,0.4,0.1,0.5,0c0,0,1.3-0.9,4.1-0.9c1.6,0,2.6,0.6,3.6,1c0.7,0.4,1.3,0.7,2.1,0.7c0.5,0,0.6,0.1,0.8,0.4' .
									'c0.3,0.4,0.6,0.8,1.7,0.8c1,0,1.4-0.3,1.8-0.6c0.3-0.2,0.6-0.4,1.2-0.4c0.6,0,0.9,0.2,1.3,0.4c0.4,0.3,1,0.6,1.9,0.6' .
									'c1.4,0,1.6-1,1.8-1.6c0.1-0.4,0.2-0.8,0.4-1c0.2-0.2,0.4-0.1,1,0.1c0.6,0.2,1.4,0.6,2.1-0.1c0.6-0.6,0.7-1.1,0.8-1.5' .
									'c0.1-0.4,0.1-0.6,0.5-1c0.6-0.5,2-0.1,2.4,0.1c0.2,0.1,0.5,0,0.6-0.2c0-0.1,1.1-1.7,1.1-3.3C30,17.8,29.4,16.8,28.5,16.3z"' .
							' />';
				break;
			case 'Exit':
				$result .= 	'<path d="M30 24.398l-8.406-8.398 8.406-8.398-5.602-5.602-8.398 8.402-8.402-8.402-5.598 5.602 8.398 8.398-8.398 8.398 5.598 5.602 8.402-8.402 8.398 8.402z"></path>';
				break;
			case 'Exit-2':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z ' .
								'M9 4 L15 10 L21 4 L26 9 L20 15 L26 21 L21 26 L15 20 L9 26 L4 21 L10 15 L4 9Z' .
								'"' .
							' />';
				break;
			case 'Full-Screen':
				$result .= 	'<path d="M27.414 24.586l-4.586-4.586-2.828 2.828 4.586 4.586-4.586 4.586h12v-12zM12 0h-12v12l4.586-4.586 4.543 4.539 2.828-2.828-4.543-4.539zM12 22.828l-2.828-2.828-4.586 4.586-4.586-4.586v12h12l-4.586-4.586zM32 0h-12l4.586 4.586-4.543 4.539 2.828 2.828 4.543-4.539 4.586 4.586z"></path>';
				break;
			case 'Full-Screen-2':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z ' .
								'M4 4 L12 4 L10 6 L14 10 L10 14 L6 10 L4 12Z' .
								'M18 4 L26 4 L26 12 L24 10 L20 14 L16 10 L20 6Z' .
								'M26 26 L18 26 L20 24 L16 20 L20 16 L24 20 L26 18Z' .
								'M4 26 L4 18 L6 20 L10 16 L14 20 L10 24 L12 26Z' .
								'"' .
							' />';

				break;
			case 'Exit-Full-Screen':
				$result .= 	'<path d="M24.586 27.414l4.586 4.586 2.828-2.828-4.586-4.586 4.586-4.586h-12v12zM0 12h12v-12l-4.586 4.586-4.539-4.543-2.828 2.828 4.539 4.543zM0 29.172l2.828 2.828 4.586-4.586 4.586 4.586v-12h-12l4.586 4.586zM20 12h12l-4.586-4.586 4.547-4.543-2.828-2.828-4.547 4.543-4.586-4.586z"></path>';
				break;
			case 'Exit-Full-Screen-2':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z ' .
								'M17 17 L25 17 L23 19 L27 23 L23 27 L19 23 L17 25Z' .
								'M5 17 L13 17 L13 25 L11 23 L7 27 L3 23 L7 19Z' .
								'M13 13 L5 13 L7 11 L3 7 L7 3 L11 7 L13 5Z' .
								'M17 13 L17 5 L19 7 L23 3 L27 7 L23 11 L25 13Z' .
								'"' .
							' />';
				break;
			case 'Content-View':
				$result .= 	'<path' .
								' d="M21.5,25.5h4c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4c-0.276,0-0.5,0.224-0.5,0.5S21.224,25.5,21.5,25.5z' .
									'M21.5,18.5h4c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4c-0.276,0-0.5,0.224-0.5,0.5S21.224,18.5,21.5,18.5z M21.5,23.5h4' .
									'c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4c-0.276,0-0.5,0.224-0.5,0.5S21.224,23.5,21.5,23.5z M21.5,16.5h4' .
									'c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4c-0.276,0-0.5,0.224-0.5,0.5S21.224,16.5,21.5,16.5z M21.5,11.5h4' .
									'c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4c-0.276,0-0.5,0.224-0.5,0.5S21.224,11.5,21.5,11.5z M26.864,0.5H3.136' .
									'C1.407,0.5,0,1.866,0,3.545v22.91C0,28.134,1.407,29.5,3.136,29.5h23.728c1.729,0,3.136-1.366,3.136-3.045V3.545' .
									'C30,1.866,28.593,0.5,26.864,0.5z M9.5,2.5C9.776,2.5,10,2.724,10,3S9.776,3.5,9.5,3.5S9,3.276,9,3S9.224,2.5,9.5,2.5z M6.5,2.5' .
									'C6.776,2.5,7,2.724,7,3S6.776,3.5,6.5,3.5S6,3.276,6,3S6.224,2.5,6.5,2.5z M3.5,2.5C3.776,2.5,4,2.724,4,3S3.776,3.5,3.5,3.5' .
									'S3,3.276,3,3S3.224,2.5,3.5,2.5z M29,26.455c0,1.128-0.958,2.045-2.136,2.045H3.136C1.958,28.5,1,27.583,1,26.455V5.5h28V26.455z' .
									'M21.5,9.5h4C25.776,9.5,26,9.276,26,9s-0.224-0.5-0.5-0.5h-4C21.224,8.5,21,8.724,21,9S21.224,9.5,21.5,9.5z M4.5,25.5h2' .
									'C6.776,25.5,7,25.276,7,25v-2c0-0.276-0.224-0.5-0.5-0.5h-2C4.224,22.5,4,22.724,4,23v2C4,25.276,4.224,25.5,4.5,25.5z M17.5,11.5' .
									'h2c0.276,0,0.5-0.224,0.5-0.5V9c0-0.276-0.224-0.5-0.5-0.5h-2C17.224,8.5,17,8.724,17,9v2C17,11.276,17.224,11.5,17.5,11.5z' .
									'M8.5,25.5h4c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4C8.224,24.5,8,24.724,8,25S8.224,25.5,8.5,25.5z M8.5,18.5h4' .
									'c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4C8.224,17.5,8,17.724,8,18S8.224,18.5,8.5,18.5z M8.5,23.5h4' .
									'c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4C8.224,22.5,8,22.724,8,23S8.224,23.5,8.5,23.5z M4.5,11.5h2' .
									'C6.776,11.5,7,11.276,7,11V9c0-0.276-0.224-0.5-0.5-0.5h-2C4.224,8.5,4,8.724,4,9v2C4,11.276,4.224,11.5,4.5,11.5z M4.5,18.5h2' .
									'C6.776,18.5,7,18.276,7,18v-2c0-0.276-0.224-0.5-0.5-0.5h-2C4.224,15.5,4,15.724,4,16v2C4,18.276,4.224,18.5,4.5,18.5z M17.5,25.5' .
									'h2c0.276,0,0.5-0.224,0.5-0.5v-2c0-0.276-0.224-0.5-0.5-0.5h-2c-0.276,0-0.5,0.224-0.5,0.5v2C17,25.276,17.224,25.5,17.5,25.5z' .
									'M17.5,18.5h2c0.276,0,0.5-0.224,0.5-0.5v-2c0-0.276-0.224-0.5-0.5-0.5h-2c-0.276,0-0.5,0.224-0.5,0.5v2' .
									'C17,18.276,17.224,18.5,17.5,18.5z M8.5,16.5h4c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4C8.224,15.5,8,15.724,8,16' .
									'S8.224,16.5,8.5,16.5z M8.5,9.5h4C12.776,9.5,13,9.276,13,9s-0.224-0.5-0.5-0.5h-4C8.224,8.5,8,8.724,8,9S8.224,9.5,8.5,9.5z' .
									'M8.5,11.5h4c0.276,0,0.5-0.224,0.5-0.5s-0.224-0.5-0.5-0.5h-4C8.224,10.5,8,10.724,8,11S8.224,11.5,8.5,11.5z"' .
							' />';
				break;
			case 'Left-4':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z' .
									'M24.5,19c0,0.3-0.2,0.5-0.5,0.5h-9.5V24' .
									'c0,0.2-0.1,0.4-0.3,0.5c-0.1,0-0.1,0-0.2,0c-0.1,0-0.3-0.1-0.4-0.1l-9-9c-0.2-0.2-0.2-0.5,0-0.7l9-9c0.1-0.1,0.4-0.2,0.5-0.1' .
									'c0.2,0.1,0.3,0.3,0.3,0.5v4.5H24c0.3,0,0.5,0.2,0.5,0.5V19z"' .
							'/>';
				break;
			case 'Right-4':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z' .
									'M5.5,11c0-0.3,0.2-0.5,0.5-0.5h9.5V6' .
									'c0-0.2,0.1-0.4,0.3-0.5c0.1,0,0.1,0,0.2,0c0.1,0,0.3,0.1,0.4,0.1l9,9c0.2,0.2,0.2,0.5,0,0.7l-9,9c-0.1,0.1-0.4,0.2-0.5,0.1' .
									'c-0.2-0.1-0.3-0.3-0.3-0.5v-4.5H6c-0.3,0-0.5-0.2-0.5-0.5V11z"' .
							'/>';
				break;
			case 'Up-4':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z' .
									'M11,24.5c-0.3,0-0.5-0.2-0.5-0.5v-9.5H6' .
									'c-0.2,0-0.4-0.1-0.5-0.3c0-0.1,0-0.1,0-0.2c0-0.1,0.1-0.3,0.1-0.4l9-9c0.2-0.2,0.5-0.2,0.7,0l9,9c0.1,0.1,0.2,0.4,0.1,0.5' .
									'c-0.1,0.2-0.3,0.3-0.5,0.3h-4.5V24c0,0.3-0.2,0.5-0.5,0.5H11z"' .
							'/>';
				break;
			case 'Down-4':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z' .
									'M19,5.5c0.3,0,0.5,0.2,0.5,0.5v9.5H24' .
									'c0.2,0,0.4,0.1,0.5,0.3c0,0.1,0,0.1,0,0.2c0,0.1-0.1,0.3-0.1,0.4l-9,9c-0.2,0.2-0.5,0.2-0.7,0l-9-9c-0.1-0.1-0.2-0.4-0.1-0.5' .
									'c0.1-0.2,0.3-0.3,0.5-0.3h4.5V6c0-0.3,0.2-0.5,0.5-0.5H19z"' .
							'/>';
				break;
			case 'ZoomIn':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z' .
								'M5.5,11h5.5v-5.5h8v5.5h5.5v8h-5.5v5.5h-8v-5.5h-5.5z' .
									'"' .
							'/>';
				break;
			case 'ZoomOut':
				$result .= '<path' .
								' d="M30,0H0V30H30V0z' .
								'M5.5,11h19v8h-19z' .
									'"' .
							'/>';
				break;
			case 'Redo':
				$result .= '<path d="M29.8,12.6l-14-9c-0.2-0.1-0.4-0.1-0.5,0C15.1,3.7,15,3.8,15,4v5.5C5,9.8,0.1,19.4,0,26l0,0c0,0,0,0,0,0l0,0c0,0,0,0,0,0.1
								v0v0l0,0c0,0.2,0.2,0.4,0.5,0.4c0,0,0,0,0,0C0.8,26.5,1,26.3,1,26c0.1-1.5,6.7-8.2,14-8.5V23c0,0.2,0.1,0.4,0.3,0.4
								c0.2,0.1,0.4,0.1,0.5,0l14-10c0.1-0.1,0.2-0.3,0.2-0.4S29.9,12.7,29.8,12.6z"/>';
				break;
			default:
				$result .= '<path d="M30,0H0V30H30V0z" />';
				break;
		}

		$result .= 		'</g>' .
					'</svg>';

		if ( is_ssl() ) {
			$result = str_replace( 'http://', 'https://', $result );
		}
		$result = wppa_compress_html( $result );
		return $result;

}

function wppa_get_mime_type( $id ) {

	$ext = strtolower( wppa_get_photo_item( $id, 'ext' ) );
	if ( $ext == 'xxx' ) {
		$ext = wppa_geter_ext( $id );
	}

	switch ( $ext ) {
		case 'jpg':
		case 'jpeg':
			$result = 'image/jpeg';
			break;
		case 'png':
			$result = 'image/png';
			break;
		case 'gif':
			$result = 'image/gif';
			break;
		default:
			$result = '';
	}

	return $result;
}

// Test if a given url is to a photo file
function wppa_is_url_a_photo( &$url, $save = true ) {
global $wppa_supported_photo_extensions;
global $wppa_session;

	// Been here before?
	if ( isset( $wppa_session['rem_url'][$url] ) ) {
		return $wppa_session['rem_url'][$url];
	}

	// Check existence
	if ( ! wppa_remote_file_exists( $url, $save ) ) {
		$wppa_session['rem_url'][$url] = false;
		if ( count( $wppa_session['rem_url'] ) > 100 ) array_shift( $wppa_session['rem_url'] );
		return false;
	}

	// Init
	$result = true;
	$ext 	= wppa_get_ext( $url );

	// If the url does not have a valid photo extension, its not a photo file
	if ( ! in_array( $ext, $wppa_supported_photo_extensions ) ) {
		$wppa_session['rem_url'][$url] = false;
		if ( count( $wppa_session['rem_url'] ) > 100 ) array_shift( $wppa_session['rem_url'] );
		return false;
	}

	// Using curl may be protected/limited
	// Use curl to see if the url is found to prevent a php warning
	/* experimental */
	if ( function_exists( 'curl_init' ) && false ) {

		// Create a curl handle to the expected photofile
		$ch = curl_init( $url );

		// Execute
		curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
		curl_setopt( $ch, CURLOPT_FAILONERROR, true );
		curl_exec( $ch );

		// Check if HTTP code > 400 i.e. error 22 occurred
		if( curl_errno( $ch ) == 22 ) {
			$result = false;
		}

		// Close handle
		curl_close($ch);

	}

	// No curl on system, or do not use curl
	else {

		// getimagesize on a non imagefile produces a php warning
		$result = is_array( @ getimagesize( $url ) );
	}

	// Done
	$wppa_session['rem_url'][$url] = $result;
	if ( count( $wppa_session['rem_url'] ) > 100 ) array_shift( $wppa_session['rem_url'] );
	return $result;
}

function wppa_get_like_title_a( $id ) {
global $wpdb;

	$me 	= wppa_get_user();
	$likes 	= wppa_get_photo_item( $id, 'rating_count');
	$mylike = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->wppa_rating WHERE photo = %d AND user = %s", $id, $me ) );

	if ( $mylike ) {
		if ( $likes > 1 ) {
			$text = sprintf( _n( 'You and %d other person like this', 'You and %d other people like this', $likes - 1 ), $likes - 1 );
		}
		else {
			$text = __( 'You are the first one who likes this', 'wp-photo-album-plus' );
		}
		$text .= "\n"
 . __( 'Click again if you do no longer like this', 'wp-photo-album-plus' );
	}
	else {
		if ( $likes ) {
			$text = sprintf( _n( '%d person likes this', '%d people like this', $likes, 'wp-photo-album-plus' ), $likes );
		}
		else {
			$text = __( 'Be the first one to like this', 'wp-photo-album-plus' );
		}
	}
	$result['title']  	= $text;
	$result['mine']  	= $mylike;
	$result['total'] 	= $likes;
	$result['display'] 	= sprintf( _n( '%d like', '%d likes', $likes ), $likes );

	return $result;
}

function wppa_print_tree( $path ) {
	$path = rtrim( $path, '/' );
	echo $path . '<br />';
	$files = wppa_glob( $path . '/*' );
	foreach( $files as $file ) {
		echo $file . '<br />';
		if ( wppa_is_dir( $file ) ) {
			wppa_print_tree( $file );
		}
	}
}

// Returns available memory in bytes
function wppa_memry_limit() {

	// get memory limit
	$memory_limit = 0;
	$memory_limini = wppa_convert_bytes( ini_get( 'memory_limit' ) );
	$memory_limcfg = wppa_convert_bytes( get_cfg_var( 'memory_limit' ) );

	// find the smallest not being zero
	if ( $memory_limini && $memory_limcfg ) $memory_limit = min( $memory_limini, $memory_limcfg );
	elseif ( $memory_limini ) $memory_limit = $memory_limini;
	else $memory_limit = $memory_limcfg;

	// No data, return 64MB
	if ( ! $memory_limit ) {
		return 64 * 1024 * 1024;
	}

	return $memory_limit;
}

// Create qr code cache and return its url
function wppa_create_qrcode_cache( $url, $size = '80' ) {

	$qrsrc = 	'http' . ( is_ssl() ? 's' : '' ) . '://api.qrserver.com/v1/create-qr-code/' .
				'?format=' . ( wppa_switch( 'qr_cache' ) ? 'svg' : 'png' ) .
				'&size=' . strval( intval( $size ) ) . 'x' . strval( intval( $size ) ) .
				'&color=' . trim( wppa_opt( 'qr_color' ), '#' ) .
				'&bgcolor=' . trim( wppa_opt( 'qr_bgcolor' ), '#' ) .
				'&data=' . urlencode( wppa_convert_to_pretty( $url ) );

	// Anything to do here?
	if ( ! wppa_switch( 'qr_cache' ) ) {
		return $qrsrc;
	}

	// Make sure we have .../uploads/wppa/qr
	if ( ! wppa_is_dir( WPPA_UPLOAD_PATH . '/qr' ) ) {
		wppa_mkdir( WPPA_UPLOAD_PATH . '/qr' );
	}

	// In cache already?
	$key = md5( $qrsrc );
	if ( is_file( WPPA_UPLOAD_PATH . '/qr/' . $key . '.svg' ) ) {

		// Bump cache found counter
		update_option( 'wppa_qr_cache_hits', wppa_get_option( 'wppa_qr_cache_hits', 0 ) + 1 );
		return WPPA_UPLOAD_URL . '/qr/' . $key . '.svg';
	}

	// Bump cache miss counter
	update_option( 'wppa_qr_cache_miss', wppa_get_option( 'wppa_qr_cache_miss', 0 ) + 1 );

	// Catch the qr image
	$curl = curl_init();
	curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 );
	curl_setopt( $curl, CURLOPT_URL, $qrsrc );
	$contents = curl_exec( $curl );
	curl_close( $curl );

	// Save the image
	if ( strlen( $contents ) > 1000 ) {

		wppa_put_contents( WPPA_UPLOAD_PATH . '/qr/' . $key . '.svg', $contents );

		/*
		$file = wppa _fopen( WPPA_UPLOAD_PATH . '/qr/' . $key . '.svg', 'w' );
		if ( $file ) {
			fwrite( $file, $contents, strlen( $contents ) );
			fclose( $file );
		}
		*/
	}

	if ( wppa_is_file( WPPA_UPLOAD_PATH . '/qr/' . $key . '.svg' ) ) {
		return WPPA_UPLOAD_URL . '/qr/' . $key . '.svg';
	}
	else {
		return $qrsrc;
	}
}


function wppa_use_svg( $is_admin = false ) {
	if ( wppa_is_ie() ) {
		return false;
	}
	if ( ! $is_admin && wppa_opt( 'icon_corner_style' ) == 'gif' ) {
		return false;
	}
	return true;
}


function wppa_get_spinner_svg_html( $xargs = array() ) {

	$defaults = array(
					'id' 		=> 'wppa-spinner',
					'class' 	=> 'wppa-spinner',
					'size' 		=> '120',
					'position' 	=> 'fixed',
					'lightbox' 	=> false,
					'display' 	=> 'none',
					'left' 		=> '50%',
					'top' 		=> '50%',
					'margin' 	=> false,
					);

	$args 	= wp_parse_args( $xargs, $defaults );
	if ( $args['margin'] === false ) $args['margin'] = $args['size'] / 2;
	$width  = $args['size'];
	$height = $args['size'];
	$type 	= wppa_opt( 'spinner_shape' );
	$corner = wppa_opt( 'icon_corner_style' );
	$fgcol 	= $args['lightbox'] ? wppa_opt( 'ovl_svg_color' ) : wppa_opt( 'svg_color' );
	$bgcol 	= $args['lightbox'] ? wppa_opt( 'ovl_svg_bg_color' ) : wppa_opt( 'svg_bg_color' );
	$stcol 	= $fgcol;

	// .svg requested and possible?
	if ( wppa_use_svg() ) {
		switch ( $type ) {

			case 'audio':
				$viewbox = '0 0 55 80';
				$width = round( $args['size'] * 55 / 80 );
				break;

			case 'ball-triangle':
				$viewbox = '0 0 57 57';
				break;

			case 'bars':
				$viewbox = '0 0 135 140';
				$width = round( $args['size'] * 135 / 140 );
				break;

			case 'circles':
				$viewbox = '0 0 135 135';
				$stcol = '';
				break;

			case 'grid':
				$viewbox = '0 0 105 105';
				$stcol = '';
				break;

			case 'hearts':
				$viewbox = '0 0 140 64';
				$height = round( $args['size'] * 64 / 140 );
				break;

			case 'puff':
				$viewbox = '0 0 44 44';
				break;

			case 'rings':
				$viewbox = '0 0 45 45';
				break;

			case 'spinning-circles':
				$viewbox = '0 0 58 58';
				break;

			case 'oval':
			case 'tail-spin':
				$viewbox = '0 0 38 38';
				break;

			case 'three-dots':
				$viewbox = '0 0 120 30';
				$height = round( $args['size'] / 4 );
				break;

			default:
				$viewbox = '0 0 100 100';
				$stcol = '';
				break;
		}

		switch ( $corner ) {

			case 'light':
				$bradius = round( $args['size'] / 10 );
				break;
			case 'medium':
				$bradius = round( $args['size'] / 5 );
				break;
			case 'heavy':
				$bradius = round( $args['size'] / 2 );
				break;
			default: // case 'gif': case 'none':
				$bradius = '0';
				break;
		}

		$result =
			'<svg' .
				' id="' . $args['id'] . '"' .
				' class="' . $args['class'] . ' uil-default"' .
				' width="' . $width . 'px"' .
				' height="' . $height . 'px"' .
				' xmlns="http://www.w3.org/2000/svg"' .
				' viewBox="' . $viewbox . '"' .
				' preserveAspectRatio="xMidYMid"' .
				' stroke="' . $stcol . '"' .
				' style="' .
					'width:' . $width . 'px;' .
					'height:' . $height . 'px;' .
					'position:' . $args['position'] . ';' .
					'top:' . $args['top'] . ';' .
					'margin-top:-' . $args['margin'] . 'px;' .
					'left:' . $args['left'] . ';' .
					'margin-left:-' . $args['margin'] . 'px;' .
					'z-index:200100;' .
					'opacity:1;' .
					'display:' . $args['display'] . ';' .
					'fill:' . $fgcol . ';' .
					'background-color:' . $bgcol . ';' .
					'box-shadow:none;' .
					'border-radius:' . $bradius .'px;' .
					'"' .
				' >';

		switch ( $type ) {

			case 'audio':
				$result .=
					'<g transform="matrix(1 0 0 -1 0 80)">
        <rect width="10" height="20" rx="3">
            <animate attributeName="height"
                 begin="0s" dur="4.3s"
                 values="20;45;57;80;64;32;66;45;64;23;66;13;64;56;34;34;2;23;76;79;20" calcMode="linear"
                 repeatCount="indefinite" />
        </rect>
        <rect x="15" width="10" height="80" rx="3">
            <animate attributeName="height"
                 begin="0s" dur="2s"
                 values="80;55;33;5;75;23;73;33;12;14;60;80" calcMode="linear"
                 repeatCount="indefinite" />
        </rect>
        <rect x="30" width="10" height="50" rx="3">
            <animate attributeName="height"
                 begin="0s" dur="1.4s"
                 values="50;34;78;23;56;23;34;76;80;54;21;50" calcMode="linear"
                 repeatCount="indefinite" />
        </rect>
        <rect x="45" width="10" height="30" rx="3">
            <animate attributeName="height"
                 begin="0s" dur="2s"
                 values="30;45;13;80;56;72;45;76;34;23;67;30" calcMode="linear"
                 repeatCount="indefinite" />
        </rect>
    </g>';
				break;

			case 'ball-triangle':
				$result .=
					'<g fill="none" fill-rule="evenodd">
						<g transform="translate(1 1)" stroke-width="2">
							<circle cx="5" cy="50" r="5">
								<animate attributeName="cy"
									 begin="0s" dur="2.2s"
									 values="50;5;50;50"
									 calcMode="linear"
									 repeatCount="indefinite" />
								<animate attributeName="cx"
									 begin="0s" dur="2.2s"
									 values="5;27;49;5"
									 calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="27" cy="5" r="5">
								<animate attributeName="cy"
									 begin="0s" dur="2.2s"
									 from="5" to="5"
									 values="5;50;50;5"
									 calcMode="linear"
									 repeatCount="indefinite" />
								<animate attributeName="cx"
									 begin="0s" dur="2.2s"
									 from="27" to="27"
									 values="27;49;5;27"
									 calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="49" cy="50" r="5">
								<animate attributeName="cy"
									 begin="0s" dur="2.2s"
									 values="50;50;5;50"
									 calcMode="linear"
									 repeatCount="indefinite" />
								<animate attributeName="cx"
									 from="49" to="49"
									 begin="0s" dur="2.2s"
									 values="49;5;27;49"
									 calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
						</g>
					</g>';
				break;

			case 'bars':
				$result .=
					'<rect y="10" width="15" height="120" rx="6">
						<animate attributeName="height"
							 begin="0.5s" dur="1s"
							 values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
							 repeatCount="indefinite" />
						<animate attributeName="y"
							 begin="0.5s" dur="1s"
							 values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
							 repeatCount="indefinite" />
					</rect>
					<rect x="30" y="10" width="15" height="120" rx="6">
						<animate attributeName="height"
							 begin="0.25s" dur="1s"
							 values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
							 repeatCount="indefinite" />
						<animate attributeName="y"
							 begin="0.25s" dur="1s"
							 values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
							 repeatCount="indefinite" />
					</rect>
					<rect x="60" width="15" height="140" rx="6">
						<animate attributeName="height"
							 begin="0s" dur="1s"
							 values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
							 repeatCount="indefinite" />
						<animate attributeName="y"
							 begin="0s" dur="1s"
							 values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
							 repeatCount="indefinite" />
					</rect>
					<rect x="90" y="10" width="15" height="120" rx="6">
						<animate attributeName="height"
							 begin="0.25s" dur="1s"
							 values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
							 repeatCount="indefinite" />
						<animate attributeName="y"
							 begin="0.25s" dur="1s"
							 values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
							 repeatCount="indefinite" />
					</rect>
					<rect x="120" y="10" width="15" height="120" rx="6">
						<animate attributeName="height"
							 begin="0.5s" dur="1s"
							 values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
							 repeatCount="indefinite" />
						<animate attributeName="y"
							 begin="0.5s" dur="1s"
							 values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
							 repeatCount="indefinite" />
					</rect>';
				break;

			case 'circles':
				$result .=
					'<path d="M67.447 58c5.523 0 10-4.477 10-10s-4.477-10-10-10-10 4.477-10 10 4.477 10 10 10zm9.448 9.447c0 5.523 4.477 10 10 10 5.522 0 10-4.477 10-10s-4.478-10-10-10c-5.523 0-10 4.477-10 10zm-9.448 9.448c-5.523 0-10 4.477-10 10 0 5.522 4.477 10 10 10s10-4.478 10-10c0-5.523-4.477-10-10-10zM58 67.447c0-5.523-4.477-10-10-10s-10 4.477-10 10 4.477 10 10 10 10-4.477 10-10z">
						<animateTransform
							attributeName="transform"
							type="rotate"
							from="0 67 67"
							to="-360 67 67"
							dur="2.5s"
							repeatCount="indefinite"/>
					</path>
					<path d="M28.19 40.31c6.627 0 12-5.374 12-12 0-6.628-5.373-12-12-12-6.628 0-12 5.372-12 12 0 6.626 5.372 12 12 12zm30.72-19.825c4.686 4.687 12.284 4.687 16.97 0 4.686-4.686 4.686-12.284 0-16.97-4.686-4.687-12.284-4.687-16.97 0-4.687 4.686-4.687 12.284 0 16.97zm35.74 7.705c0 6.627 5.37 12 12 12 6.626 0 12-5.373 12-12 0-6.628-5.374-12-12-12-6.63 0-12 5.372-12 12zm19.822 30.72c-4.686 4.686-4.686 12.284 0 16.97 4.687 4.686 12.285 4.686 16.97 0 4.687-4.686 4.687-12.284 0-16.97-4.685-4.687-12.283-4.687-16.97 0zm-7.704 35.74c-6.627 0-12 5.37-12 12 0 6.626 5.373 12 12 12s12-5.374 12-12c0-6.63-5.373-12-12-12zm-30.72 19.822c-4.686-4.686-12.284-4.686-16.97 0-4.686 4.687-4.686 12.285 0 16.97 4.686 4.687 12.284 4.687 16.97 0 4.687-4.685 4.687-12.283 0-16.97zm-35.74-7.704c0-6.627-5.372-12-12-12-6.626 0-12 5.373-12 12s5.374 12 12 12c6.628 0 12-5.373 12-12zm-19.823-30.72c4.687-4.686 4.687-12.284 0-16.97-4.686-4.686-12.284-4.686-16.97 0-4.687 4.686-4.687 12.284 0 16.97 4.686 4.687 12.284 4.687 16.97 0z">
						<animateTransform
							attributeName="transform"
							type="rotate"
							from="0 67 67"
							to="360 67 67"
							dur="8s"
							repeatCount="indefinite"/>
					</path>';
				break;

			case 'grid':
				$result .=
					'<circle cx="12.5" cy="12.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="0s" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="12.5" cy="52.5" r="12.5" fill-opacity=".5">
						<animate attributeName="fill-opacity"
						 begin="100ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="52.5" cy="12.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="300ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="52.5" cy="52.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="600ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="92.5" cy="12.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="800ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="92.5" cy="52.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="400ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="12.5" cy="92.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="700ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="52.5" cy="92.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="500ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>
					<circle cx="92.5" cy="92.5" r="12.5">
						<animate attributeName="fill-opacity"
						 begin="200ms" dur="1s"
						 values="1;.2;1" calcMode="linear"
						 repeatCount="indefinite" />
					</circle>';
				break;

			case 'hearts':
				$result .=
					'<path d="M30.262 57.02L7.195 40.723c-5.84-3.976-7.56-12.06-3.842-18.063 3.715-6 11.467-7.65 17.306-3.68l4.52 3.76 2.6-5.274c3.717-6.002 11.47-7.65 17.305-3.68 5.84 3.97 7.56 12.054 3.842 18.062L34.49 56.118c-.897 1.512-2.793 1.915-4.228.9z" fill-opacity=".5">
						<animate attributeName="fill-opacity"
							 begin="0s" dur="1.4s"
							 values="0.5;1;0.5"
							 calcMode="linear"
							 repeatCount="indefinite" />
					</path>
					<path d="M105.512 56.12l-14.44-24.272c-3.716-6.008-1.996-14.093 3.843-18.062 5.835-3.97 13.588-2.322 17.306 3.68l2.6 5.274 4.52-3.76c5.84-3.97 13.592-2.32 17.307 3.68 3.718 6.003 1.998 14.088-3.842 18.064L109.74 57.02c-1.434 1.014-3.33.61-4.228-.9z" fill-opacity=".5">
						<animate attributeName="fill-opacity"
							 begin="0.7s" dur="1.4s"
							 values="0.5;1;0.5"
							 calcMode="linear"
							 repeatCount="indefinite" />
					</path>
					<path d="M67.408 57.834l-23.01-24.98c-5.864-6.15-5.864-16.108 0-22.248 5.86-6.14 15.37-6.14 21.234 0L70 16.168l4.368-5.562c5.863-6.14 15.375-6.14 21.235 0 5.863 6.14 5.863 16.098 0 22.247l-23.007 24.98c-1.43 1.556-3.757 1.556-5.188 0z" />';
				break;

			case 'oval':
				$result .=
					'<g fill="none" fill-rule="evenodd">
						<g transform="translate(1 1)" stroke-width="2">
							<circle stroke-opacity=".3" cx="18" cy="18" r="18"/>
							<path d="M36 18c0-9.94-8.06-18-18-18">
								<animateTransform
									attributeName="transform"
									type="rotate"
									from="0 18 18"
									to="360 18 18"
									dur="1s"
									repeatCount="indefinite"/>
							</path>
						</g>
					</g>';
				break;

			case 'puff':
				$result .=
					'<g fill="none" fill-rule="evenodd" stroke-width="2">
						<circle cx="22" cy="22" r="1">
							<animate attributeName="r"
								begin="0s" dur="1.8s"
								values="1; 20"
								calcMode="spline"
								keyTimes="0; 1"
								keySplines="0.165, 0.84, 0.44, 1"
								repeatCount="indefinite" />
							<animate attributeName="stroke-opacity"
								begin="0s" dur="1.8s"
								values="1; 0"
								calcMode="spline"
								keyTimes="0; 1"
								keySplines="0.3, 0.61, 0.355, 1"
								repeatCount="indefinite" />
						</circle>
						<circle cx="22" cy="22" r="1">
							<animate attributeName="r"
								begin="-0.9s" dur="1.8s"
								values="1; 20"
								calcMode="spline"
								keyTimes="0; 1"
								keySplines="0.165, 0.84, 0.44, 1"
								repeatCount="indefinite" />
							<animate attributeName="stroke-opacity"
								begin="-0.9s" dur="1.8s"
								values="1; 0"
								calcMode="spline"
								keyTimes="0; 1"
								keySplines="0.3, 0.61, 0.355, 1"
								repeatCount="indefinite" />
						</circle>
					</g>';
				break;

			case 'rings':
				$result .=
					'<g fill="none" fill-rule="evenodd" transform="translate(1 1)" stroke-width="2">
						<circle cx="22" cy="22" r="6" stroke-opacity="0">
							<animate attributeName="r"
								 begin="1.5s" dur="3s"
								 values="6;22"
								 calcMode="linear"
								 repeatCount="indefinite" />
							<animate attributeName="stroke-opacity"
								 begin="1.5s" dur="3s"
								 values="1;0" calcMode="linear"
								 repeatCount="indefinite" />
							<animate attributeName="stroke-width"
								 begin="1.5s" dur="3s"
								 values="2;0" calcMode="linear"
								 repeatCount="indefinite" />
						</circle>
						<circle cx="22" cy="22" r="6" stroke-opacity="0">
							<animate attributeName="r"
								 begin="3s" dur="3s"
								 values="6;22"
								 calcMode="linear"
								 repeatCount="indefinite" />
							<animate attributeName="stroke-opacity"
								 begin="3s" dur="3s"
								 values="1;0" calcMode="linear"
								 repeatCount="indefinite" />
							<animate attributeName="stroke-width"
								 begin="3s" dur="3s"
								 values="2;0" calcMode="linear"
								 repeatCount="indefinite" />
						</circle>
						<circle cx="22" cy="22" r="8">
							<animate attributeName="r"
								 begin="0s" dur="1.5s"
								 values="6;1;2;3;4;5;6"
								 calcMode="linear"
								 repeatCount="indefinite" />
						</circle>
					</g>';
				break;

			case 'spinning-circles':
				$result .=
					'<g fill="none" fill-rule="evenodd">
						<g transform="translate(2 1)" stroke="' . $stcol . '" stroke-width="1.5">
							<circle cx="42.601" cy="11.462" r="5" fill-opacity="1" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="1;0;0;0;0;0;0;0" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="49.063" cy="27.063" r="5" fill-opacity="0" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="0;1;0;0;0;0;0;0" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="42.601" cy="42.663" r="5" fill-opacity="0" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="0;0;1;0;0;0;0;0" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="27" cy="49.125" r="5" fill-opacity="0" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="0;0;0;1;0;0;0;0" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="11.399" cy="42.663" r="5" fill-opacity="0" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="0;0;0;0;1;0;0;0" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="4.938" cy="27.063" r="5" fill-opacity="0" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="0;0;0;0;0;1;0;0" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="11.399" cy="11.462" r="5" fill-opacity="0" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="0;0;0;0;0;0;1;0" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
							<circle cx="27" cy="5" r="5" fill-opacity="0" fill="' . $fgcol . '">
								<animate attributeName="fill-opacity"
									 begin="0s" dur="1.3s"
									 values="0;0;0;0;0;0;0;1" calcMode="linear"
									 repeatCount="indefinite" />
							</circle>
						</g>
					</g>';
				break;

			case 'tail-spin':
				$result .=
					'<defs>' .
						'<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">' .
							'<stop stop-color="#000" stop-opacity="0" offset="0%"/>' .
							'<stop stop-color="#000" stop-opacity=".631" offset="63.146%"/>' .
							'<stop stop-color="#000" offset="100%"/>' .
						'</linearGradient>' .
					'</defs>' .
					'<g fill="none" fill-rule="evenodd">' .
						'<g transform="translate(1 1)">' .
							'<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="' . $fgcol . '" stroke-width="2">' .
								'<animateTransform' .
									' attributeName="transform"' .
									' type="rotate"' .
									' from="0 18 18"' .
									' to="360 18 18"' .
									' dur="1.25s"' .
									' repeatCount="indefinite" />' .
							'</path>' .
							'<circle fill="' . $fgcol . '" cx="36" cy="18" r="1">' .
								'<animateTransform' .
									' attributeName="transform"' .
									' type="rotate"' .
									' from="0 18 18"' .
									' to="360 18 18"' .
									' dur="1.25s"' .
									' repeatCount="indefinite" />' .
							'</circle>' .
						'</g>' .
					'</g>';
				break;

			case 'three-dots':
				$result .=
					'<circle cx="15" cy="15" r="15">
						<animate attributeName="r" from="15" to="15"
								 begin="0s" dur="0.8s"
								 values="15;9;15" calcMode="linear"
								 repeatCount="indefinite" />
						<animate attributeName="fill-opacity" from="1" to="1"
								 begin="0s" dur="0.8s"
								 values="1;.5;1" calcMode="linear"
								 repeatCount="indefinite" />
					</circle>
					<circle cx="60" cy="15" r="9" fill-opacity="0.3">
						<animate attributeName="r" from="9" to="9"
								 begin="0s" dur="0.8s"
								 values="9;15;9" calcMode="linear"
								 repeatCount="indefinite" />
						<animate attributeName="fill-opacity" from="0.5" to="0.5"
								 begin="0s" dur="0.8s"
								 values=".5;1;.5" calcMode="linear"
								 repeatCount="indefinite" />
					</circle>
					<circle cx="105" cy="15" r="15">
						<animate attributeName="r" from="15" to="15"
								 begin="0s" dur="0.8s"
								 values="15;9;15" calcMode="linear"
								 repeatCount="indefinite" />
						<animate attributeName="fill-opacity" from="1" to="1"
								 begin="0s" dur="0.8s"
								 values="1;.5;1" calcMode="linear"
								 repeatCount="indefinite" />
					</circle>';
				break;

			default:
				$result .=
					'<rect x="0" y="0" width="100" height="100" fill="none" class="bk" >' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(0 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(22.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.09375s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(45 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.1875s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(67.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.28125s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(90 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.375s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(112.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.46875s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(135 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.5625s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(157.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.65625s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(180 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.75s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(202.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.84375s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(225 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0.9375s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(247.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.03125s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(270 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.125s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(292.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.21875s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(315 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.3125s" repeatCount="indefinite"/>' .
					'</rect>' .
					'<rect class="wppa-ajaxspin"  x="47" y="40" width="6" height="20" rx="3" ry="3" transform="rotate(337.5 50 50) translate(0 -32)">' .
						'<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="1.40625s" repeatCount="indefinite"/>' .
					'</rect>';
				break;

		}
		$result .= '</svg>';
	}

	// No .svg possible / requested, default to .gif
	else {
		$result =
			'<img' .
				' id="' . $args['id'] . '"' .
				' src="' . wppa_get_imgdir() . 'loader.gif"' .
				' class="' . $args['class'] . ' wppa-ajax-spin"' .
				' alt="spinner"' .
				' style="' .
					'width:' . $args['size'] . 'px;' .
					'height:' . $args['size'] . 'px;' .
					'position:' . $args['position'] . ';' .
					'top:' . $args['top'] . ';' .
					'margin-top:-' . $args['margin'] . 'px;' .
					'left:' . $args['left'] . ';' .
					'margin-left:-' . $args['margin'] . 'px;' .
					'z-index:200100;' .
					'opacity:1;' .
					'display:' . $args['display'] . ';' .
					'background-color:' . $bgcol . ';' .
					'box-shadow:none;' .
					'"' .
			' />';
	}

	if ( is_ssl() ) {
		$result = str_replace( 'http://', 'https://', $result );
	}
	return $result;
}

// Are we on a windows platform?
function wppa_is_windows() {

	// Windows uses \ instead of /, so if no / in ABSPATH, we are on a windows platform
	return strpos( ABSPATH, '/' ) === false;
}

// If it is a pdf, do postprocessing
function wppa_pdf_postprocess( $id ) {

	// If pdf...
	if ( wppa( 'is_pdf' ) ) {
		$filename = wppa_get_photo_item( $id, 'filename' );
		$filename = str_replace( '.jpg', '.pdf', $filename );
		wppa_update_photo( array( 'id' => $id, 'filename' => $filename ) );
	}

	// Reset switch
	wppa( 'is_pdf', false );
}

// Has the system 'many' albums?
function wppa_has_many_albums() {
global $wpdb;
static $n_albums;

	// Max specified? If not, return false
	if ( ! wppa_opt( 'photo_admin_max_albums' ) ) {
		return false;
	}

	// Find total number of albums, if not done before
	if ( ! $n_albums ) {
		$n_albums = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->wppa_albums" );
	}

	// Decide if many
	if ( $n_albums > wppa_opt( 'photo_admin_max_albums' ) ) {
		return true;
	}
	return false;
}

// Return false if user is logged in, upload roles are specified and i do not have one of them
function wppa_check_user_upload_role() {

	// Not logged in, ok
	if ( ! is_user_logged_in() ) {
		return true;
	}

	// No roles specified: ok
	if ( ! wppa_opt( 'user_opload_roles' ) ) {
		return true;
	}

	// Roles specified
	$roles = explode( ',', wppa_opt( 'user_opload_roles' ) );
	foreach ( $roles as $role ) {
		if ( current_user_can( $role ) ) {
			return true;
		}
	}

	// No matching role
	return false;
}

// Like wp_parse_args (args is array only), but it replaces NULL array elements also with the defaults.
function wppa_parse_args( $args, $defaults ) {

	// Remove NULL elements from $args
	$r = (array) $args;

	foreach( array_keys( $r ) as $key ) {

		// This looks funny, but:
		// a NULL element is regarded as being not set,
		// but it would not be overwritten by the default value in the merge
		if ( ! isset( $r[$key] ) ) {
			unset( $r[$key] );
		}
	}

	// Do the merge
	if ( is_array( $defaults ) ) {
		$r = array_merge( $defaults, $r );
	}

	return $r;
}

function wppa_is_divisible( $t, $n ) {

	if ( ! is_numeric( $t ) || ! is_numeric( $n ) ) {
		return false;
	}
	else {
		return ( round( $t / $n ) == ( $t / $n ) );
	}
}

function wppa_dump( $txt = '' ) {

	if ( ! wppa_switch( 'allow_debug' ) ) return;
	if ( ! is_writable( dirname( __FILE__ ) ) ) return;

	// Init
	$file = dirname( __FILE__ ) . '/wppa-dump.txt';

	$who = wppa_get_user( 'login' );
	$when = wppa_local_date( 'd.m.Y H:i:s', time());

	if ( $txt ) {
		if ( wppa_is_file( $file ) ) {
			$txt = wppa_get_contents( $file ) . "\n" . $who . ' ' . $when . ' ' . $txt;
		}
		wppa_put_contents( $file, $txt );
	}
	else {
		wppa_unlink( $file );
	}
}

function wppa_is_pdf( $id ) {
	return ( strtolower( wppa_get_ext( wppa_get_photo_item( $id, 'filename' ) ) ) == 'pdf' );
}

function wppa_get_pdf_html( $id ) {

	if ( wppa_is_mobile() || ! wppa_is_pdf( $id ) ) {
		$result = '';
	}
	else {
		$result = 'src="' . esc_attr( wppa_get_hires_url( $id ) ) . '"';
	}
	return $result;
}

// If wppa embedded lightbox, show wait cursor prior to lightbox init. when generic lightbox, show pointer cursor
function wppa_wait() {
	$result = 'wait';
	return $result;
}

// Get navigation symbol size
function wppa_icon_size( $default = '', $type = 0, $factor = 1 ) {

	switch ( $type ) {
		case 1:
			$opt = wppa_opt( 'nav_icon_size_slide' );
			break;
		case 2:
			$opt = wppa_opt( 'icon_size_rating' );
			break;
		default:
			$opt = wppa_opt( 'nav_icon_size' );
			break;
	}

	if ( $opt === 'default' ) {
		$units = strpos( $default, 'em' ) !== false ? 'em' : 'px';
		$opt = rtrim( $default, 'pxem;' );
	}
	else {
		$units = 'px';
	}

	$opt *= $factor;
	$result = ( wppa_in_widget() ? $opt / '2' : $opt ) . $units . ';';

	return $result;
}

// See if a photo is a panorama
function wppa_is_panorama( $id ) {

	$result = wppa_get_photo_item( $id, 'panorama' );
	if ( $result == '1' && ! wppa_switch( 'enable_panorama' ) ) {
		$result = '';
	}
	return $result;
}

// See if a remote file exists
function wppa_remote_file_exists( &$url, $save = true ) {

	$orig_url 	= $url;
	$ext 		= wppa_get_ext( $url );
	$url_1 		= wppa_strip_ext( $url );
	$exts 		= array( 'jpg', 'png', 'jpeg' );

	for ( $i=1; $i<4; $i++ ) { 				// Give it four tries
		foreach( $exts as $ext ) { 			// Try all possible extensions

			$url = $url_1 . '.' . $ext;
			$ch = curl_init( $url );
			$options = array(
				CURLOPT_NOBODY => true,
				CURLOPT_SSL_VERIFYPEER => true,
				);
			curl_setopt_array( $ch, $options );
			curl_exec( $ch );
			$httpCode 	= curl_getinfo( $ch, CURLINFO_HTTP_CODE );
			curl_close( $ch );
			wppa_log( 'dbg', 'curl_exec() returned {b}' . $httpCode . '{/b} for ' . $url );
			if ( $httpCode == 200 ) {
				$path = WPPA_DEPOT_PATH . '/' . basename( wppa_compress_tree_path( $url ) );
				if ( ! wppa_is_file( $path ) || ! wppa_filesize( $path ) ) {
					$data = file_get_contents( $url );
					if ( $save ) {
						file_put_contents( $path, $data );
						wppa_log( 'dbg', basename( $path ) . ' saved by wppa_remote_file_exists()' );
					}
				}
				return true;
			}
		}
		sleep( 1 );							// Wait a second, may help sometimes
	}
	$url = $orig_url;
	return false;
}

// Rename all files inside a tree to their sanitized name (recursive)
function wppa_rename_files_sanitized( $root ) {

	// Get the filesystem objects
	$my_import_files = wppa_glob( $root . '/*' );

	// If files
	if ( is_array( $my_import_files ) ) {

		foreach( $my_import_files as $path ) {

			// See if entryname is utf8 encoded
			$file = basename( $path );
			if ( ! seems_utf8( $file ) ) {
				$file = utf8_encode( $file );
			}

			// Remove really impossible chars
			$file = str_replace( '%', 'pct', $file );

			// Sanitize path, at least utf8 converted and extension downcased
			if ( wppa_switch( 'sanitize_import' ) ) {
				$new_path = dirname( $path ) . '/' . wppa_down_ext( sanitize_file_name( $file ) );
			}
			else {
				$new_path = dirname( $path ) . '/' . wppa_down_ext( $file );
			}

			// Process files
			if ( wppa_is_file( $path ) ) {

				if ( $new_path != $path ) {
					wppa_rename( $path, $new_path );
				}
			}

			// Process directories
			elseif ( wppa_is_dir( $path ) ) {

				if ( $new_path != $path ) {
					wppa_rename( $path, $new_path );
					wppa_log( 'fso', 'Sanitized import folder ' . $path . ' to ' . $new_path );
				}

				// Recursively one level deeper
				wppa_rename_files_sanitized( $path );
			}
		}
	}
}

function wppa_sanitize_album_photo_name( $xname ) {

	$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
	$name = str_replace( $special_chars, '', $xname );

	if ( wppa_switch( 'remove_accents' ) ) {
		$name = remove_accents( $name );
	}
	$name = sanitize_file_name( $name );

	return $name;
}

function wppa_nl2sp( $text ) {
	$result = str_replace( array( "\r\n","\n" ), ' ', $text );
	return $result;
}

// Thumbnail aspect (for real calendar)
function wppa_get_thumb_aspect() {

	$aspect = 1;
	if ( wppa_opt( 'thumb_aspect' ) != '0:0:none' ) {
		$t = explode( ':', wppa_opt( 'thumb_aspect' ) );
		$aspect = $t[0] / $t[1];
	}
	elseif ( wppa_opt( 'resize_to' ) != '-1' && wppa_opt( 'resize_to' ) != '0' ) {
		$t = explode( 'x', wppa_opt( 'resize_to' ) );
		$aspect = $t[1] / $t[0];
	}
	else {
		$aspect = wppa_opt( 'maxheight' ) / wppa_opt( 'fullsize' );
	}

	return $aspect;
}

// Wrapper around get_option, but checks the settings first
function wppa_get_option( $name, $default = null ) {
global $wppa_defaults;

	// If the option is a setting, use the default for the setting as the default
	if ( isset( $wppa_defaults[$name] ) ) {
		$default = $wppa_defaults[$name];
	}

	$result = get_option( $name, $default );

	// If an array is expected and its not an array, return an empty array
	if ( is_array( $default ) && ! is_array( $result ) ) {
		$result = array();
	}

	return $result;
}

// Compress html
function wppa_compress_html( $txt ) {

	$result = $txt;
	$result = str_replace( array( "\t", "\r\n", "\n", "  ", "   " ), " ", $result );
	$result = str_replace( array( "  ", "   " ), " ", $result );
	$result = str_replace( array( "  ", "   " ), " ", $result );
	$result = str_replace( "  ", " ", $result );
	$result = str_replace( '> <', '><', $result );
	$result = str_replace( '<ul></ul>', '', $result );
	$result = str_replace( '</script><script>', '', $result );
	$result = str_replace( '<!--', "\n<!--", $result );

	return $result;
}

// Find albums nesting level
function wppa_get_nesting_level( $id ) {
static $level;

	$level = _wppa_get_nesting_level( $id );

	return $level;
}
function _wppa_get_nesting_level( $id ) {

	if ( ! $id ) return '0';
	$alb = wppa_cache_album( $id );
	if ( is_array( $alb ) && $alb['a_parent'] > '0' ) {
		return _wppa_get_nesting_level( $alb['a_parent'] ) + '1';
	}
	else {
		return '0';
	}
}

// Can we do magick?
function wppa_can_magick() {
	return ( wppa_opt( 'image_magick' ) && wppa_opt( 'image_magick' ) != 'none' );
}

// Get my first granted album
function wppa_my_get_first_grant_album() {
global $wpdb;

	if ( ! wppa_switch( 'grant_an_album' ) ) return '0';
	$parents = wppa_opt( 'grant_parent' );
	if ( is_array( $parents ) ) {
		$parent = $parents[0];
	}
	else {
		$parent = $parents;
	}
	$album = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->wppa_albums WHERE owner = %s AND a_parent = %d LIMIT 1", wppa_get_user(), $parent ) );

	return $album;
}

// Construct current shortcode
function wppa_get_shortcode( $key, $atts ) {

	// Init
	$result = '';

	if ( $key && is_array( $atts ) ) {

		$result = '[' . $key;

		foreach ( array_keys( $atts ) as $key ) {
			if ( is_numeric( $key ) ) {
				$result .= ' ' . $atts[$key];
			}
			else {
				$result .= ' ' . $key . '="' . $atts[$key] . '"';
			}
		}

		$result .= ']';
	}

	return $result;
}

// Do we do lazy loading?
function wppa_lazy() {

	$setting = wppa_opt( 'lazy' );
	if ( $setting == 'all' ) {
		return true;
	}
	if ( $setting == 'none' ) {
		return false;
	}
	if ( wppa_is_mobile() ) {
		return ( $setting == 'mobile' );
	}
	else {
		return ( $setting == 'pc' );
	}
}

// Are we building a cache file?
function wppa_is_caching() {

	$temp 		= wppa_test_for_caching();
	$caching 	= $temp['caching'];

	return $caching;
}