Friday, August 28, 2020

Custom Authentication Scheme Part 2

 


In Part 1 we showed you how to create your own custom authentication scheme. In this blog we want to add to our previously created custom authentication scheme the functions to change or reset the password.


Let's start with the function to change the password.


Before we can start we need the application from Part 1. If you haven't created it yet please follow the instructions from the first blog (https://tm-apex.blogspot.com/2020/03/custom-authentication-scheme-part1.html). Now we open the application builder of the application and create a new page which we call "CHANGE PASSWORD". In our example it is Page 9998.  Change the Template Option to "Login". Then add a new static content region that we call "Change Password" and choose "Login" as Template Style. Next we need 4 Page-Items.

  • Name: P9998_USERNAME
  • Icon: icon-login-username
  • Placeholder: Username
  • Name: P9998_OLD_PASSWORD
  • Icon: icon-login-password
  • Placeholder: Enter Current Password
  • Name: P9998_NEW_PASSWORD
  • Icon: icon-login-password
  • Placeholder: Enter New Password
  • Name: P9998_NEW_PASSWORD_2
  • Icon: icon-login-password
  • Placeholder: Re-type New Password
When we have done this, we add three validations to the items that check the input before we change the password. First validation is to check if the username exists. For this we add a validation to P9998_USERNAME. Choose "Rows returned" as type and enter the following sql query:

SELECT USER_NAME
  FROM TB_USERS
 WHERE upper(USER_NAME= UPPER(:P9998_USERNAME)

As error message we can write for example "Username does not exist". 

Next we check if the old password is correct. Again add a validation to P9998_OLD_PASSWORD. Choose "PL/SQL Function (returning Error Text)" as type and enter the following pl/sql code:

DECLARE 
  error      varchar2(200) := 'The old password is not correct.';
  v_password tb_users.password%type;
BEGIN
  select password 
    into v_password 
    from tb_users
   where upper(user_name= upper(:P9998_USERNAME);  

  if ENCRYPT_PASSWORD(upper(:P9998_USERNAME), :P9998_OLD_PASSWORD) != v_password then
    return error;
  else 
    return null;
  end if;
END;

As a last validation we want to check if the both new passwords are identical. So again we need a validation to P9998_NEW_PASSWORD_2. Choose "PL/SQL Function (returning Error Text)" as type and enter the following pl/sql code:

DECLARE 
  error varchar2(200) := 'The password does not match.';
BEGIN
  if :P9998_NEW_PASSWORD != :P9998_NEW_PASSWORD_2 then
    return error;
  else 
    return null;
  end if;
END;

At this point you could add several more checks. For example, you could specify the minimum length of a password. Or if it should contain special characters, numbers etc. But for our demo it should be enough for now :-)

Next we need 2 buttons with which we can either start the process for changing the password or cancel the process.

  • Name: CHANGE
  • Label: Change Password
  • Template: Text (Hot)
  • Type: Normal
  • Action: Submit Page
  • Name: CANCEL
  • Label: Cancel
  • Template: Text
  • Type: Primary
  • Action: Redirect to Page in this Application
  • Page: &APP_RETURN_PAGE.
Once we have done that, we need to add a process that changes the password. Name it for example "Change User Password" and enter the following PL/SQL Code:

Begin
   Update tb_users
      set password = ENCRYPT_PASSWORD(upper(:P9998_USERNAME), :P9998_NEW_PASSWORD)
      WHERE upper(USER_NAME= upper(:P9998_USERNAME);
end;

Success Message will be for example "The password has been changed.".

Finally add an after process branch to return to the previous page.

Type: Page or URL
Target: &APP_RETURN_PAGE.

To access the page you could add another entry for to the "Desktop Navigation bar". 

That´s it :-)



As second topic we want to show you how to add a function to reset the password.

For that we move to the "Login" Page. In our example it will be Page 9999 and add a new Button "RESET" that redirect to Page 9997 in this application. Then we create a new page which we call "RESET PASSWORD". In our example it is Page 9997. Change the Template Option to "Login" and make the page as a public page, because at this point we are not authenticated. Then add a new static content region that we call "Reset Password" and choose "Login" as Template Style. Next we need a Page-Item that we name "P9997_USERNAME".

When we have done this, we add a validation to the page-item to check if the username exists. Choose "Rows returned" as type and enter the following sql query:

SELECT USER_NAME
  FROM TB_USERS
 WHERE upper(USER_NAME= UPPER(:P9997_USERNAME)

As error message we can write for example "Username does not exist". 

Next we need 2 buttons with which we can either start the process for changing the password or cancel the process.

  • Name: RESET
  • Label: Reset Password
  • Template: Text (Hot)
  • Type: Normal
  • Width: Stretch
  • Action: Submit Page
  • Name: CANCEL
  • Label: Cancel
  • Template: Text
  • Type: Normal
  • Width: Stretch
  • Action: Redirect to Page in this Application
  • Page: 9999
Once we have done that, we need to add a process that reset the password. Name it for example "Reset Password" and enter the following PL/SQL Code:

declare
    l_new_pw      varchar2(80):= dbms_random.string('x',8);
    l_username    tb_users.user_name%type;  
    l_email       tb_users.email%type;
    l_body        clob;
begin
    select user_name, email
      into l_username, l_email
      from tb_users
     where upper(user_name= upper(:P9997_USERNAME); 
     
    l_body := 'Dear Sir or Madame,'||utl_tcp.crlf||utl_tcp.crlf;
    l_body := l_body ||'your password has been reset to ' || l_new_pw ||utl_tcp.crlf;
    l_body := l_body ||'Best regards'||utl_tcp.crlf;
    l_body := l_body ||'The APEX Dev Team'||utl_tcp.crlf
     
    apex_mail.send(
        p_to       => l_email,   
        p_from     => 'noreply@apex.com',
        p_body     => l_body,
        p_subj     => 'Reset Password'); 
        
    apex_mail.push_queue;    
    
    update tb_users
       set password = ENCRYPT_PASSWORD(upper(:P9997_USERNAME), l_new_pw)
     where upper(USER_NAME= l_username;
end;

Success Message will be for example "Your password has been reset. You will shortly receive an email with a new password".

Finally add an after process branch to return to the previous page.

Type: Page or URL
Target: 9999

All done...now you can change or reset your password by yourself :-)
We hope you enjoyed and you learned something :)
Feel free to ask us if you run into any issue.


Labels: , ,

Friday, August 14, 2020

Highlight cells based on value (Interactive Grid #5)

 




As we said before, our tips and tricks series about the Interactive Grid continues, which have always helped us in the development process.

Let's move on tip five :-) 


#5 Highlight cells based on value

It happens from time to time that it can be useful to highlight cells in an interactive grid. In this blog we would like to show you how this can be done based on cell values.

For this example we use the sample dataset "Tasks Spreadsheet", so we have the table "EBA_TASK_SS" with a status column which filled with different values. Then we create an application with an interactive grid. As source we use the table "EBA_TASK_SS". Now we a add a CSS class to the interactive grid that we call for example "highlight".




After that we must add a class to the column which we want to highlight. As already mentioned we want to highlight the column "status", so we add the css class "status_column" to it. 




So that our cells are now highlighted we need a JavaScript function which we have to execute on page load. For that we create first the js function in the JavaScript "Function and Global Variable Declaration". 


function highlight_ig_cells() {
    // for each cell in marked column
    $('.highlight td.status_column').each(function() {
        // get cell text
        cellData = $(this).text();
        // rules for coloring
        if (cellData == 'Open')
            this.style.backgroundColor = 'limegreen';
        else if (cellData == 'Pending')
            this.style.backgroundColor = 'gold';
        else if (cellData == 'On-Hold')
            this.style.backgroundColor = 'orange';
        else if (cellData == 'Closed')
            this.style.backgroundColor = 'tomato';
    })
};


To execute the Function when Page Load, enter the following Code in the "Execute when Page Loads Editor":

highlight_ig_cells()




From now on our cells are highlighted :-)

But we still have two Problems that we have to solved. 

The first problem we have when we change pages or load more rows in the interactive grid. Whenever this happens the JavaScript function must be executed again. For that we create a Dynamic Action that fired by the event "Page Change (Interactive Grid)". Create a true action that execute our JavaScript function (highlight_ig_cells()).




And the second problem we have is when the interactive grid is editable. For example, if you change the "Status" column, the color must also change. So we need a sceond Dynamic Action that execute our JavaScript Code. But in this case by the Event "Save (Interactive Grid). 


That´s it :-)





And here is the demo app.


We hope the tips and tricks will help you. 
If you have questions or suggestions, please leave us a comment ;-)

Labels: , ,

Thursday, August 6, 2020

Set a value in the „Search Field“ from a previous page (Interactive Grid #4)





As already mentioned, we share with you some interesting tips and tricks that have always helped us in the development process.

Let's move on tip four :-) 


#4 Set a value in the „Search Field“ from a previous page

For this example we have first installed the sample dataset "Customer Orders" (SQL Workshop > Utilities > Sample Datasets). When this is done, we create an application with an interactive grid. As source we use the table "Customers". Then we specify the column type for "FULL_NAME" and "EMAIL_ADDRESS" as a link. As the link target we indicate the page to which the value should be transferred to the search field. By this done we are finished creating the first page. 

Now we create a second page with an interactive grid as modal dialog (can also be a normal page). This will be the page that we open by clicking on one of the link column. As source we use a SQL query with the following code:


select ORD.ORDER_ID,
       ORD.ORDER_DATETIME,
       CUS.FULL_NAME,
       CUS.EMAIL_ADDRESS,
       ORD.ORDER_STATUS
  from ORDERS ORD
  join CUSTOMERS CUS
    on ORD.CUSTOMER_ID = CUS.CUSTOMER_ID


After that, we assign a static-id to the Interactive Grid, so that we can address it via JavaScript without any problems. In our example this is "my_ig". To initialize the search value we need a page-item that we name "P2_SEARCH" and specify the column type to hidden.

So that the search field is set with the value of the previous page, we need two lines of javascript code to first set the value in the search field and then execute the search. Enter the following Code in the JavaScript Execute when Page Loads Editor:


apex.region("my_ig").call("getToolbar").toolbar("findElement""search_field").val('&P2_SEARCH.');
apex.region("my_ig").call("getActions").invoke("search");


Finally, on the previous page we have to set the value in the two link columns. Click on link target and choose by "Name" the page-item "P2_SEARCH" and for the "Value" &FULL_NAME. (or &EMAIL_ADRESS.)




That's it...from now on you can click on a link column and set the value to the search field on the target page :-)


Optional hint:

If you set several search values one after the other, they will always be added. To clear the search field you can add a "close" button on the modal dialog page that triggers a dynamic action. Create a true action that execute a JavaScript Code with the following:


apex.region("my_ig").call("getActions").invoke('reset-report');


Finally to close the dialog create a second true action and set the action to "Close Dialog".


And here is the demo app.


We hope the tips and tricks will help you. 
If you have questions or suggestions, please leave us a comment ;-)

Labels: , ,

Monday, August 3, 2020

Only allow numeric values as text input (Interactive Grid #3)




As already said, we share some interesting tips and tricks with you that have helped us again and again in  developments.

Let's start with the third tip :-) 


#3 Only allow numeric values as text input

If data is to be edited in the Interactive Grid, it can be helpful to use functions to check the input before inserting or updating. Normally this can be done with validations. But why should the user be given the opportunity to enter incorrect data? For example, when entering numerical values, you can directly exclude the possibility of entering letters or special characters. For this we only need a few lines of code and save ourselves a lot of trouble or the user unnecessary error messages.

For the implementation we create a jQuery validation that is executed on page load. So in the JavaScript Execute when Page Loads Code Editor, enter the following code:

$(".only-numeric").bind("keypress"function (e) {
    var keyCode = e.which ? e.which : e.keyCode               
    if (!(keyCode >= 48 && keyCode <= 57)) {
      return false;
    }else{
      return true;
    }
});

Then specify the css class "only-numeric" for all affected columns.





From now on only numeric values can be entered into the cell via keyboard :-)


Optional:
If you need decimal places, use this code so that you can also write an dot in the field.

$(".only-numeric").bind("keypress"function (e) {
    var keyCode = e.which ? e.which : e.keyCode               
    if (!(keyCode >= 48 && keyCode <= 57&& !(keyCode == 46)) {
      return false;
    }else{
    }
});


We hope the tips and tricks will help you. If you have questions or suggestions, please leave us a comment ;-)


And here is the demo app.



Labels: , ,