cornernote / yii-audit-module

Track and display usage information including page requests, database field changes, php errors and yii logs.
https://cornernote.github.io/yii-audit-module
Other
22 stars 13 forks source link

Configuration for Fields #19

Closed Talwoasc closed 10 years ago

Talwoasc commented 10 years ago

Hi cornernote,

I've installed your module using the manual way and noticed that some of the Yiistrap helpers and widgets were not being imported as I am using Yii Bootstrap even when specifying the path to yiistrap in the auditModule config (its an old project which we are adding new features to) so I added the following lines to AuditModule.php Ln 282

//Yii::import('bootstrap.helpers.TbHtml');
Yii::import('bootstrap.helpers.*');
Yii::import('bootstrap.widgets.*');
Yii::import('bootstrap.behaviors.*');
Yii::import('bootstrap.form.*');

However my problem comes from the fact that no records ever appear in the Fields section when their are changed. I have added the code into the behaviour which all my models extend from but was unsure what the additionalAuditModules array does and if that had something to do with it?

Thanks very much in advance.

cornernote commented 10 years ago

Hi @Talwoasc,

Since the time of writing this module I believe YiiStrap removed these import lines from its own module and relies more heavily on the auto-loader in composer. I have applied your suggestion to the module code, and also done the same in my other modules that use YiiStrap.

additionalAuditModules allows you to log multiple lines into the audit_field table. For example if you have a table called post_user you may put Post and User into additionalAuditModules so that records are added for model_name='Post', model_id='123' and model_name='User', model_id='456' in addition to the normal logging. I don't think this is the cause of your problem.

Things to check:

If it still does not work, please paste your config/main.php and the model code into this issue.

Talwoasc commented 10 years ago

Hi @cornernote, Thanks for explaining that to me!

I've double checked both of them and they are set to true.

Here is the main.php

<?php

// uncomment the following to define a path alias
// Yii::setPathOfAlias('local','path/to/local-folder');

/**
 * Path to bootstrap files which provide nicer views
 * This can be updated to newer versions with the following notes:
 * NEW FILE extensions/bootstrap/widgets/TbKeypairTypeahead.php
 * EDITS extensions/bootstrap/widgets/input/
 *      TbInputVertical.php Ln 27
 *      Tbinputhorizontal.php Ln 52
 */
Yii::setPathOfAlias('bootstrap', dirname(__FILE__).'/../extensions/bootstrap');

/**
 * Path to audit modules
 * This can be updated to newer versions with the following notes:
 * EDITS modules/audit/
 *      AuditModule.php Ln 282-286
 */
Yii::setPathOfAlias('audit', dirname(__FILE__).'/../modules/audit');

// This is the main Web application configuration. Any writable
// CWebApplication properties can be configured here.
return array(
    'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
    'name'=>'Cryogenic Gases System',
    'sourceLanguage'=>'en_gb',

    //Use Bootstrap Theme
    'theme'=>'bootstrap',

    // preloading log and errorHandler component
    'preload' => array(
        'log',
        'errorHandler', // handle fatal errors in AuditModule
    ),

    // autoloading model and component classes
    'import'=>array(
        'application.models.*',
        'application.components.*',
        'ext.yii-mail.*',
    ),

    'modules'=>array(
        // Config for the AuditModule
        'audit' => array(
            // path to the AuditModule class
            'class' => 'audit.AuditModule',

            // set this to your user view url,
            // AuditModule will replace --user_id-- with the actual user_id
            'userViewUrl' => array('/user/view', 'id' => '--user_id--'),

            // Set to false if you do not wish to track database audits.
            'enableAuditField' => true,

            // The ID of the CDbConnection application component. If not set, a SQLite3
            // database will be automatically created in protected/runtime/audit-AuditVersion.db.
            'connectionID' => 'db',

            // Whether the DB tables should be created automatically if they do not exist. Defaults to true.
            // If you already have the table created, it is recommended you set this property to be false to improve performance.
            'autoCreateTables' => true,

            // The layout used for module controllers.
            'layout' => 'audit.views.layouts.column1',

            // The widget used to render grid views.
            'gridViewWidget' => 'bootstrap.widgets.TbGridView',

            // The widget used to render detail views.
            'detailViewWidget' => 'zii.widgets.CDetailView',

            // Defines the access filters for the module.
            // The default is AuditAccessFilter which will allow any user listed in AuditModule::adminUsers to have access.
            'controllerFilters' => array(
                //'auditAccess' => array('audit.components.AuditAccessFilter'),
                'auditAccess' => array('application.components.AuditAccessFilter'), // Custom access filter
            ),

            // A list of users who can access this module.
            //'adminUsers' => array('admin'),

            // The path to YiiStrap.
            // Only required if you do not want YiiStrap in your app config, for example, if you are running YiiBooster.
            // Only required if you did not install using composer.
            // Please note:
            // - You must download YiiStrap even if you are using YiiBooster in your app.
            // - When using this setting YiiStrap will only loaded in the audi module
            'yiiStrapPath' => dirname(__FILE__).'/../extensions/yiistrap',
        ),
    ),

    // application components
    'components'=>array(
        'user'=>array(
            // enable cookie-based authentication
            'allowAutoLogin'=>true,
            // use custom class components/WebUser.php which allows simple role based access control
            'class' => 'WebUser',
        ),

        // uncomment the following to enable URLs in path-format
        'urlManager'=>array(
            'urlFormat'=>'path',
            'showScriptName'=>false,
            'rules'=>array(
                '<controller:\w+>/<id:\d+>'=>'<controller>/view',
                '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
                '<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
            ),
        ),

        // MySQL database connection properties
        'db'=>array(
            'connectionString' => 'mysql:host=localhost;dbname=dbs',
            //'emulatePrepare' => true,
            'username' => '',
            'password' => '',
            'charset' => 'utf8',
            'tablePrefix' => 'tbl_',
        ),
        // Changed error handler to accommodate AuditModule
        'errorHandler' => array(
            // path to the AuditErrorHandler class
            'class' => 'audit.components.AuditErrorHandler',

            // set this as you normally would for CErrorHandler
            'errorAction' => 'site/error',

            // Set to false to only track error requests.  Defaults to true.
            'trackAllRequests' => true,

            // Set to false to not handle fatal errors.  Defaults to true.
            'catchFatalErrors' => true,

            // Request keys that we do not want to save in the tracking data.
            'auditRequestIgnoreKeys' => array('PHP_AUTH_PW', 'password'),

        ),
        'log'=>array(
            'class'=>'CLogRouter',
            'routes'=>array(
                array(
                    'class'=>'CFileLogRoute',
                    'levels'=>'error, warning',
                ),
                // uncomment the following to show log messages on web pages
                array(
                    'class'=>'CWebLogRoute',
                ),
                // logRoute for AuditModule
                array(
                    // path to the AuditLogRoute class
                    'class' => 'audit.components.AuditLogRoute',

                    // can be: trace, warning, error, info, profile
                    // can also be anything else you want to pass as a level to `Yii::log()`
                    'levels' => 'error, warning, profile, audit',
                ),
            ),
        ),

        // Bootstrap for theming and UI
        'bootstrap'=>array(
            'class'=>'bootstrap.components.Bootstrap',
        ),
        // Yii-Mail wrapper for Swiftmailer
        'mail' => array(
            'class' => 'ext.yii-mail.YiiMail',
            'transportType' => 'php',
            'viewPath' => 'application.views.mail',
            'logging' => true,
            'dryRun' => false
        ),
    ),

    // application-level parameters that can be accessed
    // using Yii::app()->params['paramName']
    'params'=>array(
        'useDefaultScope'=>true,
    ),
);

And ActiveRecord.php which everything else extends from

<?php

/**
 * ActiveRedord is the customized base activerecord class.
 * All activerecord classes for this application should extend from this base class.
 */
class ActiveRecord extends CActiveRecord
{
    /**
     * Returns the static model of the specified AR class.
     * @param string $className active record class name.
     * @return Billing the static model class
     */
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    /**
     * Default behaviours function to remember filters on search pages
     */
    public function behaviors() {
       return array(
            // Behaviour to remember fileds in CGridViews
           'ERememberFiltersBehavior' => array(
               'class' => 'application.components.ERememberFiltersBehavior',
               'defaults'=>array(),           /* optional line */
               'defaultStickOnClear'=>false   /* optional line */
           ),
           // Behaviour to enable Audit logging
           'AuditFieldBehavior' => array(
                // Path to AuditFieldBehavior class.
                'class' => 'audit.components.AuditFieldBehavior',

                // Set to false if you just want to use getDbAttribute and other methods in this class.
                // If left unset the value will come from AuditModule::enableAuditField
                'enableAuditField' => true,

                // Any additional models you want to use to write model and model_id audits to.  If this array is not empty then
                // each field modifed will result in an AuditField being created for each additionalAuditModels.
                'additionalAuditModels' => array(
                    //'Post' => 'post_id',
                ),

                // A list of values that will be treated as if they were null.
                'ignoreValues' => array('0', '0.0', '0.00', '0.000', '0.0000', '0.00000', '0.000000', '0000-00-00', '0000-00-00 00:00:00'),
            ),
       );
    }

    /**
     * Returns the default named scope that should be implicitly applied to all queries for this model.
     * Note, default scope only applies to SELECT queries. It is ignored for INSERT, UPDATE and DELETE queries.
     * The default implementation simply returns an empty array. You may override this method
     * if the model needs to be queried with some default criteria (e.g. only active records should be returned).
     * @return array the query criteria. This will be used as the parameter to the constructor
     * of {@link CDbCriteria}.
     */
    public function defaultScope()
    {
        // We use the defaultScope to only collect records which are active
        // This functionality can be turned off by setting the application paramater 'useDefaultScope'
        // to false which is useful to query soft deleted records
        if(Yii::app()->params['useDefaultScope'])
        {
            return array(
                'condition'=>$this->getTableAlias(false, false).'.`is_active`=1',
            );
        }
        else
        {
            return array();
        }
    }

    /**
     * Create a CDbCriteria with between condition which can be merged into a different CDbCriteria
     * @param string $att the column to add the condition to
     * @param mixed $valueStart the default start
     * @param mixed $valueEnd the default end
     */
    public function addRangeCriteria($att, $valueStart, $valueEnd)
    {
        $criteria = new CDbCriteria;
        // If the value searching on is an array add the between condition
        if(isset($this->$att) && is_array($this->$att))
        {
            // Fix magic overloading bug to allow refering to the array elements
            $magicAtt = $this->$att;
            // If either one of them is not empty
            if(!empty($magicAtt[0]) || !empty($magicAtt[1]))
            {
                $valueStart = !empty($magicAtt[0]) ? $magicAtt[0] : $valueStart;
                $valueEnd = !empty($magicAtt[1]) ? $magicAtt[1] : $valueEnd;
                // Add the condition
                $criteria->addBetweenCondition($att, $valueStart, $valueEnd);
            }
            else
            {
                // Get rid of the array if both empty
                unset($this->$att);
            }
        }
        // Otherwise just add a normal compare
        else
        {
            $criteria->compare($att,$this->$att);
        }
        return $criteria;
    }

    /**
     * Wrapper function for addRangeCondition to preset the valueStart and valueEnd for dates
     * @param string $att the column to add the condition to
     */
    public function addDateRangeCriteria($att)
    {
        // Set date defaults
        $valueStart = '1000-01-01';
        $valueEnd = '9999-12-31';
        // Return function
        return $this->addRangeCriteria($att, $valueStart, $valueEnd);
    }
}

And I checked to make sure my soft delete detection doesn't interfere with your module for some reason and it doesn't, query executes as expected

Querying SQL: SELECT * FROM `audit_field` `t` ORDER BY id DESC LIMIT 10
cornernote commented 10 years ago

Everything looks fine.

The next thing I can suggest is to get down-and-dirty, and put some print_r('foobar'); die; into the AuditFieldBehavior.php to see where it's not working.

The only other thing I can think is that perhaps you have an afterSave() method which does not call parent::afterSave().

Talwoasc commented 10 years ago

@cornernote,

Found the problem (sorry I've only just had time to have a dig), in some of the methods you missed out the leading underscore for property names $this->_enableAuditField. I've sent a pull request with the changes on lines 116 and 203. Odd that it seems to work fine for everyone else though...

Regards, Tal

cornernote commented 10 years ago

Awesome. I completely missed that when I refactored to not need a getEnableAuditField() method.

Thank you.