HEX
Server: Apache
System: Linux WWW 6.1.0-40-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.153-1 (2025-09-20) x86_64
User: web11 (1011)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/ippmt.kauko.lt/wp-content/plugins/wp-photo-album-plus/wppa-mailing.php
<?php
/* wppa-mailing.php
* Package: wp-photo-album-plus
*
* Contains mailing functions
*
* Version 9.0.01.003
*
*/

/* The following mailing lists exist:
'newalbumnotify',
'feuploadnotify',
'commentnotify',
'commentprevious',
'moderatephoto',
'moderatecomment',
'photoapproved',
'commentapproved',
'subscribenotify',
*/

add_action( 'wppa_do_mailinglist_cron', 'wppa_do_mailinglist', 10, 6 );

// Call this function to schedule a mailinglist emission
function wppa_schedule_mailinglist( $type, $alb = 0, $pho = 0, $com = 0, $url = '', $start = 0, $delay = 120 ) {

	if ( ! wppa_switch( 'email_on' ) ) return;
	if ( $type == 'newalbumnotify' ) $delay = 120 * 60; // Give the user an hour to put something in the album

	$log_args = " Args: $alb, $pho, $com, $url, $start.";

	// If user is an admin and void_admin is active, do nothing
	if ( wppa_user_is_admin() && wppa_switch( 'void_admin_email' ) ) {
		if ( in_array( $type, array( 'newalbumnotify', 'feuploadnotify', 'commentnotify' ) ) ) {
			wppa_log( 'Eml', "Admin email $type skipped." );
			return;
		}
	}

	// If feuploadnotify, see if one is pending, if so, do nothing
	if ( $type == 'feuploadnotify' ) {

		if ( ! $alb ) {
			$alb = wppa_get_photo_item( $pho, 'album' );
		}
		$owner = wppa_get_photo_item( $pho, 'owner' );

		$pending = get_transient( 'last_feuploadnotify_scheduled-' . $owner . '-' . $alb );

		if ( ! $pending ) {

			// Save this one to signal next that this one is pending
			set_transient( 'last_feuploadnotify_scheduled-' . $owner . '-' . $alb, $pho, HOUR_IN_SECONDS );
			wppa_log( 'eml', 'Writing transient ' . 'last_feuploadnotify_scheduled-' . $owner . '-' . $alb . ' Value = ' . $pho );
		}
		else {
			$pho = $pending;
			$log_args = " Args: $alb, $pho, $com, $url, $start.";
		}
	}

	// wp_schedule_single_event( int $timestamp, string $hook, array $args = array(), bool $wp_error = false )
	// wp_next_scheduled( string $hook, array $args = array() )

	// Already scheduled?
	$tm = wp_next_scheduled( 'wppa_do_mailinglist_cron', array( $type, $alb, $pho, $com, $url, $start ) );
	if ( $tm ) {
		wppa_log( 'Eml', 'Mailinglist {b}' . $type . '{/b} already scheduled to run at ' . wppa_local_date( '', $tm ) . $log_args );
		return;
	}

	// No, schedule
	else {
		$tm = wp_schedule_single_event( time() + $delay, 'wppa_do_mailinglist_cron', array( $type, $alb, $pho, $com, $url, $start ) );
	}

	// Success?
	if ( $tm === true ) {
		wppa_log( 'Eml', 'Mailinglist {b}' . $type . '{/b} ' . ( $start ? 're-' : '' ) . 'scheduled for run in ' . $delay . ' seconds.' . $log_args );
	}
	else {
		wppa_log( 'Err', 'Mailinglist {b}' . $type . '{/b} could not be scheduled for run in ' . $delay . ' seconds.' . $log_args );
	}

}

// Send the mails for a mailinglist
function wppa_do_mailinglist( $type, $alb = 0, $pho = 0, $com = 0, $url = '', $start = 0 ) {
global $wpdb;

	$log_args = " Args: $alb, $pho, $com, $url, $start.";

	// Add new users to default mailing list subscriptions
	if ( wppa_opt( 'mailinglist_policy' ) == 'opt-out' ) {

		$from 	= wppa_get_option( 'wppa_mailinglist_highest_user_auto_subscribed', 0 );
		$to 	= wppa_get_var( "SELECT ID from $wpdb->users ORDER BY ID DESC LIMIT 1" );

		if ( $to > $from ) {

			wppa_log( 'Eml', 'Start adding users to mailinlists' );
			$i = $from + 1;
			$mailings = array( 	'newalbumnotify',
								'feuploadnotify',
								'commentnotify',
								'commentprevious',
								'moderatephoto',
								'moderatecomment',
								'photoapproved',
								'commentapproved',
								'subscribenotify',
								);

			while ( ! wppa_is_time_up() && $i <= $to ) {
				foreach( $mailings as $list ) {
					if ( $list == 'subscribenotify' && ! wppa_user_is( 'administrator' ) ) {
					}
					elseif ( substr( $list, 0, 8 ) == 'moderate' && ! user_can( $i, 'wppa_moderate' ) ) {
					}
					else {
						wppa_subscribe_user( $i, $list );
					}
				}
				$i++;
			}
			wppa_log( 'Eml', $to - $from . ' users added to mailinglists' );
			wppa_update_option( 'wppa_mailinglist_highest_user_auto_subscribed', $to );

			// Redo the mailing
			wppa_schedule_mailinglist( $type, $alb, $pho, $com, $url, $start, 15 );
			wppa_exit();
		}
	}

	// Mailinglist enabled?
	if ( ! wppa_switch( $type ) ) {
		wppa_log( 'Eml', 'Mailinglist {b}' . $type . '{/b} is disabled and will not run' );
		wppa_exit();
	}

	// Get mailinglist user ids
	$mailinglist 	= wppa_get_option( 'wppa_mailinglist_' . $type, '' );
	$userarray 		= wppa_index_string_to_array( $mailinglist );

	// Mailinglist empty?
	if ( empty( $userarray ) ) {
		wppa_log( 'Eml', 'Mailinglist {b}' . $type . '{/b} has no subscribers and will not run' );
		wppa_exit();
	}

	// There is a bug in wp cron
	// The cron lock sometimes fails, so there is a possbility that two processes execute the same cron job simultaneously
	// To prevent duplicate emails, we have our own 'lock' that can not run stuck because it needs no release

	// Check for 'lock'
	if ( wppa_is_cron() ) {

		// Before going on, we wait a small period dependant of our process id,
		// to make sure that if there are two simultaneous cron processes,
		// they will get out of sync
		$sleep = getmypid() % 17;
		wppa_log( 'Eml', "Waiting $sleep seconds" );
		sleep( $sleep );

		// Mailinglist just done or executoing?
		$lock_file 	= WPPA_LOCKDIR . '/' . $type;
		if ( wppa_is_file( $lock_file ) ) {
			$lock_value = wppa_get_contents( $lock_file );
		}
		else {
			$lock_value = '';
		}
		$our_lock 	= $alb . '-' . $pho . '-' . $com . '-' . $start;

		// Is last mailing equal to our mailing?
		if ( $lock_value == $our_lock ) {

			// Is this lock not older than 18 second?
			if ( wppa_filetime( $lock_file ) >= ( time() - 18 ) ) {
				wppa_log( 'Eml', '{span style="color:red;" }{b}CRON ERROR{/b}{/span} Duplicate cron process detected. Aborting {b}' . $type . '{/b}' . $log_args );
			}

			// It's older, so it is a regular re-run, but unneeded
			else {
				wppa_log( 'Eml', 'Duplicate mailing detected. Aborting {b}' . $type . '{/b}' . $log_args );
			}
			wppa_exit();
		}

		// Lock mailinglist
		wppa_put_contents( $lock_file, $our_lock, false );
	}

	// Log we are in
	wppa_log( 'Eml', 'Doing mailing {b}' . $type . '{/b} for ' . count( $userarray ) . ' recipients.' . $log_args );

	// Find itemtyupe: photo, video, audio or document; translated
	if ( $com ) {
		$p = wppa_get_var( $wpdb->prepare( "SELECT photo FROM $wpdb->wppa_comments WHERE id = %d", $com ) );
		$itemtype = wppa_get_type( $p, true );
		if ( ! $pho ) $pho = $p;
	}
	elseif ( $pho ) {
		$itemtype = wppa_get_type( $pho, true );
	}

	// Dispatch on type of mailinglist
	switch( $type ) {

		case 'subscribenotify':
			{
				$email_types = array(
					'newalbumnotify' 	=> __('New album', 'wp-photo-album-plus'),
					'feuploadnotify' 	=> __('Upload', 'wp-photo-album-plus'),
					'commentnotify' 	=> __('Comment', 'wp-photo-album-plus'),
					'commentprevious' 	=> __('Comment previous', 'wp-photo-album-plus'),
					'moderatephoto' 	=> __('Moderate photo', 'wp-photo-album-plus'),
					'moderatecomment' 	=> __('Moderate comment', 'wp-photo-album-plus'),
					'photoapproved' 	=> __('Photo approved', 'wp-photo-album-plus'),
					'commentapproved' 	=> __('Comment approved', 'wp-photo-album-plus'),
					'subscribenotify' 	=> __('Subscribe/unsubscribe', 'wp-photo-album-plus'),
				);
				$list_type 	= $alb;
				$user_id 	= $pho;
				$onoff 		= $com;
				$subj 		= $onoff == 'on' ? __( 'Email subscription', 'wp-photo-album-plus' ) : __( 'Email unsubscription', 'wp-photo-album-plus' );
				$user 		= get_user_by( 'ID', $user_id );
				$b 			= '<strong>';
				$_b 		= '</strong>';

				if ( $onoff == 'on' ) {
					/* translators: username, mailinglist type */
					$cont 	= sprintf( __( 'User %1$s has subscribed to mailinglist %2$s', 'wp-photo-album-plus' ), $b.$user->display_name.$_b, $b.$email_types[$list_type].$_b );
				}
				elseif ( $onoff == 'off' ) {
					/* translators: username, mailinglist type */
					$cont 	= sprintf( __( 'User %1$s has unsubscribed from mailinglist %2$s', 'wp-photo-album-plus' ), $b.$user->display_name.$_b, $b.$email_types[$list_type].$_b );
				}
				else {
					wppa_log( 'err', 'Unknown action to mailinglist subscription/unsubscription in wppa_do_mailinglist' );
					wppa_exit();
				}

				foreach( $userarray as $usr ) {

					// Get the user data
					$user = get_user_by( 'ID', $usr );

					// If user exists, mail
					if ( $user ) {
						wppa_send_mail( array( 'to' 			=> $user->user_email,
											   'subj' 			=> $subj,
											   'cont' 			=> $cont,
											   'photo' 			=> 0,
											   'listtype' 		=> $type,
											   'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
											));
					}
					else {
						wppa_unsubscribe_all( $usr );
					}
				}
			}
			break;

		case 'newalbumnotify':
			{
				// If album removed, quit
				$album = wppa_get_row( $wpdb->prepare( "SELECT * FROM $wpdb->wppa_albums WHERE id = %d", $alb ) );
				if ( ! $album ) {
					wppa_log( 'Eml', 'Mailing skipped: album ' . $alb . ' vanished' );
					wppa_exit();
				}

				// Get the album items we need
				$name = wppa_get_album_name( $alb );
				$desc = wppa_get_album_desc( $alb );

				// The blog
				$blog = get_bloginfo( 'name' );

				// The callback url if any
				$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

				// The content of the mail
				$content =
					/* translators: albumname, blogname */
					sprintf( __( 'A new album: %1$s has been created on %2$s', 'wp-photo-album-plus' ),
									'<b>' . $name . '</b>',
									'<b>' . $blog . '</b>' );

					if ( $desc ) {
						$content .=
							'<br><br>' . __( 'Description', 'wp-photo-album-plus' ) . ':<br><br>' .
							'<blockquote style="color:#000077; background-color: #dddddd; border:1px solid black; padding: 6px; border-radius: 4px">' .
							'<em>' . $desc . '</em><br>' .
							'</blockquote>';
					}

					// Preview
					if ( $link ) {
						$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&cover=1&occur=1' );
						$content .=
							'<br>' .
							/* translators: html a tag */
							sprintf( __( 'You can see the content %1$shere%2$s', 'wp-photo-album-plus' ), '<a href="' . $the_link . '" >', '</a>' ) .
							'<br>' . __( 'If there is no content yet: try again later. The creator of the album may need some time to add content', 'wp-photo-album-plus' );
					}


				// Process all subscribed users
				foreach( $userarray as $usr ) {

					if ( $usr > $start ) {

						// Get the user data
						$user = get_user_by( 'ID', $usr );

						// Send the mail
						if ( $user ) {
							wppa_send_mail( array( 	'to' 			=> $user->user_email,
													'subj' 			=> __( 'New album created', 'wp-photo-album-plus' ),
													'cont' 			=> $content,
													'listtype' 		=> 'newalbumnotify',
													'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
													) );
						}
						else {
							wppa_unsubscribe_all( $usr );
						}

						// If time up, reschedule at current user id to run in 15 sec
						if ( wppa_is_time_up() ) {
							wppa_schedule_mailinglist( $type, $alb, $pho, $com, $url, $usr, 15 );
							wppa_exit();
						}
					}
				}
			}
			break;

		case 'feuploadnotify':
			{
				// If moderation required, do the moderatephoto mailing
				if ( wppa_get_photo_item( $pho, 'status' ) == 'pending' ) {
					wppa_schedule_mailinglist( 'moderatephoto', $alb, $pho, $com, $url );
					wppa_exit();
				}

				// See if there are more directly uploaded by this user in this album
				$timestamp 	= wppa_get_photo_item( $pho, 'timestamp' );
				$owner 		= wppa_get_photo_item( $pho, 'owner' );
				$query 		= $wpdb->prepare( "SELECT id FROM $wpdb->wppa_photos WHERE timestamp >= %d AND owner = %s", $timestamp, $owner );
				$photos 	= wppa_get_col( $query );
				$multi 		= count( $photos ) > 1;

				// The subject
				if ( $multi ) {
					$count = count( $photos );
					/* translators: integer count */
					$subj = sprintf( __( '%d New items uploaded', 'wp-photo-album-plus' ), $count );
				}
				else {
					/* translators: media item type, item name */
					$subj = sprintf( __( 'New %1$s uploaded: %2$s' , 'wp-photo-album-plus' ), wppa_get_type( $pho, true ), wppa_get_photo_item( $pho, 'name' ) );
				}

				// The album
				if ( ! $alb ) {
					$alb = wppa_get_photo_item( $pho, 'album' );
				}

				// The callback url if any
				$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

				// The content
				if ( $multi ) {
					/* translators: username, count, albumname */
					$cont = sprintf( __( 'User %1$s uploaded %2$s items into album %3$s' , 'wp-photo-album-plus' ), $owner, count( $photos ), wppa_get_album_name( $alb ) );
				}
				else {
					/* translators: username, count, mediatype, albumname */
					$cont = sprintf( __( 'User %1$s uploaded %2$s %3$s into album %4$s' , 'wp-photo-album-plus' ), $owner, wppa_get_type( $pho, true ), $pho, wppa_get_album_name( $alb ) );
				}

				// Preview
				if ( $link ) {
					$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
					$cont .= '<br>' . wppa_get_preview_text( $multi ? $photos : $pho, $the_link );
				}

				// Process all subscribed users
				foreach( $userarray as $usr ) {

					if ( $usr > $start ) {

						// Get the user data
						$user = get_user_by( 'ID', $usr );

						// If user exists, mail
						if ( $user ) {
							wppa_send_mail( array( 'to' 			=> $user->user_email,
												   'subj' 			=> $subj,
												   'cont' 			=> $cont,
												   'photo' 			=> ( $multi ? $photos : $pho ),
												   'listtype' 		=> $type,
												   'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
												));
						}
						else {
							wppa_unsubscribe_all( $usr );
						}

						// If time up, reschedule at current user id to run in 15 sec
						if ( wppa_is_time_up() ) {
							wppa_schedule_mailinglist( $type, $alb, $pho, $com, $url, $usr, 15 );
							wppa_exit();
						}
					}
				}
				wppa_log( 'eml', 'deleting transient: ' . 'last_feuploadnotify_scheduled-' . $owner . '-' . $alb);
				delete_transient( 'last_feuploadnotify_scheduled-' . $owner . '-' . $alb );
			}
			break;

		case 'commentnotify':

				// Get the comment
				$comment = wppa_get_row( $wpdb->prepare( "SELECT * FROM $wpdb->wppa_comments WHERE id = %d", $com ) );

				// Get the photo id
//				if ( ! $pho ) {
//					$pho = $comment['photo'];
//				}

				// Get the photo owner
				$owner = wppa_get_photo_item( $pho, 'owner' );
				$owuser = get_user_by('login',$owner);

				// The author by email
				$author = get_user_by( 'email', $comment['email'] );

				if ( ! $author ) {

					// Try the author by login
					$author = get_user_by( 'login', $comment['email'] );
				}

				if ( $author ) {
					$aut = $author->display_name;
				}
				else {
					$aut = $comment['user'];
				}

				// Get the photos album
				if ( ! $alb ) {
					$alb = wppa_get_photo_item( $pho, 'album' );
				}

//				// If moderation required, do the moderatecomment mailing
//				if ( $comment['status']  == 'pending' ) {
//					wppa_do_mailinglist( 'moderatecomment', $alb, $pho, $com, $url );
//					return;
//				}

				// If limited receivers activated, reduce subscriptionlist
				if ( wppa_switch( 'commentnotify_limit' ) ) {

					// Admins
					$admins = wppa_get_admin_ids_a();
					wppa_log( 'Eml', 'admins ' . implode( ',', $admins ) );

					// Photo owner
					$powner = array( $owuser->ID );
					wppa_log( 'Eml', 'photo owner ' . $owuser->ID . ',  ' . $owuser->user_login );

					// Comment owner
					if ( $author ) {
						$cowner = $author ? array( $author->ID ) : array();
						wppa_log( 'Eml', 'comment owner ' . $author->ID . ',  ' . $author->user_login );
					}

					// Superusers
					$susers = wppa_get_superuser_ids_a();
					wppa_log( 'Eml', 'superusers ' . implode( ',', $susers ) );

					// All potential receipients
					// The next line fails on https://learn.iphotography.com/
				//	$all_potential = array_unique( array_merge( $admins, $powner, $cowner, $susers ), SORT_NUMERIC );
					// So we do it manually
					$all_potential = array();
					foreach( $admins as $a ) {
						$all_potential[] = $a;
					}
					if ( isset( $powner[0] ) ) $all_potential[] = $powner[0];
					if ( isset( $cowner[0] ) ) $all_potential[] = $cowner[0];
					foreach( $susers as $s ) {
						$all_potential[] = $s;
					}
					$all_potential = array_unique( $all_potential );

					$userarray = array_intersect( $userarray, $all_potential );
					wppa_log( 'Eml', 'Potential receipients: ' . implode( ',', $all_potential ) . ', all subscribed potential: ' . implode( ',', $userarray ) );
				}

				// Subject
				/* translators: media type, item name */
				$subj = sprintf( __( 'Comment on %1$s %2$s' , 'wp-photo-album-plus' ), wppa_get_type( $pho, true ), wppa_get_photo_name( $comment['photo'] ) );

				// The callback url if any
				$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

				// The content
				/* Translators: <username> wrote on <photo|video|audio> <itemid> in album <albumname> */
				$cont = sprintf( __( '%1$s wrote on %2$s %3$s in album %4$s', 'wp-photo-album-plus' ), $aut, wppa_get_type( $pho, true ), wppa_get_photo_name( $comment['photo'] ), wppa_get_album_name( $alb ) ) .

					'<blockquote style="color:#000077; background-color: #dddddd; border:1px solid black; padding: 6px; border-radius: 4px">
						<em> ' . stripslashes( nl2br( $comment['comment'] ) ) . '</em>
					</blockquote>';

				// Preview
				if ( $link ) {
					$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
					$cont .= '<br>' . wppa_get_preview_text( $pho, $the_link );
				}

				// Process all subscribed users
				foreach( $userarray as $usr ) {

					if ( $usr > $start ) {

						// Get the user data
						$user = get_user_by( 'ID', $usr );

						// If user exists
						if ( $user ) {
							wppa_send_mail( array( 'to' 			=> $user->user_email,
												   'subj' 			=> $subj,
												   'cont' 			=> $cont,
												   'photo' 			=> $pho,
												   'listtype' 		=> $type,
												   'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
												));
						}

						// User does not exist, remove him from all lists
						else {
							wppa_unsubscribe_all( $usr );
						}

						// If time up, reschedule at current user id to run in 15 sec
						if ( wppa_is_time_up() ) {
							wppa_schedule_mailinglist( $type, $alb, $pho, $com, $url, $usr, 15 );
							wppa_exit();
						}
					}
				}

				// Now do the 'commentprevious' mailing
				wppa_schedule_mailinglist( 'commentprevious', $alb, $pho, $com );

			break;

		case 'photoapproved':

				// To who?
				$user = get_user_by( 'login', wppa_get_photo_item( $pho, 'owner' ) );
				if ( ! $user ) {
					wppa_log( 'Eml', 'Mailing skipped: user ' . wppa_get_photo_item( $pho, 'owner' ) . ' vanished' );
					wppa_exit();
				}
				$usr = $user->ID;

				// The callback url if any
				$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

				// Did user subscribe?
				if ( wppa_is_user_in_mailinglist( $user->ID, 'photoapproved' ) ) {

					$to 	= $user->user_email;
					/* translators: media item type */
					$subj 	= sprintf( __( '%s approved', 'wp-photo-album-plus' ), ucfirst( wppa_get_type( $pho, true ) ) );
					/* Translators: Your recently uploaded <itemtype> <itemname> in album <albumname> has been approved */
					$cont 	= sprintf( 	__( 'Your recently uploaded %1$s %2$s in album %3$s has been approved', 'wp-photo-album-plus' ),
										wppa_get_type( $pho, true ),
										'<b>' . wppa_get_photo_item( $pho, 'name' ) . '</b>',
										'<b>' . wppa_get_album_name( wppa_get_photo_item( $pho, 'album' ) ) . '</b>' );

					// Preview
					if ( $link ) {
						$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
						$cont .= '<br>' . wppa_get_preview_text( $pho, $the_link );
					}

					if ( $user ) {
						wppa_send_mail( array( 	'to'			=> $to,
												'subj'			=> $subj,
												'cont'			=> $cont,
												'photo'			=> $pho,
												'listtype'		=> $type,
												'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
												));
					}
					else {
						wppa_unsubscribe_all( $usr );
					}
				}

				// Now the photo is approved, we can mail 'feuploadnotify'
				wppa_schedule_mailinglist( 'feuploadnotify', 0, $pho );

			break;

		// A comment on my photo is approved or my comment is approved
		case 'commentapproved':

				// Get the comment
				$comment = wppa_get_row( $wpdb->prepare( "SELECT * FROM $wpdb->wppa_comments WHERE id = %d", $com ) );

				// The photo
				if ( ! $pho ) {
					$pho = $comment['photo'];
				}

				// Find the owner of the photo
				$owner = wppa_get_photo_item( $pho, 'owner' );

				// The author
				$author = get_user_by( 'login', $comment['user'] );
				if ( $author ) {
					$aut = $author->display_name;
				}
				else {
					$aut = $comment['user'];
				}

				// The callback url if any
				$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

				// Send the owner of the photo the mail if he is in the mailinglist
				if ( wppa_is_user_in_mailinglist( $owner, $type ) ) {

					// Get the user data
					$user = get_user_by( 'login', $owner );
					$usr = $user->ID;

					// If user still exists...
					if ( $user ) {

						$cont =
						$aut . ' ' . __( 'wrote on photo' , 'wp-photo-album-plus' ) . ' ' . wppa_get_photo_name( $comment['photo'] ) . ':' .
						'<blockquote style="color:#000077; background-color: #dddddd; border:1px solid black; padding: 6px; border-radius: 4px">
							<em> ' . stripslashes( nl2br( $comment['comment'] ) ) . '</em>
						</blockquote>';

						// Preview
						if ( $link ) {
							$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
							$cont .= '<br>' . wppa_get_preview_text( $pho, $the_link );
						}

						/* Translators: Comment on <itemtype> <itemname> approved */
						$subj = sprintf( __( 'Comment on %1$s %2$s appoved' , 'wp-photo-album-plus' ), wppa_get_type( $pho, true ), wppa_get_photo_name( $comment['photo'] ) );

						wppa_send_mail( array( 	'to' 			=> $user->user_email,
												'subj' 			=> $subj,
												'cont' 			=> $cont,
												'photo' 		=> $comment['photo'],
												'replyurl' 		=> $url,
												'listtype' 		=> $type,
												'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
												));
					}
					else {
						wppa_unsubscribe_all( $usr );
					}
				}

				// Send the author of the comment the mail if he is in the mailinglist
				if ( $author && wppa_is_user_in_mailinglist( $author->ID, $type ) ) {

					$cont = __( 'You wrote on photo', 'wp-photo-album-plus' ) . ' ' . wppa_get_photo_name( $comment['photo'] ) . ':' .
					'<blockquote style="color:#000077; background-color: #dddddd; border:1px solid black; padding: 6px; border-radius: 4px">
						<em> ' . stripslashes( nl2br( $comment['comment'] ) ) . '</em>
					</blockquote>';

					// Preview
					if ( $link ) {
						$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
						$cont .= '<br>' . wppa_get_preview_text( $pho, $the_link );
					}

					/* Translators: Your comment on <itemtype> <itemname> approved */
					$subj = sprintf( __( 'Your comment on %1$s %2$s appoved' , 'wp-photo-album-plus' ), wppa_get_type( $pho, true ), wppa_get_photo_name( $comment['photo'] ) );

					wppa_send_mail( array( 	'to' 			=> $author->user_email,
											'subj' 			=> $subj,
											'cont' 			=> $cont,
											'photo' 		=> $comment['photo'],
											'replyurl' 		=> $url,
											'listtype' 		=> $type,
											'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
											));

				}

				// Now the comment is approved, we can mail 'commentnotify'
				wppa_schedule_mailinglist( 'commentnotify', 0, 0, $com );


			break;

		case 'commentprevious':

				// Get the comment
				$comment = wppa_get_row( $wpdb->prepare( "SELECT * FROM $wpdb->wppa_comments WHERE id = %d", $com ) );

				if ( $comment ) {

					// Get the photo
					if ( ! $pho ) {
						$pho = $comment['photo'];
					}

					// Get the author
					$author = get_user_by( 'login', $comment['user'] );
					if ( $author ) {
						$aut = $author->display_name;
					}
					else {
						$aut = $comment['user'];
					}

					// Get the users who commented on the photo
					$users = wppa_get_col( $wpdb->prepare( "SELECT DISTINCT user FROM $wpdb->wppa_comments WHERE photo = %d", $pho ) );

					// If the current author is in the list: remove him, we do not send mails to the owner of the comment
					$users = array_diff( $users, [$comment['user']] );

					// Any users left?
					if ( empty( $users ) ) {
						wppa_log( 'Eml', 'No items for commentprevious mailing' );
						wppa_exit();
					}

					// The callback url if any
					$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

					$cont =
					$aut . ' ' . __( 'wrote on photo' , 'wp-photo-album-plus' ) . ' ' . wppa_get_photo_name( $comment['photo'] ) . ':' .
					'<blockquote style="color:#000077; background-color: #dddddd; border:1px solid black; padding: 6px; border-radius: 4px">
						<em> ' . stripslashes( nl2br( $comment['comment'] ) ) . '</em>
					</blockquote>';

					// Preview
					if ( $link ) {
						$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
						$cont .= '<br>' . wppa_get_preview_text( $pho, $the_link );
					}

					/* Translators: Ccomment on <itemtype> <itemname> that you commented earlier */
					$subj = sprintf( __( 'Comment on %1$s %2$s that you commented earlier' , 'wp-photo-album-plus' ), wppa_get_type( $pho, true ), wppa_get_photo_name( $comment['photo'] ) );

					// Process users
					foreach( $users as $usr ) {

						$user = get_user_by( 'login', $usr );

						// User still exists?
						if ( $user ) {

							wppa_send_mail( array( 	'to' 			=> $user->user_email,
													/* translators: photoname */
													'subj' 			=> $subj,
													'cont' 			=> $cont,
													'photo' 		=> $comment['photo'],
													'replyurl' 		=> $url,
													'listtype' 		=> $type,
													'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
													));

						}
						else {
							wppa_unsubscribe_all( $usr );
						}
					}
				}

			break;

		case 'moderatephoto':

				// The subject
				/* translators: photoname */
				$subj = sprintf( __( 'New photo moderate request: %s' , 'wp-photo-album-plus' ), wppa_get_photo_item( $pho, 'name' ) );

				// The photo owner
				$owner = get_user_by( 'login', wppa_get_photo_item( $pho, 'owner' ) );
				if ( ! $owner ) {
					$owner = new WP_User;
					$owner->display_name = __( 'Anonymus', 'wp-photo-album-plus' );
					$owner->user_email = '';
				}

				// The album
				if ( ! $alb ) {
					$alb = wppa_get_photo_item( $pho, 'album' );
				}

				// The callback url if any
				$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

				// The content
				/* translators: username, photoname, album */
				$cont = sprintf( __( 'User %1$s uploaded photo %2$s into album %3$s' , 'wp-photo-album-plus' ),
						$owner->display_name . ( $owner->user_email ? ' (' . make_clickable( $owner->user_email ) . ') ' : '' ),
						$pho,
						wppa_get_album_name( $alb ) );

				$cont .= '<br>' . __( 'This photo needs moderation', 'wp-photo-album-plus' );

				// Preview
				if ( $link ) {
					$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
					$cont .= '<br>' . wppa_get_preview_text( $pho, $the_link );
				}

				// Moderate links
				$cont .=
				'<br><a href="' . get_admin_url() . 'admin.php?page=wppa_moderate_photos&photo=' . wppa_encrypt_photo( $pho ) . '" >' .
					__( 'Moderate photo admin' , 'wp-photo-album-plus' ) .
				'</a>';

				// Process all subscribed users
				foreach( $userarray as $usr ) {

					// Get the user data
					$user = get_user_by( 'ID', $usr );

					// If user exists, mail
					if ( $user ) {
						if ( user_can( $usr, 'wppa_moderate' ) ) {
							wppa_send_mail( array( 'to' 			=> $user->user_email,
												   'subj' 			=> $subj,
												   'cont' 			=> $cont,
												   'photo' 			=> $pho,
												   'email' 			=> $owner->user_email,
												   'listtype' 		=> $type,
												   'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
												));
						}
					}
					else {
						wppa_unsubscribe_all( $usr );
					}
				}

			break;

		case 'moderatecomment':

				// The comment
				$comment = wppa_get_row( $wpdb->prepare( "SELECT * FROM $wpdb->wppa_comments WHERE id = %d", $com ) );

				// The photo
				if ( ! $pho ) {
					$pho = $comment['photo'];
				}

				// The album
				if ( ! $alb ) {
					$alb = wppa_get_photo_item( $pho, 'album' );
				}

				// If the comment is already approved by a vote when comment needs vote is on, we're done
				if ( $comment['status'] == 'approved' ) {
					wppa_log( 'Eml', 'Comment approved by voting. Mailing {b}moderatecomment{/b} aborted' );
					wppa_exit();
				}

				// The subject
				/* translators: photoname */
				$subj = sprintf( __( 'New comment moderate request: %s' , 'wp-photo-album-plus' ), wppa_get_photo_item( $pho, 'name' ) );

				// Get teh author
				$author = get_user_by( 'email', $comment['email'] );
				if ( $author ) {
					$aut = $author->display_name;
				}
				else {
					$aut = $comment['user'];
				}

				// The callback url if any
				$link = wppa_get_option( 'wppa_mailinglist_callback_url', '' );

				$cont =
				$aut . ( strpos( $comment['email'], '@' ) ? ' (' . make_clickable( $comment['email'] ) . ') ' : ' ' ) . __( 'wrote on photo' , 'wp-photo-album-plus' ) . ' ' . wppa_get_photo_name( $pho ) . ':' .
				'<blockquote style="color:#000077; background-color: #dddddd; border:1px solid black; padding: 6px; border-radius: 4px">
					<em> ' . stripslashes( nl2br( $comment['comment'] ) ) . '</em>
				</blockquote>';

				// Preview
				if ( $link ) {
					$the_link = wppa_encrypt_url( $link . '?album=' . $alb . '&photo=' . $pho . '&occur=1' );
					$cont .= '<br>' . wppa_get_preview_text( $pho, $the_link );
				}

				// Moderate links
				$cont .=
				'<br><a href="' . get_admin_url() . 'admin.php?page=wppa_manage_comments&commentids=' . $com . '" >' .
					__( 'Moderate photo admin' , 'wp-photo-album-plus' ) .
				'</a>';

				// The commenters email, only if the user exists, i.e. we are sure the email is valid
				$email = ( $author ? $author->user_email : '' );

				// Process all subscribed users
				foreach( $userarray as $usr ) {

					// Get the user data
					$user = get_user_by( 'ID', $usr );

					// If user exists, mail
					if ( $user ) {
						if ( user_can( $usr, 'wppa_moderate' ) ) {
							wppa_send_mail( array( 'to' 			=> $user->user_email,
												   'subj' 			=> $subj,
												   'cont' 			=> $cont,
												   'photo' 			=> $pho,
												   'email' 			=> $email,
												   'listtype' 		=> $type,
												   'unsubscribe' 	=> wppa_unsubscribe_link( $usr, $type ),
												));
						}
					}
					else {
						wppa_unsubscribe_all( $usr );
					}
				}

			break;

		default:
			wppa_log( 'Err', 'Unimplemented mailinglist type found: {b}' . $type . '{/b}', true );
	}

	wppa_log( 'Eml', 'Done mailing {b}' . $type . '{/b}' . $log_args );
	wppa_exit();
}

// Is current user in mailinglist?
function wppa_am_i_in_mailinglist( $list ) {

	$my_user_id = wppa_get_user( 'id' );
	return wppa_is_user_in_mailinglist( $my_user_id, $list );
}

// Is a given user in a certain mailinglist?
// @1: user, id (numeric), or login name
// @2: list, slug indicating list type
function wppa_is_user_in_mailinglist( $usr, $list ) {

	if ( is_numeric( $usr ) ) {
		$user_id = $usr;
	}
	else {
		$user = get_user_by( 'login', $usr );
		if ( $user ) {
			$user_id = $user->ID;
		}
		else {
			$user_id = 0;
		}
	}
	$mailinglist = wppa_get_option( 'wppa_mailinglist_' . $list, '' );
	$userarray = wppa_index_string_to_array( $mailinglist );
	return ( in_array( $user_id, $userarray ) );
}

// Remove user fron mailinglist
function wppa_unsubscribe_user( $user_id, $list_type ) {

	if ( ! $user_id ) {
		return;
	}

	$mailinglist 	= wppa_get_option( 'wppa_mailinglist_' . $list_type, '' );
	$userarray 		= wppa_index_string_to_array( $mailinglist );
	if ( in_array( $user_id, $userarray ) ) {
		$userarray 		= array_diff( $userarray, array( $user_id ) );
		$mailinglist 	= wppa_index_array_to_string( $userarray );
		wppa_update_option( 'wppa_mailinglist_' . $list_type, $mailinglist );
	}
}

// Remove user from all lists
function wppa_unsubscribe_all( $user_id ) {

	$lists = array( 'newalbumnotify',
					'feuploadnotify',
					'commentnotify',
					'commentprevious',
					'moderatephoto',
					'moderatecomment',
					'photoapproved',
					'commentapproved',
					);

	foreach( $lists as $list_type ) {
		wppa_unsubscribe_user( $user_id, $list_type );
	}
}

// Add user to mailinglist
function wppa_subscribe_user( $user_id, $list_type ) {

	if ( ! $user_id ) {
		return;
	}

	$mailinglist 	= wppa_get_option( 'wppa_mailinglist_' . $list_type, '' );
	$userarray 		= wppa_index_string_to_array( $mailinglist );
	if ( ! in_array( $user_id, $userarray ) ) {
		$userarray[] = $user_id;
		sort( $userarray );
		$mailinglist 	= wppa_index_array_to_string( $userarray );
		wppa_update_option( 'wppa_mailinglist_' . $list_type, $mailinglist );
	}
}

// Get the unsubscribe link
function wppa_unsubscribe_link( $user_id, $listtype ) {

	$user = get_user_by( 'ID', $user_id );
	$crypt = crypt( $listtype . $user->ID . $user->login_name, $user->display_name );

	$method = wppa_opt( 'ajax_method' );

	switch ( $method ) {
		case 'redir':
			if ( is_admin() ) {
				$url = site_url() . '/wp-admin/admin-ajax.php?action=wppa';
			}
			else {
				$url = ( wppa_switch( 'ajax_home' ) ? home_url() : site_url() ) . '/wppaajax?action=wppa';
			}
			break;
		case 'admin':
		case 'none':
		case 'rest':
		default:
			$url = site_url() . '/wp-admin/admin-ajax.php?action=wppa';
			break;
	}

	$url .= '&wppa-action=mailinglist&list=' . $listtype . '&onoff=off&user=' . $user_id . '&crypt=' . $crypt;

	$link = '<a href="' . $url . '" >';
	/* translators: html a tag */
	$result = sprintf( __( 'You can %1$sunsubscribe%2$s here from this mailinglist', 'wp-photo-album-plus' ), $link, '</a>' );

	return $result;
}

// Send a mail
function wppa_send_mail( $args ) {

global $wppa_opt;

	$wppa_opt['wppa_lazy'] = 'none';

	// Enhance $args
	$defaults = array( 	'to' => '',
						'subj' => '',
						'cont' => '',
						'photo' => 0,
						'email' => '',
						'listtype' => '',
						'replyurl' => '',
						'unsubscribe' => '',
						);
	$args = wp_parse_args( $args, $defaults );
	extract( $args );

	// User id given?
	if ( is_numeric( $to ) ) {
		$user = get_user_by( 'id', $to );
		if ( $user ) {
			$to = $user->user_email;
			$id = $user->ID;
		}
		else {
			wppa_log( 'Err', 'Attempt to mail to a non existing user', true );
			return;
		}
	}
	else {
		$user = get_user_by( 'email', $to );
		if ( $user ) {
			$id = $user->ID;
		}
		else {
			$id = 'NN';
		}
	}

	$message_part_1 = '';
	$message_part_2 = '';
	$message_part_3 = '';

	$headers 	= array( 	'From: ' . wppa_opt( 'email_from_email' ),
							'Content-Type: text/html; charset=UTF-8'
						);

	$photos = array();
	if ( is_array( $photo ) ) {
		$photos = $photo;
	}
	else {
		$photos[] = $photo;
	}

	$message_part_1	.= '
<html>
	<head>
		<style>
			.emoji {
				height: 1em;
				max-height: 1em;
			}
		</style>
	</head>
	<body>
		<table>
			<tbody>
				<tr>
					<td>
						<h3>' . $subj . '</h3>
					</td>
				</tr>';
				if ( ! empty( $photos ) && wppa_switch( 'show_email_thumbs' ) ) {
					$message_part_1	.= '
					<tr>
						<td style="padding:4px;text-align:center">';
						$i = 0;
						if ( count( $photos ) ) {
							foreach( $photos as $p ) if ( $p ) {
								$message_part_1	.=
								wppa_html_tag( 'img', ['src' => wppa_get_thumb_url($p), 'alt' => wppa_get_imgalt($p, false, true), 'style' => "height:120px"] );
								$i++;
								if ( 0 == ( $i % 5 ) ) {
									$message_part_1	.= '</td></tr><tr><td style="padding:4px;text-align:center">';
								}
							}
						}
						$message_part_1	.= '
						</td>
					</tr>';
				}


				if ( is_array( $cont ) && wppa_switch( 'show_email_thumbs' ) ) {
					foreach ( $cont as $c ) if ( $c ) {
						$message_part_1 .= '<tr><td>'.convert_smilies( $c ).'</td></tr>';
					}
				}
				else {
					$message_part_1 .= '<tr><td>'.convert_smilies( $cont ).'</td></tr>';
				}

				// Tell the moderator the email address of the originator of the photo/comment
				if ( $email && substr( $listtype, 0, 8 ) == 'moderate' || $listtype == 'showemail' ) {

					/* translators: email */
					$eml = sprintf(__('The visitors email address is: <a href="mailto:%1$s">%2$s</a>', 'wp-photo-album-plus' ), $email, $email);
					$message_part_2 .= '<tr><td>'.$eml.'</td></tr>';
				}

				// Reply link
				if ( $replyurl ) {
					$message_part_2 .= '<tr><td><a href="' . $replyurl . '" >' . __( 'Reply' , 'wp-photo-album-plus' ) . '</a></td></tr>';
				}

				// Unsubscribe link
				if ( $unsubscribe ) {
					$message_part_2 .= '<tr><td>' . $unsubscribe . '</td></tr>';
				}

				// Generic message
				$message_part_3 .=
				'<tr><td>
					<small>' .
						/* translators: webaddress */
						sprintf(__('This message is automatically generated at %s. It is useless to respond to it.', 'wp-photo-album-plus' ), '<a href="'.home_url().'" >'.home_url().'</a>') .
					'</small>' .
					( defined( 'WP_DEBUG' ) ? ' <small>(' . $listtype . ')</small>' : '' ) . '
				</td></tr>
			</tbody>
		</table>' .
	'</body>' .
'</html>';

	$subject = '['.str_replace('&#039;', '', wppa_opt('email_from_site') ).'] '.$subj;
	$message = $message_part_1 . $message_part_2 . $message_part_3;

	// If this mail has already been sent, skip and report
	$hash = wppa_get_mail_hash( $to, $subject, $message, $headers );
	if ( get_transient( 'wppa_' . $hash ) ) {

		wppa_log( 'Eml', 'Hash: ' . $hash . ' Sending duplicate mail skipped to: ' . $to . ' (' . $id . ') subject: ' . $subject );
		return;
	}

	// Try to send it with extra headers and with html
	$iret = wp_mail( 	$to,
						$subject,
						$message,
						$headers
					);
	if ( $iret ) {
		wppa_log( 'Eml',
				  'Hash: ' . wppa_get_mail_hash( $to, $subject, $message, $headers ) . ', ' .
				  'Mail sent to: ' . $to . ' (' . $id . ') ' .
				  'subject: ' . $subject . ', ' .
				  'photo: ' . ( $photo ? ( is_array( $photo ) ? serialize( $photo ) : $photo ) : 'not supplied.' ) );

		// Remember this mail has been sent
		set_transient( 'wppa_' . $hash, getmypid(), WEEK_IN_SECONDS );
		return;
	}

	wppa_log( 'Err', 'Mail sending failed. Hash: ' . $hash . ', To=' . $to . ', subject=' . $subject . ', message=' . $message );

	// Failed
	if ( ! wppa_is_cron() ) {
		wppa_echo( __( 'Mail sending Failed', 'wp-photo-album-plus' ) );
	}

	// Registee failed mail
	wppa_process_failed_mail(	$to,
								$subject,
								$message,
								$headers,
								'' );
}

// Compute mail id
function wppa_get_mail_hash( $to = '', $subject = '', $message = '', $headers = '' ) {

	$mes = str_replace( array(	'newalbumnotify',
								'feuploadnotify',
								'commentnotify',
								'commentprevious',
								'moderatephoto',
								'moderatecomment',
								'photoapproved',
								'commentapproved',
							),
							'',
							$message );

	return md5( ( is_array( $to ) ? implode( '|', $to ) : $to ) . $subject . $mes );
}

// Save failed mail data to retry later
function wppa_process_failed_mail( $to = '', $subject = '', $message = '', $headers = '' ) {

	// Ignore mails that lack essential data
	if ( ! $to || ! $subject || ! $message ) {
		return;
	}

	// Compute mail id
	$id = wppa_get_mail_hash( $to, $subject, $message, $headers );

	// Get stack of failed mails
	$failed_mails = wppa_get_option( 'wppa_failed_mails', array() );

	// See if this mail appears in the failed mails list
	$found = false;
	foreach( array_keys( $failed_mails ) as $key ) {
		if ( $id == $key ) {
			$found = true;
		}
	}

	// Found? do nothing
	if ( $found ) {
		return;
	}

	// Not found, add it
	$failed_mails[$id] = array( 'to' 		=> $to,
								'subj' 		=> $subject,
								'message' 	=> $message,
								'headers' 	=> $headers,
								'retry' 	=> wppa_opt( 'retry_mails' ),
								);

	// Store list
	wppa_update_option( 'wppa_failed_mails', $failed_mails );

}

// Get the translated preview text, taking item type into account
function wppa_get_preview_text( $id, $link ) {

	if ( is_array( $id ) ) {
		/* translators: html a tag */
		return sprintf( __( 'You can see the items %1$shere%2$s', 'wp-photo-album-plus' ), '<a href="' . $link . '" >', '</a>' );
	}

	$type = wppa_get_type( $id );
	switch ( $type ) {
		case 'audio':
			/* translators: html a tag */
			return sprintf( __( 'You can hear the audio %1$shere%2$s', 'wp-photo-album-plus' ), '<a href="' . $link . '" >', '</a>' );
			break;
		case 'video':
			/* translators: html a tag */
			return sprintf( __( 'You can see the video %1$shere%2$s', 'wp-photo-album-plus' ), '<a href="' . $link . '" >', '</a>' );
			break;
		case 'document';
			/* translators: html a tag */
			return sprintf( __( 'You can see the document %1$shere%2$s', 'wp-photo-album-plus' ), '<a href="' . $link . '" >', '</a>' );
			break;
		default:
			/* translators: html a tag */
			return sprintf( __( 'You can see the photo %1$shere%2$s', 'wp-photo-album-plus' ), '<a href="' . $link . '" >', '</a>' );
			break;
	}
}