fulldecent/cameralife

View on GitHub
sources/Models/AuditTrail.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php
namespace CameraLife\Models;

/**
 * The class for logging and reverting changes to the site
 * @author William Entriken <cameralife@phor.net>
 * @copyright 2001-2014 William Entriken
 * @access public
 */
class AuditTrail extends IndexedModel
{
    public $record;

    /**
     * Logs information about a user and a change to the database, so this can be undone later
     *
     * @param  User   $user        The user making the change
     * @param  string $record_type one of ('photo','album','preference','user')
     * @param  int    $record_id   id of the record being changed
     * @param  string $value_field field being changed
     * @param  string $value_old   old field value
     * @param  string $value_new   new field value
     * @return Audit trail of the action performed
     */
    public static function createAuditTrailForChange(User $user, $record_type, $record_id, $value_field, $value_old, $value_new)
    {
        if ($value_old == $value_new) {
            return null;
        }
        $retval = new AuditTrail();
        $retval->record['record_type'] = $record_type;
        $retval->record['record_id'] = $record_id;
        $retval->record['value_field'] = $value_field;
        $retval->record['value_new'] = $value_new;
        $retval->record['user_name'] = $user->name;
        $retval->record['user_ip'] = $user->remoteAddr;
        $retval->record['user_date'] = date('Y-m-d');
        $retval->record['id'] = Database::insert('logs', $retval->record);
        $retval->id = $retval->record['id'];
        return $retval;
    }

    /**
     * getAuditTrailWithID function.
     *
     * @access public
     * @static
     * @param  mixed $id
     * @return void
     */
    public static function getAuditTrailWithID($modelId)
    {
        $retval = new AuditTrail;
        $query = Database::select('logs', '*', 'id=' . $modelId);
        $retval->record = $query->fetchAssoc();
        if (empty($retval->record)) {
            throw new \Exception('AT not found');
        }
        $retval->id = $retval->record['id'];
        if (!is_array($retval->record)) {
            throw new \Exception("Invalid receipt id #$modelId");
        }
        return $retval;
    }

    /**
     * Get previous value for this record field
     *
     * @access public
     * @return mixed
     */
    public function previousValue()
    {
        $condition = 'record_id=' . $this->record['record_id'];
        $condition .= " AND record_type='" . $this->record['record_type'] . "'";
        $condition .= " AND value_field='" . $this->record['value_field'] . "'";
        $condition .= " AND id < " . $this->record['id'];
        $result = Database::select('logs', '*', $condition, 'ORDER BY id DESC LIMIT 1');
        $prior = $result->fetchAssoc();
        if (is_array($prior) && isset($prior['value_new'])) {
            $old = $prior['value_new'];
        } else {
            $old = self::getDefaultForRecordTypeAndValueField($this->record['record_type'], $this->record['value_field']);
        }
        return $old;
    }

    /**
     * Revert Camera Life to the state before the specified action took effect
     *
     * @access public
     * @return void
     */
    public function revertChange()
    {
        $previousValue = $this->previousValue();
        $mod = array($this->record['value_field'] => $previousValue);
        Database::update($this->record['record_type'] . 's', $mod, 'id=' . $this->record['record_id']);
        $condition = 'record_id=' . $this->record['record_id'];
        $condition .= " AND record_type='" . $this->record['record_type'] . "'";
        $condition .= " AND value_field='" . $this->record['value_field'] . "'";
        $condition .= " AND id >= " . $this->record['id'];
        Database::delete('logs', $condition);
    }

    /**
     * isValid function.
     *
     * @access public
     * @return bool true if this receipt represents the most recent change to the affected record
     */
    public function isValid()
    {
        $condition = 'record_id=' . $this->record['record_id'];
        $condition .= " AND record_type='" . $this->record['record_type'] . "'";
        $condition .= " AND value_field='" . $this->record['value_field'] . "'";
        $result = Database::select('logs', '*', $condition, 'ORDER BY id DESC LIMIT 1');
        $newest = $result->fetchAssoc();
        return ($newest['id'] == $this->record['id']);
    }

    /**
     * Getter for elements in my record
     *
     * @access public
     * @param  mixed $item
     * @return mixed
     */
    public function get($item)
    {
        return $this->record[$item];
    }

    /**
     * English language description for the action performed
     *
     * @access public
     * @return void
     */
    public function getDescription()
    {
        if ($this->record['record_type'] == 'photo' && $this->record['value_field'] == 'description') {
            return 'The description has been updated.';
        }
        if ($this->record['record_type'] == 'photo' && $this->record['value_field'] == 'status') {
            return 'The photo has been flagged.';
        }
        return 'Action completed.';
    }

    /**
     * Factory for the associated object
     *
     * @access public
     * @return mixed
     */
    public function getObject()
    {
        if ($this->record['record_type'] == 'photo') {
            return Photo::getPhotoWithID($this->record['record_id']);
        }
        if ($this->record['record_type'] == 'album') {
            return new Album($this->record['record_id']);
        }
        if ($this->record['record_type'] == 'preference') {
            return $cameralife;
        }
        $cameralife->Error("Unknown receipt type: " . $this->record['record_type']);
        return false;
    }

    /**
     * Returns all receipts from this back to the beginning
     *
     * @access public
     * @param  float $checkpoint (default: -1)
     * @return void
     */
    public function getTrailsBackToCheckpoint($checkpoint = -1)
    {
        $retval = array();
        $condition = "value_field='" . $this->record['value_field'] . "'";
        $condition .= " AND record_type='" . $this->record['record_type'] . "'";
        $condition .= " AND record_id='" . $this->record['record_id'] . "'";
        $condition .= " AND id>$checkpoint";
        $query = Database::select('logs', 'id', $condition, 'ORDER BY id');
        while ($row = $query->fetchAssoc()) {
            $retval[] = AuditTrail::getAuditTrailWithID($row['id']);
        }
        return $retval;
    }

    /**
     * Gets the default value for a record type and field
     *
     * @access public
     * @static
     * @param  mixed $recordType
     * @param  mixed $valueField
     * @return mixed
     */
    private static function getDefaultForRecordTypeAndValueField($recordType, $valueField)
    {
        switch ($recordType . '_' . $valueField) {
            case 'photo_description':
                $oldvalue = 'unnamed';
                break;
            case 'photo_status':
                $oldvalue = '0';
                break;
            case 'photo_keywords':
                $oldvalue = '';
                break;
            case 'photo_flag':
                $oldvalue = '';
                break;
            case 'album_name':
                $oldvalue = '';
                break;
            case 'photo_poster_id':
                $album = new Album($record['record_id']);
                $condition = "status=0 and lower(description) like lower('%" . $album->Get['term'] . "%')";
                $query = Database::select('photos', 'id', $condition);
                $result = $query->fetchAssoc();
                if ($result) {
                    $oldvalue = $result['id'];
                } else {
                    throw new \Exception("Cannot find a poster for the album #" . $record['record_id']);
                }
                break;
            default:
                throw new \Exception("I don't know default value for -$recordType- -$valueField-");
        }
        return $oldvalue;
    }
}