diff --git a/include/class-Log.php b/include/class-Log.php index cb4cab4..8f3b0ad 100644 --- a/include/class-Log.php +++ b/include/class-Log.php @@ -1,281 +1,291 @@ . // make sure that this class is loaded at startup class_exists( User::class ); class_exists( Domain::class ); class_exists( Mailbox::class ); class_exists( Mailforwardfrom::class ); trait LogTrait { /** * Get the log actor name * * @return string */ public function getLogActorFirm() { return User::firm( $this->get( 'actor_uid' ) ); } /** * Get the action family * * @return string */ public function getLogFamily() { return $this->get( 'log_family' ); } /** * Get the action */ public function getLogAction() { return $this->get( 'log_action' ); } /** * Get the action */ public function getLogDate() { return $this->get( 'log_timestamp' ); } /** * Get the log message * * @param array $args Arguments * @return self */ public function getLogMessage( $args ) { $family = $this->getLogFamily(); $action = $this->getLogAction(); // trigger the right message family switch( $family ) { case 'domain': return self::domainMessage( $action, $this, $args ); case 'mailbox': return self::mailboxMessage( $action, $this, $args ); case 'mailforward': return self::mailforwardMessage( $action, $this, $args ); } return self::unknownAction( $family, $action ); } /** * Get the log message alongside the date and the actor name * * @param array $args Arguments * @return self */ public function getLogMessageWithDateAndUser( $args ) { $actor = $args['actor'] ?? $this; // create the Actor firm from the passed User object or from the Log $actor_firm = $actor instanceof User ? $actor->getUserFirm() : $actor->getLogActorFirm(); return sprintf( "%s - %s %s", $this->getLogDate()->format( __( "Y-m-d H:i" ) ), $actor_firm, $this->getLogMessage( $args ) ); } protected function normalizeLog() { $this->datetimes( 'log_timestamp' ); } } /** * A generic log of an action * * Something happened. Dunno what. */ class Log extends Queried { use LogTrait; use UserTrait; use DomainTrait; use MailboxTrait; use MailforwardfromTrait; public function __construct() { $this->normalizeLog(); } /** * Database table name */ const T = 'log'; /** * Generate a Domain-related message * * @param string $action The related action name * @param object $log * @param array $args Arguments * @return string Message */ public static function domainMessage( $action, $log, $args ) { /** * You can pass some objects to build the message: * * A complete 'actor' User object * A complete 'domain' Domain object */ $domain = $args['domain'] ?? $log; $plan = $args['plan'] ?? $log; switch( $action ) { // an administrator has changed the Plan for a Domain case 'plan.change': return sprintf( __( "changed the Plan for %s to %s" ), $domain->getDomainFirm(), esc_html( $plan->getPlanName() ) ); } // default dummy message return self::unknownAction( 'domain', $action ); } /** * Generate a Mailbox-related message * * @param string $action The related action name * @param object $log * @param array $args Arguments * @return string Message */ public static function mailboxMessage( $action, $log, $args ) { /** * You can pass some objects to build the message: * * A complete 'actor' User object * A complete 'domain' Domain object * A complete 'mailbox' Mailbox object */ $actor = $args['actor'] ?? $log; $domain = $args['domain'] ?? $log; $mailbox = $args['mailbox'] ?? $log; $mailbox_firm = Mailbox::firm( $domain->getDomainName(), $mailbox->getMailboxUsername() ); // trigger the right action message switch( $action ) { // the mailbox was created case 'create': return sprintf( __( "created the mailbox %s" ), $mailbox_firm ); // the description was changed case 'description.change': return sprintf( __( "edited description of %s" ), $mailbox_firm ); case 'newpassword': return sprintf( __( "reset password of %s" ), $mailbox_firm ); } // default dummy message return self::unknownAction( 'mailbox', $action ); } /** * Generate a Mailforward-related message * * @param string $action The related action name * @param object $log * @param array $args Arguments * @return string Message */ public static function mailforwardMessage( $action, $log, $args ) { /** * You can pass some objects to build the message: * * A complete 'domain' Domain object * A complete 'mailbox' Mailbox object */ $domain = $args['domain'] ?? $log; $mailforward = $args['mailforward'] ?? $log; - $firm = Mailforwardfrom::firm( - $domain->getDomainName(), - $mailforward->getMailforwardfromUsername() - ); + if( $mailforward ) { + $firm = $domain->getDomainFirm(); + } else { + $firm = Mailforwardfrom::firm( + $domain->getDomainName(), + $mailforward->getMailforwardfromUsername() + ); + } // trigger the right action message switch( $action ) { // a Mailforward destination was added case 'add.destination': return sprintf( __( "added a destination for %s" ), $firm ); // a Mailforward destination was removed case 'remove.destination': return sprintf( __( "removed a destination for %s" ), $firm ); // a Mailforward mailbox was created case 'create': return sprintf( __( "created %s" ), $firm ); + + case 'delete': + return sprintf( + __( "deleted an %s mail forwarding" ), + $firm + ); } // default dummy message return self::unknownAction( 'mailforward', $action ); } private static function unknownAction( $family, $action ) { return esc_html( sprintf( __( "misterious action about %s (%s)" ), $family, $action ) ); } } diff --git a/template/mailforwards.php b/template/mailforwards.php index 859d3ba..2318906 100644 --- a/template/mailforwards.php +++ b/template/mailforwards.php @@ -1,70 +1,71 @@ . /* * This is the template for the mailboxes list * * Called from: * template/domain.php * * Available variables: * $domain object */ // unuseful when load directly defined( 'BOZ_PHP' ) or die; // domain mail forwardings $mailforwardfroms = $domain->factoryMailforwardfrom() + ->joinDomain() ->select( [ 'mailforwardfrom.mailforwardfrom_ID', 'mailforwardfrom_username', 'domain_name', ] ) ->queryGenerator(); ?>

valid() ): ?>

getPlanName(), $plan->getPlanMailForwardings(), __( "Forwarding" ) ) ) ?>

getDomainName() ), __( "Create" ) ) ?>

diff --git a/www/mailforward.php b/www/mailforward.php index 64cd0c3..383899a 100644 --- a/www/mailforward.php +++ b/www/mailforward.php @@ -1,223 +1,239 @@ . /* * This is the single e-mail forwarding edit page */ // load framework require '../load.php'; // wanted informations $domain = null; $mailforwardfrom = null; // URL paramenters (maximum both domain and mailforward source, minimum just domain) list( $domain_name, $mailforwardfrom_username ) = url_parts( 2, 1 ); // eventually retrieve mailforward from database if( $mailforwardfrom_username ) { $mailforwardfrom = ( new MailforwardfromQuery() ) ->select( [ 'domain.domain_ID', 'domain_name', 'mailforwardfrom.mailforwardfrom_ID', 'mailforwardfrom_username', ] ) + ->joinDomain() ->whereDomainName( $domain_name ) ->whereMailforwardfromUsername( $mailforwardfrom_username ) ->whereDomainIsEditable() ->queryRow(); // 404 $mailforwardfrom or PageNotFound::spawn(); // recycle the mailforward object that has domain informations $domain = $mailforwardfrom; } // eventually retrieve domain from database if( ! $domain ) { $domain = ( new DomainAPI() ) ->select( [ 'domain.domain_ID', 'domain.domain_name', ] ) ->whereDomainName( $domain_name ) ->whereDomainIsEditable() ->queryRow(); // 404 $domain or PageNotFound::spawn(); } // save destination action if( is_action( 'mailforward-save' ) ) { // save source only during creation if( ! $mailforwardfrom ) { // sanitize if( ! isset( $_POST[ 'mailforwardfrom_username' ] ) ) { BadRequest::spawn( __( "missing parameter" ) ); } $username = luser_input( $_POST[ 'mailforwardfrom_username' ], 128 ); if( ! validate_mailbox_username( $username ) ) { BadRequest::spawn( __( "invalid mailbox name" ) ); } // check existence $mailforwardfrom_exists = ( new MailforwardfromQuery() ) ->select( 1 ) ->whereDomain( $domain ) ->whereMailforwardfromUsername( $username ) ->queryRow(); // die if exists if( $mailforwardfrom_exists ) { BadRequest::spawn( __( "e-mail forwarding already existing" ) ); } query( 'START TRANSACTION' ); // insert as new row insert_row( 'mailforwardfrom', [ new DBCol( 'domain_ID', $domain->getDomainID(), 'd' ), new DBCol( 'mailforwardfrom_username', $username, 's' ), ] ); // remember this action in the registry APILog::insert( [ 'family' => 'mailforward', 'action' => 'create', 'domain' => $domain, 'mailforwardfrom' => last_inserted_ID(), ] ); query( 'COMMIT' ); // POST/redirect/GET http_redirect( Mailforwardfrom::permalink( $domain->getDomainName(), $username, true ), 303 ); } } // delete action if( $mailforwardfrom ) { // action fired when deleting a whole mailforward if( is_action( 'mailforward-delete' ) ) { + query( 'START TRANSACTION' ); + + // mark a deletion on this Domain + APILog::insert( [ + 'family' => 'mailforward', + 'action' => 'delete', + 'domain' => $domain, + ] ); + + // drop the foreign key constraints + // See https://gitpull.it/T518 + ( new QueryLog() ) + ->whereMailforwardfrom( $mailforwardfrom ) + ->update( [ + 'mailforwardfrom_ID' => null, + ] ); + // drop the existing one - // TODO: refactor with Query builder - query( sprintf( - "DELETE FROM %s WHERE domain_ID = %d AND mailforwardfrom_username = '%s'", - T( 'mailforwardfrom' ), - $mailforwardfrom->getDomainID(), - $mailforwardfrom->getMailforwardfromUsername() - ) ); + ( new MailforwardfromQuery() ) + ->whereMailforwardfrom( $mailforwardfrom ) + ->delete(); + + query( 'COMMIT' ); // POST/redirect/GET http_redirect( $domain->getDomainPermalink( true ), 303 ); } // action fired when adding/removing a mailforward if( ( is_action( 'mailforwardto-add' ) || is_action( 'mailforwardto-remove' ) ) && isset( $_POST[ 'address' ] ) ) { $address = require_email( $_POST[ 'address' ] ); if( $address === $mailforwardfrom->getMailforwardfromAddress() ) { BadRequest::spawn( __( "do not try to create a loop" ) ); } $existing_address = ( new MailforwardtoAPI() ) ->whereMailforwardfrom( $mailforwardfrom ) ->whereMailforwardtoAddress( $address ) ->queryRow(); // action fired when removing a mailforward if( is_action( 'mailforwardto-remove' ) && $existing_address ) { query( 'START TRANSACTION' ); // TODO refactor with query builder query( sprintf( "DELETE FROM %s WHERE mailforwardfrom_ID = %d and mailforwardto_address = '%s'", T( 'mailforwardto' ), $mailforwardfrom->getMailforwardfromID(), esc_sql( $address ) ) ); // remember that we removed an address APILog::insert( [ 'family' => 'mailforward', 'action' => 'remove.destination', 'domain' => $domain, 'mailforwardfrom' => $mailforwardfrom, ] ); query( 'COMMIT' ); } // action fired when adding a mailforward if( is_action( 'mailforwardto-add' ) && ! $existing_address ) { query( 'START TRANSACTION' ); insert_row( 'mailforwardto', [ new DBCol( 'mailforwardfrom_ID', $mailforwardfrom->getMailforwardfromID(), 'd' ), new DBCol( 'mailforwardto_address', $address, 's' ), ] ); // remember that we added an address APILog::insert( [ 'family' => 'mailforward', 'action' => 'add.destination', 'domain' => $domain, 'mailforwardfrom' => $mailforwardfrom, ] ); query( 'COMMIT' ); } } } // spawn header Header::spawn( [ 'uid' => false, 'title-prefix' => __( "E-mail forwarding" ), 'title' => $mailforwardfrom ? $mailforwardfrom->getMailforwardfromAddress() : __( "create" ), 'breadcrumb' => [ new MenuEntry( null, $domain->getDomainPermalink(), $domain->getDomainName() ), ], ] ); // spawn the page content template( 'mailforward', [ 'domain' => $domain, 'mailforwardfrom' => $mailforwardfrom, ] ); // spawn the footer Footer::spawn();