
/**
 *  Class that that edits the user properties from the
 *  perspective of the user itself.  Which is not necessarily
 *  all the properties of a user but only those in which the
 *  user has rights to edit.
 *  
 *  @superclass SmartClient DynamicForm
 *  
 *  Constructor Properties
 *  @parentEditor   Parent Editor that contains the Save and
 *                  Cancel Buttons.  In particulare the parent
 *                  editor must adhere to haveing the functions:
 *                  enableSave, disabeSave
 */
isc.defineClass("OPINIATOR_SelfForm", isc.DynamicForm);
OPINIATOR_SelfForm.addProperties({
    // Constructor Properties 
    parentEditor: null,

    // Show busy functions allow this canvas have a busy wait indicator
    showBusy:OPINIATOR_ShowBusy,
    destroyBusy:OPINIATOR_DestroyBusy,

    // Properties for the Dynamic Form
    width:"100%",
    height:"100%",
    minColWidth:10,
    cellPadding:2,
    numCols:2,
    fixedColWidths:true,
    titleWidths:80,
    cellBorder:0,
    showResizeBar:false,
    layoutAlign:"left",
    itemHoverDelay:3000,

    // Editing items in the Form
    items:[
        // Give us a blank row 5 pixels high
        { height:5, editorType:"RowSpacerItem" },

        //  The First Name of the user.  Uses OPINIATOR_TextItem as the editor
        {
        name:"firstname",
        title:"First Name",
        type:"text",
        tabIndex:1,
        width:250,
        height:25,
        editorType:"OPINIATOR_TextItem"
        },

        // Give us a blank row 5 pixels high
        { height:5, startRow:true, editorType:"rowSpacer" },

        //  The Last Name of the user.  Uses OPINATOR_TextItem as the editor
        {
        name:"lastname",
        title:"Last Name",
        type:"text",
        tabIndex:2,
        startRow:true,
        width:250,
        height:25,
        editorType:"OPINIATOR_TextItem"
        },

        // Give us a blank row 5 pixels high.  
        { height:5, type:"rowSpacer" },

        //  The Email Address of the user. Uses OPINATOR_TextItem as the editor
        {
        name:"email",
        title:"Email Address",
        type:"text",
        tabIndex:3,
        startRow:true,
        width:250,
        height:25,
        editorType:"OPINIATOR_TextItem"
        },
        { height:5, type:"rowSpacer" },

        //  Change Password fields.  Password fields are grouped by a section type
        {
        type:"section",
        defaultValue:"Change Password",   // Title of the sction
        sectionExpanded:true,  // Always expanded
        canCollapse:false,  // Can never collapse it
        // Item ids of items that this section will group
        itemIds:["oldpassword", "sp1", "newpassword", "sp2", "verifypassword"]
        },

        //  The old password of the user. 
        {
        name:"oldpassword",
        title:"Old",
        titleOrientation:"left",
        tabIndex:4,
        startRow:true,
        type:"text",
        width:150,
        height:25,
        editorType:"PasswordItem"
        },

        //  The new password of the user. 
        { name:"sp1", height:5, startRow:true, editorType:"rowSpacer" },
        {
        name:"newpassword",
        title:"New",
        titleOrientation:"left",
        tabIndex:5,
        startRow:true,
        type:"text",
        width:150,
        height:25,
        editorType:"PasswordItem"
        },

        //  Verifying the new password of the user. 
        { name:"sp2", height:5, startRow:true, editorType:"rowSpacer" },
        {
        name:"verifypassword",
        title:"Verify",
        titleOrientation:"left",
        tabIndex:6,
        startRow:true,
        type:"text",
        width:150,
        height:25,
        editorType:"PasswordItem"
        }
        ],

    /**
     *  Initialize the widget. Called after construction but before
     *  draw.
     */
    initWidget: function ()
        { this.Super("initWidget", arguments); },

    /**
     *  Called upon destruction of the object
     */
    destroy: function ()
        {
        // Help the garbage collector by deleting the reference to the parent
        delete this.parentEditor;
        // Destroy the busy spinnter
        this.destroyBusy();
        this.Super("destroy", arguments);
        },

    /**
     *  Called by the parent editor to set the Values of of the
     *  object to the forms fields.  Another way to do this is to
     *  use SmartClient's DataBinding model.  However, for
     *  a number of reasons we are not using the DataBinding Model.
     *  
     *  @param user     The object we are editing
     */
    editValues: function ( user )
        {
        this.setValue("firstname", user.firstName);
        this.setValue("lastname", user.lastName);
        this.setValue("email", user.emailAddress);
        this.focusInItem("firstname");
        },

    /**
     * Verify the forms values are acurate.  Called prior to saving 
     * values changed in the form.  Note this function will notify 
     * the user of what values are not valid.  One can either use 
     * set item in an error state or use a Dialog box.  We are using 
     * the dialog box methodolgy for the moment. 
     *  
     * @return true if the values are valid false if not. 
     */
    verifyValues: function ()
        {
        // Verify the password field
        if ( isc.isA.nonemptyString(newpassword) )
            {
            var newpassword = this.getValue("newpassword");
            var verifypassword = this.getValue("verifypassword");
            if ( newpassword != verifypassword )
                {
                OPINIATOR.warn("Passwords do not match");
                return false;
                }
            }

        return true;
        },
        
    /**
     * Store the values in the form's fields in to the object.  No 
     * verification is performed. 
     *  
     * @param user The object in which the forms fields values are 
     *             stored.
     */
    saveValues: function ( user )
        {
        user.firstName = this.getValue("firstname");
        user.lastName = this.getValue("lastname");
        user.emailAddress = this.getValue("email");
        var oldpassword = this.getValue("oldpassword");
        var newpassword = this.getValue("newpassword");
        if ( isc.isA.nonemptyString(oldpassword) )
            {
            user.oldpassword = sha1(oldpassword);
            user.password = sha1(newpassword);
            }
        else
            {
            delete user.oldpassword;
            delete user.password;
            }
        },

    /**
     * Called by the form whenever an item's values are changed. 
     * We enable the save button upon change. 
     */
    itemChange: function (item, newValue, oldValue)
        { this.parentEditor.enableSave(); }

    });


/**
 *  Edit's the users information via the User object.  Since the
 *  user cannot edit all elements of his/her own object this
 *  editor is limited to only those values that user can edit.
 *  
 *  The editor is a Vetical Layout canvas that contains the form
 *  the edits the users values.  However, it also contains
 *  an editor tools button strip that provides the save and
 *  cancel buttons for the editor
 */
isc.defineClass("OPINIATOR_SelfEditor", isc.VLayout);
OPINIATOR_SelfEditor.addProperties({

    // Private Properties
    _saveButton: null,
    _cancelButton: null,
    _form: null,
    _editortools: null,

    /**
     * Called when the widget is first intialized but prior to 
     * drawing. 
     */
    initWidget : function () 
        {
        //  The Editor tool strip which contains the editor buttons save and cancel
        this._editortools = isc.ToolStrip.create({
            align:"right",
            layoutRightMargin:3,
            styleName: "defaultCanvas",
            height:30,
            width:"100%"
            });


        // Save Button when clicked calls this object's 'save' method.  Uses Opiniator button for styling
        this._saveButton = OPINIATOR_Button.create({
            title:"Save",
            visibility: "hidden",
            target:this,
            layoutAlign:"center",
            icon: "icons/16/save.png",
            click: function ()
                { this.target.save(); return true;}
            });

        // Cancel Button when clicked calls this object's 'cancel' method.  Uses Opiniator button for styling
        this._cancelButton = OPINIATOR_Button.create({
            title:"Cancel",
            layoutAlign:"center",
            visibility: "hidden",
            target:this,
            icon: "icons/16/cancel.png",
            click: function ()
                { this.target.cancel(true); return true;}
            });

        //  Add the members to the tool backwards so that cancel comes first and save next.  This is the Opiniator model
        //  and might need to be reversed depending on the skin being used
        this._editortools.addMember(this._cancelButton);
        this._editortools.addMember(this._saveButton);

        // Create a form canvas that will have the editor fields for the User object.
        this._form = OPINIATOR_SelfForm.create({ 
            ID: "SelfFormID",
            width:"100%",
            height:"*",
            parentEditor:this
            });

        // Add the form and the editor tools. to the vertical layout. with a 5 px margin on the left
        this.members = [this._form, this._editortools];
        this.layoutLeftMargin = 5;

        // Call superclass implementation of this object
        this.Super("initWidget", arguments);

        this.edit();
        },

    /**
     * Called when the widget is destroyed.  In order to help the 
     * garbage collector we delete properties that we've stored 
     * objects in. 
     */
    destroy: function()
        {
        delete this._saveButton.target;
        delete this._saveButton;
        delete this._cancelButton.target;
        delete this._cancelButton;
        delete this._form;
        delete this._editortools;
        // Call the super class to destroy the rest of th widget
        this.Super("destroy", arguments);
        },


    /**
     * Enable the save and cancel buttons.   Depending on the skin 
     * used we may either use a hide/show model or enable/disable 
     * where the buttons are actually still shown. 
     *  
     * In Opiniator we use a hide/show model. Therefore, enableSave 
     * shows the buttons.  If the buttons are enabled/shown it 
     * implies that values have been modified in the form. 
     */
    enableSave: function ()
        {
        this._saveButton.show();
        this._cancelButton.show();
        },

    /**
     * Disable the save and cancel buttons.   Depending on the skin 
     * used we may either use a hide/show model or enable/disable 
     * where the buttons are actually still shown. 
     *  
     * In Opiniator we use a hide/show model. Therefore, disableSave
     * hides the buttons.  If the buttons are disabled/hidden it 
     * implies that values have been modified in the form. 
     */
    disableSave: function ()
        {
        this._saveButton.hide();
        this._cancelButton.hide();
        },

    /**
     * Determines if the object being edited was modifed.
     */
    wasModified: function ()
        { return this._saveButton.isVisible(); },

    /**
     *  Cleanup the user object.  Called to remove items from the
     *  user object that we don't want to send to the server.  And
     *  properties in the Javascript object that are not SmartClient
     *  properties will be transmitted back to the server.
     *  Therefore, we remove properties we don't want to send.
     *  
     *  @param user     The user object we want cleanup.
     */
    _cleanupSelf: function ( user )
        {
        delete user.roles;
        delete user.active;
        delete user.lastActivity;
        delete user.orgUnitRef;
        },

    /**
     * Cancel the changes made by the user and restores the object 
     * back to its original state. 
     */
    cancel: function ()
        {
        // Make sure teh save button is disabled
        this.disableSave();
        // Just re-edit the object
        this.edit();
        },

    /**
     * Saves the object edited in the form to the server.  Most 
     * often called by the save button. 
     */
    save: function ()
        {
        // Verify that the form is accurate first
        if ( this._form.verifyValues() )
            {
            // Show the busy indicator on the Editing form.  
            // This could have been done in the parent canvas as well.
            this._form.showBusy(true, OPINIATOR.BUSY_DELAY);

            // Make sure we don't just start pounding on the save button
            this.disableSave();

            // Clone the user object and then retrive all the values in the form into the cloned object.
            var user = isc.clone(OPINIATOR_MAIN.user);
            this._form.saveValues(user);
            // Cleanup the cloned object so that we only transmit the minimal amount of information back to
            // the server.
            this._cleanupSelf(user);
            // Save the object in the server.  WebClientSvc is a global object that is a Server RPC object for perfoming
            // functions on the server.   If the operation  is successful call _saveDone if an error call error.
            WebClientSvc.saveUser(user, this.callback("_saveDone"), this.callback("error"));
            }
        },

    /**
     * Called upon successfull completion of the save operation on 
     * the server. 
     *  
     * @param user  A refreshed user object is sent back upon 
     *              successful save.
     */
    _saveDone: function ( user )
        {
        // Turn off the busy indicator
        this._form.showBusy(false);
        // Replace the old global user object as the new user object
        OPINIATOR_MAIN.user = user;
        // Redit the new user object
        this.edit();
        // Change the heading information in the screen by telling the Main canvas that we have
        // a new user object
        OPINIATOR_MAIN.show(user);
        },

    /**
     * Edit the global user object.  By putting the user objects 
     * values in the form. 
     */
    edit: function ()
        { this._form.editValues(OPINIATOR_MAIN.user); },

    /**
     *  Determine's if the object being edited was modified if so
     *  then the user will be prompted to to save the object. Called
     *  when the user performs an action that would result in
     *  closing the editor without saving
     */
    checkSave: function ( )
        {
        if ( this.wasModified() )
            {
            // Prompt the user then call checkSaveReply
            OPINIATOR.askOk("You have made changes that will be lost! Would you like to save first?", this.callback("checkSaveReply"));
            }
        },

    /**
     * Callback from save prompt.
     * 
     * @param value If value is true then the user hit the 'ok'
     *              button. otherwise, they hit the 'cancel' button.
     */
    checkSaveReply: function ( value )
        {
        // if yes then save the object
        if ( value )
            { this._form.save(); }
        },


    /**
     *  Error handler for any web service methods called from this
     *  object.  The states are hanled as such.
     * 
     *  @param code - Error code sent by the server.  For SOAP web
     *              services this code is not usefull.
     *  @param msg - The message sent from the server upon error. By
     *             default the message is just displayed however,
     *             two msg's imply special states
     * 
     *  Special States:
     * 
     *  StaleState - caused when a save is performed on a object
     *  who's version number is out date with respect the version in
     *  persistent store. Implies that the object was changed while
     *  the user was editing.
     *  
     *  DeletedState - caused when a save is performed on an object
     *  and the object has been deleted in the persitent store.
     *
     */
    error: function (code, msg)
        {
        this.enableSave();
        this._form.showBusy(false);
        switch ( msg )
            {
            case "StaleState":
                OPINIATOR.warn("Changes were made to the user information by another user while you were editing.\nYour changes were lost! Please re-login.");
                OPINIATOR_MAIN.close();
                break;

            case "DeletedState":
                OPINIATOR.warn("Your account has been deleted by the administrator.");
                OPINIATOR_MAIN.close();
                break;

            default:
                OPINIATOR.warn(msg);
                break;
            }
        },

    /**
     *  Callback method returns an object that can be used as a
     *  callback to a method within this object.  
     *  
     *  @param methodName   Name of the method to callback. 
     */
    callback: function (methodName)
        { return {target:this, methodName:methodName}; }

    });

