Picture Competition Web App – Node JS, Mongo DB, Vue JS

Picture Competition is a mobile responsive real-time web application developed in Node JS and Mongo DB. Its frontend is designed in Bootstrap and Vue JS. You can create competition between 2 people and the others can vote on the person based on looks or skills etc.

Demo:

https://github.com/adnanafzal565/picture-competition-web-app-node-js

FeaturesFreePremium $100
Login and registrationYesYes
Create competitionsYesYes
Search & sortYesYes
Vote on CompetitionYesYes
Delete CompetitionYesYes
Reset PasswordNoYes
Email VerificationNoYes
Adult Image ValidationNoYes
SMTP Configurations Admin PanelNoYes
Real-time VotesNoYes
Real-time Admin Panel StatisticsNoYes
Real-time CommentsNoYes
User ProfileNoYes
Realtime update via socketsNoYes
NotificationsNoYes
Load more buttonNoYes
Admin panelNoYes
Manage competitionsNoYes
Free customer supportNoYes

Mongo DB Backend

competition’s collection

1. Login and Registration

It uses login authentication using JWT (JsonWebToken). It does not use the browser session due to the following reasons:

  1. Sessions are destroyed once your app is restarted from terminal.
  2. Sessions are not linked with the server, they are stored in browser only.

The benefit of using JWT is that you can always log out a user from your Mongo DB. Just go to the “users” collection and find the specific user. Then empty the “accessToken” field of that user’s document.

Login and Registration

2. Create Competitions

Registered users can create competitions between 2 users. You can enter name and upload 1 picture of each competitor. There is no limit in the number of competitions to create, you can create as many as you want. You can view your created competitions on your “My Competitions” page.

As soon as you create a competition, it will automatically be displayed to all the users as well as to the admin in real-time. Users do not have to refresh the page to see new competitions. It uses Socket IO to display data in real-time.

Create Competition

3. Search

Users can search competitions by the name of competitors. Data is filtered and rendered using Vue JS. There is no need to press the enter key, the data is filtered as soon as the user starts typing in the search box.

Search and Sort

4. Sort

Users can also sort the competitions by any of the following for sorting functions:

  1. Highest votes to lowest.
  2. Lowest votes to highest.
  3. Oldest to newest.
  4. Newest to oldest (default).

5. Vote on Competition

Logged-in users can vote on the competition. You can either vote on one of the competitors. Once the vote is cast on one competitor, it cannot be removed. Also, you can cast your vote on only one of the competitors, not on both of them. It is also real-time, as soon as the vote is cast, it will automatically be displayed to all the users and the counter is incremented. The counter displays the number of votes cast on each competitor.

Votes

6. Delete Competition

Competitions can only be deleted by either of the users who created the competition, or by the admin. Once the competition is deleted, all the uploaded images will be deleted too. As soon as the competition is deleted, it will automatically be removed from all the other users too, without having them refresh the page.

7. Realtime Update using Sockets

Sockets are used for real-time communication. Instead of fetching the data from the server after regular intervals, sockets attach listeners to the client-side. Listeners are listening to the events sent from the server. The server will emit the event and the client will listen to that event and respond accordingly. In this project, sockets are used for the following features:

  1. When competition is created.
  2. When competition is deleted.
  3. To increase the counter after vote is casted to the competition.
  4. Notifications.

8. Notifications

When a competition is deleted by the admin, the admin will write the reason for the deletion. Thus, a notification will be sent to the user along with the reason why his competition was removed. By default, notification status is “unread” and they are highlighted. As soon as the user clicks on any of the notifications, that notification will be marked as “read” and it will no longer be highlighted.

Notifications

9. Load More Button

When the data in the database increases, it is not feasible to load all the data in a single AJAX request. So a “load more” button is created to solve this problem. For example, 10 records are fetched in the first AJAX request. The next 10 records will be fetched when the “load more” button is clicked, and so on.

Load More

10. Admin Panel

Admin panel is created so you (administrator) can delete any competition you find offensive. The default email and password of admin are:

email = admin@gmail.com
password = admin

11. Manage Competitions

Admin can delete competitions that he finds offensive. However, the admin must give the reason why that competition is deleted. A notification will be sent to the user who created that competition and he will be able to view it from the top navigation bar.

12. Reset Password

Now you will be able to reset your password if you ever forgot. You just need to enter your email address and an email will be sent to you with a link to reset the password. We are using the nodemailer module to send an email.

Forgot Password
Reset Password

13. Email Verification

When a new user registers, we are sending a verification email to the user’s entered email address. The user will not be able to log in until he verifies his email address. When a user clicks the link on his email address, he will receive a message that says that his account is verified. Then he will be able to log in successfully.

Email Verification

14. SMTP Configurations from Admin Panel

To send an email, you will need an SMTP server. Every SMTP server requires some configurations to set up that include, host, port, email, and password. You can write these values directly hardcoded in your code, but to update these values in the future, you have to find these values in the code and update them.

In this project, you can set these configurations directly from the admin panel. Once the values are set, new emails will be sent using the new configurations.

SMTP configurations from admin panel

15. Adult Image Validation

This is a must-have feature if you are creating a website that allows users to upload pictures and they will be seen to the world. Anyone can upload an image that contains adult content, and it will not be good for your business. So when the user is uploading pictures while creating competition, the system will automatically check if the image is safe to upload.

If the image is an adult image, then an error will be shown to the user and it will not be uploaded.

16. Admin Panel Stats

Admin can see total users, total competitions, and total votes cast so far. They are also real-time, so when a new user is registered, or new competition is created, or event a new vote is cast, it will automatically be incremented here.

Also, when competition is deleted, the number will automatically get decremented as well, without having the admin refresh the page.

Admin Panel Stats

17. Real-time Comments

Users can comment on each competition. And they are also real-time as well. Once a new comment is added, it will immediately be displayed to all the other users as well. They do not have to refresh the page to see new comments.

Real-time Comments

18. User Profile

Users can now update their names and profile pictures. We are using the fs (file system) module to upload the picture. User can also add their bio, date of birth, country, and social media links. Media links include Facebook, Instagram, google plus, Twitter, and LinkedIn.

User can also change their account password. In order to change the password, the user must enter the current password. The new password should be entered twice for confirmation.

User Profile

19. Free Customer Support

This is not a feature of the project, but it is a free service provided for the pro version only. That means if you find any difficulty in installing or configuring the project, we will help you install it. Also, if you encounter any error or a bug in the released version, then it can be fixed too.

These are all the features we have right now in the picture competition web app. We are open to more ideas. If you have more ideas to add, kindly do let us know.

Use SweetAlert confirmation dialog – HTML & Javascript

In this tutorial, we are going to show you, how you can show a sweetalert confirmation dialog when submitting a form. For example, if you have a form that when submits delete the data from the database. In that case, you must verify with the user because he might click that button by accident. So you can show a nice dialog using the Sweetalert library. Suppose you have the following form:

<form method="POST" action="do-something.php" onsubmit="return submitForm(this);">
	<input type="text" name="name" />
	<input type="submit" />
</form>

When the form submits, we are calling a Javascript function submitForm and passing the form as a parameter. Then you need to download the Sweetalert library from here. After downloading, paste that into your project and include it in your HTML file:

<script src="sweetalert.min.js"></script>

Now, we can create that Javascript function that will ask for confirmation. Once confirmed, it will submit the form.

<script>
	function submitForm(form) {
		swal({
			title: "Are you sure?",
			text: "This form will be submitted",
			icon: "warning",
			buttons: true,
			dangerMode: true,
		})
		.then(function (isOkay) {
			if (isOkay) {
				form.submit();
			}
		});
		return false;
	}
</script>

At this point, if you submit the form, you will see a SweetAlert confirmation dialog first. All the form fields will be submitted correctly on the server-side. You can check it by printing out all the values received from the form:

<?php
print_r($_POST);

Checkout 10 javascript libraries for every web project.

Social networking site in Laravel – Node JS, Socket IO

When you hear the word “social networking site”, the first name that came to your mind must be Facebook. Then Instagram, Twitter, etc. We create a social networking site project just to give you an idea of how things work. It is not the exact code used in tech giants like Facebook. But it gives you an idea of how they do it.

Demo

FeaturesFreePremium ($100)
AuthenticationYesYes
Profile UpdateYesYes
Create posts (with images and videos)YesYes
Like, comments and repliesYesYes
Share postsYesYes
Friends post on your wallYesYes
Search usersYesYes
Create friendsYesYes
Admin panelYesYes
Email verificationNoYes
Reset passwordNoYes
NotificationsNoYes
Realtime post update and deleteNoYes
NoYes
Search pages, and groupsNoYes
Create pages, and groupsNoYes
Private user-to-user chatNoYes
List of people who viewed your profileNoYes
Infinite scrollNoYes
people who liked and shared your postNoYes
Chat messages encryptionNoYes
Customer supportNoYes
Ban users, posts, pages, and groupsNoYes
Adult image validationNoYes
Show emails generated by systemNoYes
Online / offline statusNoYes
User last sceneNoYes
User activity logsNoYes

Previously, we create a Social Networking Site in Node JS and Mongo DB. But many people were asking that we create the same project in Laravel too, for PHP lovers. So here it is.

We have added 4 more features to it.

  1. Track system mails: All the emails sent from the system (Mail::to) function will be stored in a database. And the admin can view all the emails from the admin panel.
  2. Online/offline status: Now you can know when your friends are online and when they are offline. This will be helpful because you can just leave a message if the person is currently offline.
  3. Last scene of the user: You can also see your friend’s last scene active. This will help in situations like earthquakes etc. you can know which friends are not active in recent times.
  4. Track user activity: Admin can view the activities of users like when they are logged in when they created a page when they joined a group etc.

Task management & time tracking script – Javascript

We are going to create customized task management and time tracking script using simple HTML and Javascript. Since we are creating our own script, so we can customize it as much as we need. You can check the demo from here.

Video tutorial

Include libraries

First, we need to include all the libraries that we are going to use. We will be using Bootstrap for design and SweetAlert for displaying pop-up messages.

<!-- to make responsive -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<!-- include bootstrap css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" />

<!-- include jquery and bootstrap js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"></script>

<!-- include sweetalert for displaying dialog messages -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/2.1.2/sweetalert.min.js"></script>

Add project

Then we are going to create a button that when clicked will display a Bootstrap modal to add a task.

<div class="container" style="margin-top: 50px; margin-bottom: 50px;">
    
    <!-- button to add task -->
    <div class="row" style="margin-bottom: 50px;">
        <div class="col-md-12">
            <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addTaskModal">Add Task</button>
        </div>
    </div>

</div>

Then we are going to create a Bootstrap modal. It will have a form with project and task name fields. It will also have a dropdown from which the user can select his already created projects.

<!-- modal to add project and task -->
<div class="modal fade" id="addTaskModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Add Task</h5>
                <button class="close" type="button" data-dismiss="modal">x</button>
            </div>

            <div class="modal-body">
                <form method="POST" onsubmit="return taskObj.addTask(this);" id="form-task-hour-calculator">
                    
                    <!-- select project from already created -->
                    <div class="form-group">
                        <label>Project</label>
                        <select name="project" id="add-task-project" class="form-control" required></select>
                    </div>

                    <!-- create new project -->
                    <div class="form-group">
                        <label>New Project</label>
                        <input type="text" name="new_project" id="add-project" class="form-control" placeholder="Project Name">

                        <button type="button" onclick="taskObj.addProject();" class="btn btn-primary" style="margin-top: 10px;">Add Project</button>
                    </div>

                    <!-- enter task -->
                    <div class="form-group">
                        <label>Task</label>
                        <input type="text" name="task" class="form-control" placeholder="What are you going to do ?" required />
                    </div>
                </form>
            </div>

            <!-- form submit button -->
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                <button type="submit" form="form-task-hour-calculator" class="btn btn-primary">Add Task</button>
            </div>
        </div>
    </div>
</div>

Then we need to create a Javascript function “addProject” inside the “taskObj” object. It will first check if there is any project selected, if not, then it will show an error message. Then it will create an empty array in local storage if not already created. Then it will create a new project object with empty “tasks” array. Finally, it will push the project into an array and update the local storage value, and also it will reload the projects and tasks.

// main object
var taskObj = {

    // local storage key
    key: "projects",

    // add project
    addProject: function () {

        // check if project is selected
        if (document.getElementById("add-project").value == "") {
            swal("Please enter project name");
            return false;
        }

        // initialize local storage if not already initialized
        var option = "";
        if (localStorage.getItem(this.key) == null) {
            localStorage.setItem(this.key, "[]");
        }

        // get stored object from local storage
        var data = JSON.parse(localStorage.getItem(this.key));

        // project object
        var project = {
            id: data.length,
            name: document.getElementById("add-project").value,
            tasks: []
        };

        // push new project in local storage
        data.push(project);
        localStorage.setItem(this.key, JSON.stringify(data));

        // re-load all projects
        this.loadAllProjects();

        // show all tasks
        this.showAllTasks();
    },

};

To load all the projects, we will be using the following 2 functions inside this object:

// get all stored projects
getAllProjects: function() {
    if (localStorage.getItem(this.key) == null) {
        localStorage.setItem(this.key, "[]");
    }
    return JSON.parse(localStorage.getItem(this.key))
},

// load all projects in dropdown
loadAllProjects: function () {
    var projects = taskObj.getAllProjects();
    projects = projects.reverse();
    var html = "<option value=''>Select Project</option>";
    for (var a = 0; a < projects.length; a++) {
        html += "<option value='" + projects[a].id + "'>" + projects[a].name + "</option>";
    }
    document.getElementById("add-task-project").innerHTML = html;
    document.getElementById("form-task-hour-calculator-all-projects").innerHTML = html;
},

We will do the “showAllTasks” function later. Now, when the page loads, we need to fetch all the projects and tasks and display them.

// when page loads
window.addEventListener("load", function () {
    // show all projects and tasks
    taskObj.loadAllProjects();
    taskObj.showAllTasks();
});

At this point, you will be able to create projects and see them in the dropdown. Now we need to add tasks.

Add task

To add a task, we will get the project and task name. Then we will push the newly created task object into the “tasks” array. Finally, we will update the local storage value, hide the Bootstrap modal and reload the tasks.

// add new task
addTask: function (form) {

    // get selected project and entered task
    var project = form.project.value;
    var task = form.task.value;

    // add task in project's array
    var projects = this.getAllProjects();
    for (var a = 0; a < projects.length; a++) {
        if (projects[a].id == project) {
            var taskObj = {
                id: projects[a].tasks.length,
                name: task,
                status: "Progress", // Progress, Completed
                isStarted: false,
                logs: [],
                started: this.getCurrentTimeInTaskStartEndFormat(),
                ended: ""
            }
            projects[a].tasks.push(taskObj);
            break;
        }
    }

    // update local storage
    localStorage.setItem(this.key, JSON.stringify(projects));

    // hide modal
    jQuery("#addTaskModal").modal("hide");
    jQuery('.modal-backdrop').remove();

    // re-load all tasks
    this.showAllTasks();

    // prevent form from submitting
    return false;
},

You might have see the function “getCurrentTimeInTaskStartEndFormat”. Our task management script will save the date and time in format YYYY-MM-DD HH:mm:ss e.g. 2021-06-15 20:53:15.

// get current datetime in proper format (e.g. 2021-06-15 20:53:15)
getCurrentTimeInTaskStartEndFormat() {
    let current_datetime = new Date();
    var date = current_datetime.getDate();
    date = (date < 10) ? "0" + date : date;
    var month = (current_datetime.getMonth() + 1);
    month = (month < 10) ? "0" + month : month;
    var hours = current_datetime.getHours();
    hours = (hours < 10) ? "0" + hours : hours;
    var minutes = current_datetime.getMinutes();
    minutes = (minutes < 10) ? "0" + minutes : minutes;
    var seconds = current_datetime.getSeconds();
    seconds = (seconds < 10) ? "0" + seconds : seconds;
    let formatted_date = current_datetime.getFullYear() + "-" + month + "-" + date + " " + hours + ":" + minutes + ":" + seconds;
    return formatted_date;
},

Show all tasks

To show all tasks, we need to create a table where we will display all tasks created.

<!-- show all tasks -->
<table class="table">
    <caption class="text-center">All Tasks</caption>
        <tr>
            <th>Task</th>
            <th>Project</th>
            <th>Status</th>
            <th>Duration</th>
            <th>Date</th>
            <th>Action</th>
        </tr>

    <tbody id="all-tasks"></tbody>
</table>

Now is the time to create a function “showAllTasks” in our “taskObj” object. This will be a long function, so we will explain the working of this function step-by-step:

// show all tasks in table
showAllTasks: function () {
    var html = "";

    // get all projects
    var projects = this.getAllProjects();
    for (var a = 0; a < projects.length; a++) {
        projects[a].tasks = projects[a].tasks.reverse();

        // get tasks in each project
        for (var b = 0; b < projects[a].tasks.length; b++) {
            html += "<tr>";
                html += "<td>" + projects[a].tasks[b].name + "</td>";
                html += "<td>" + projects[a].name + "</td>";
                if (projects[a].tasks[b].isStarted) {
                    html += "<td><label class='started'>Started</label></td>";
                } else {
                    if (projects[a].tasks[b].status == "Completed") {
                        html += "<td><label class='completed'>" + projects[a].tasks[b].status + "</label></td>";
                    } else {
                        html += "<td>" + projects[a].tasks[b].status + "</td>";
                    }
                }

                // get total duration of each task using it's logs
                var duration = 0;
                for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
                    var log = projects[a].tasks[b].logs[c];
                    if (log.endTime > 0) {
                        duration += log.endTime - log.startTime;
                    }
                }

                // convert millisecond into hours, minutes and seconds
                duration = Math.abs((duration / 1000).toFixed(0));
                var hours = Math.floor(duration / 3600) % 24;
                hours = (hours < 10) ? "0" + hours : hours;
                // var days = Math.floor(diff / 86400);
                var minutes = Math.floor(duration / 60) % 60;
                minutes = (minutes < 10) ? "0" + minutes : minutes;
                var seconds = duration % 60;
                seconds = (seconds < 10) ? "0" + seconds : seconds;

                // show timer if task is already started
                if (projects[a].tasks[b].isStarted) {
                    var dataStartedObj = {
                        "duration": duration,
                        "project": projects[a].id,
                        "task": projects[a].tasks[b].id
                    };
                    html += "<td data-started='" + JSON.stringify(dataStartedObj) + "'>" + hours + ":" + minutes + ":" + seconds + "</td>";
                } else {
                    html += "<td>" + hours + ":" + minutes + ":" + seconds + "</td>";
                }

                // show task duration if completed
                if (projects[a].tasks[b].status == "Completed") {
                    html += "<td>" + projects[a].tasks[b].started + "<br><span style='margin-left: 30px;'>to</span><br>" + projects[a].tasks[b].ended + "</td>";
                } else {
                    html += "<td>" + projects[a].tasks[b].started + "</td>";
                }

                // form to change task status
                html += "<td>";
                    html += "<form method='POST' id='form-change-task-status-" + projects[a].id + projects[a].tasks[b].id + "'>";
                        html += "<input type='hidden' name='project' value='" + projects[a].id + "'>";
                        html += "<input type='hidden' name='task' value='" + projects[a].tasks[b].id + "'>";
                        html += "<select class='form-control' name='status' onchange='taskObj.changeTaskStatus(this);' data-form-id='form-change-task-status-" + projects[a].id + projects[a].tasks[b].id + "'>";
                            html += "<option value=''>Change status</option>";
                            if (projects[a].tasks[b].isStarted) {
                                html += "<option value='stop'>Stop</option>";
                            } else {
                                html += "<option value='start'>Start</option>";
                            }
                            if (projects[a].tasks[b].status == "Progress") {
                                html += "<option value='complete'>Mark as Completed</option>";
                            } else {
                                html += "<option value='progress'>Make in Progress Again</option>";
                            }
                            html += "<option value='delete'>Delete</option>";
                        html += "</select>";
                    html += "</form>";
                html += "</td>";
            html += "</tr>";
        }
    }
    document.getElementById("all-tasks").innerHTML = html;
},

The step number corresponds to the highlighted line above.

  1. First, we are looping through all projects and each project’s tasks.
  2. Displaying the task name and project name.
  3. If the task status is “started” then displaying a started message. (we will do this later).
  4. And if the task status is “completed”, then displaying a message for the completed task.
  5. Then we will get the total duration of each task.
  6. The duration will be in milliseconds, so we will convert that into days, hours, minutes, and seconds.
  7. If the task is started, then we will attach an attribute “data-started” to the <td> tag. This will allow us to continuously update the timer every second.
  8. And if the task is completed, then we will display the start and end times of the task. So you can know when the task started and when it is finished.
  9. And finally, we are creating a form from which we can change the status of the task.

Change task status

Now we need to create a function to change the status of the task. Again, this will be a long function, we will explain that step-by-step too.

// change task status
changeTaskStatus: function (self) {

    // if task is not selected
    if (self.value == "") {
        return;
    }

    // loop through all projects
    var formId = self.getAttribute("data-form-id");
    var form = document.getElementById(formId);
    var projects = this.getAllProjects();
    for (var a = 0; a < projects.length; a++) {

        // if project matches
        if (projects[a].id == form.project.value) {

            // loop through all tasks of that project
            for (var b = 0; b < projects[a].tasks.length; b++) {

                // if task matches
                if (projects[a].tasks[b].id == form.task.value) {

                    // if the status is set to delete
                    if (self.value == "delete") {

                        // ask for confirmation
                        swal({
                            title: "Are you sure?",
                            text: "Deleting the task will delete its hours too.",
                            icon: "warning",
                            buttons: true,
                            dangerMode: true,
                        })
                        .then((willDelete) => {
                            if (willDelete) {

                                // remove task from array
                                projects[a].tasks.splice(b, 1);

                                // update local storage
                                localStorage.setItem(this.key, JSON.stringify(projects));

                                // re-load all tasks
                                this.showAllTasks();
                            } else {

                                // reset dropdown
                                self.value = "";
                            }
                        });
                    } else if (self.value == "complete") {
                        // mark as completed
                        projects[a].tasks[b].status = "Completed";

                        // stop the timer
                        projects[a].tasks[b].isStarted = false;

                        // log end time
                        projects[a].tasks[b].ended = this.getCurrentTimeInTaskStartEndFormat();
                        for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
                            if (projects[a].tasks[b].logs[c].endTime == 0) {
                                projects[a].tasks[b].logs[c].endTime = new Date().getTime();
                                break;
                            }
                        }
                    } else if (self.value == "progress") {
                        // mark as in progress
                        projects[a].tasks[b].status = "Progress";

                        // stop the timer
                        projects[a].tasks[b].isStarted = false;
                    } else if (self.value == "start") {
                        // ask for confirmation
                        swal({
                            title: "Are you sure?",
                            text: "This will start the timer.",
                            icon: "warning",
                            buttons: true,
                            dangerMode: true,
                        })
                        .then((doStart) => {
                            if (doStart) {
                                
                                // mark as started
                                projects[a].tasks[b].isStarted = true;

                                // add in log
                                var logObj = {
                                    id: projects[a].tasks[b].logs.length,
                                    startTime: new Date().getTime(),
                                    endTime: 0
                                };
                                projects[a].tasks[b].logs.push(logObj);

                                // update local storage
                                localStorage.setItem(this.key, JSON.stringify(projects));

                                // re-load all tasks
                                this.showAllTasks();
                            } else {

                                // reset dropdown
                                self.value = "";
                            }
                        });
                    } else if (self.value == "stop") {

                        // ask for confirmation
                        swal({
                            title: "Are you sure?",
                            text: "This will stop the timer.",
                            icon: "warning",
                            buttons: true,
                            dangerMode: true,
                        })
                        .then((doStop) => {
                            if (doStop) {

                                // mark as stopped
                                projects[a].tasks[b].isStarted = false;

                                // update end time in log
                                for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
                                    if (projects[a].tasks[b].logs[c].endTime == 0) {
                                        projects[a].tasks[b].logs[c].endTime = new Date().getTime();
                                        break;
                                    }
                                }

                                // update local storage
                                localStorage.setItem(this.key, JSON.stringify(projects));

                                // re-load tasks
                                this.showAllTasks();
                            } else {

                                // reset dropdown
                                self.value = "";
                            }
                        });
                    }
                    break;
                }
            }
            break;
        }
    }

    // delete, start and stop are already handled above
    if (self.value == "delete"
        || self.value == "start"
        || self.value == "stop") {
        //
    } else {
        // update local storage and re-load tasks
        localStorage.setItem(this.key, JSON.stringify(projects));
        this.showAllTasks();
    }
},

Comments have been added with each line for an explanation. If you still find it difficult, please feel free to mention it in the comments section below.

At this point, you will be able to start and stop the timer. Upon stopping, you will be able to see the start and end time of the task along with the total duration of the task.

Now, we need to update the timer each second if the task is started. You need to write the following lines in your window “load” event listener:

// call this function each second
setInterval(function () {

    // increment 1 second in all running tasks
    var dataStarted = document.querySelectorAll("td[data-started]");
    for (var i = 0; i < dataStarted.length; i++) {
        var dataStartedObj = dataStarted[i].getAttribute("data-started");
        var dataStartedObj = JSON.parse(dataStartedObj);
        dataStartedObj.duration++;

        // convert timestamp into readable format
        var hours = Math.floor(dataStartedObj.duration / 3600) % 24;
        hours = (hours < 10) ? "0" + hours : hours;
        // var days = Math.floor(diff / 86400);
        var minutes = Math.floor(dataStartedObj.duration / 60) % 60;
        minutes = (minutes < 10) ? "0" + minutes : minutes;
        var seconds = dataStartedObj.duration % 60;
        seconds = (seconds < 10) ? "0" + seconds : seconds;
        dataStarted[i].innerHTML = hours + ":" + minutes + ":" + seconds;

        // update log end time
        var projects = taskObj.getAllProjects();
        for (var a = 0; a < projects.length; a++) {
            if (projects[a].id == dataStartedObj.project) {
                for (var b = 0; b < projects[a].tasks.length; b++) {
                    if (projects[a].tasks[b].id == dataStartedObj.task) {
                        for (var c = 0; c < projects[a].tasks[b].logs.length; c++) {
                            if (c == projects[a].tasks[b].logs.length - 1) {
                                projects[a].tasks[b].logs[c].endTime = new Date().getTime();

                                // update local storage
                                window.localStorage.setItem(taskObj.key, JSON.stringify(projects));

                                // update timer
                                dataStarted[i].setAttribute("data-started", JSON.stringify(dataStartedObj));

                                break;
                            }
                        }
                        break;
                    }
                }
                break;
            }
        }
    }
}, 1000);

For explanation, comments have been added. Feel free to ask if you are having trouble understanding something.

Delete project

You are now able to create projects, now is the time to have the ability to delete them. So create a simple form that displays a list of all created projects in a dropdown:

<!-- form to delete project -->
<form method="POST" onsubmit="return taskObj.deleteProject(this);" style="display: contents;">
    <select name="project" class="form-control" style="display: initial; width: 200px; margin-left: 5px; margin-right: 5px;" id="form-task-hour-calculator-all-projects"></select>
    <input type="submit" class="btn btn-danger" value="Delete Project">
</form>

Then we need to create a function “deleteProject” in our “taskObj” object that will delete the project from local storage and refresh the projects and tasks UI.

// delete project
deleteProject: function (self) {

    // check if any project is selected
    if (self.project.value == "") {
        swal("Please select a project to delete");
        return false;
    }

    // ask for confirmation
    swal({
        title: "Are you sure?",
        text: "Deleting the project will delete its tasks too.",
        icon: "warning",
        buttons: true,
        dangerMode: true,
    })
    .then((willDelete) => {
        if (willDelete) {

            // remove from array and update local storage
            var projects = taskObj.getAllProjects();
            for (var a = 0; a < projects.length; a++) {
                if (projects[a].id == self.project.value) {
                    projects.splice(a, 1);
                    localStorage.setItem(taskObj.key, JSON.stringify(projects));

                    // re-load data
                    taskObj.loadAllProjects();
                    taskObj.showAllTasks();

                    break;
                }
            }
        } else {

            // reset project dropdown
            self.project.value = "";
        }
    });
    return false;
}

Applying CSS Styles

This may not be the major concern for you, but just to highlight the task when it is started and when it is completed, we are applying some CSS styles to it.

/* style when project is started */
.started {
    color: white;
    font-weight: bold;
    background: green;
    padding: 5px;
    border-radius: 5px;
}

/* style when project is completed */
.completed {
    color: white;
    font-weight: bold;
    background: greenyellow;
    padding: 5px;
    border-radius: 5px;
}

Congratulations ! You just created your own task management and time tracking script in Javascript. Feel free to add more features in it as per your needs.

[wpdm_package id=’1219′]

Convert date format to another – PHP, HTML

In this tutorial, we are going to teach you how to convert the date format from one to another using Javascript. We will also be adding, subtracting date by days.

Video tutorial

First, we will create a form to get the source and destination format from the user.

<form method="POST" onsubmit="return generateCode(this);">
    
    <div>
        <select name="source_format">
            <option value="">Enter source format</option>
            <option value="timestamp">Timestamp (123456789)</option>
            <option value="Y-m-d">Y-m-d (2020-08-30)</option>
            <option value="Y-m-d H:i:s">Y-m-d H:i:s (2020-08-30 14:30:18)</option>
            <option value="Y-m-d h:i:s A">Y-m-d h:i:s A (2020-08-30 02:30:18 PM)</option>
            <option value="Y-m-d h:i:s a">Y-m-d h:i:s a (2020-08-30 02:30:18 pm)</option>
            <option value="d M, Y">d M, Y (30 August, 2020)</option>
            <option value="D">D (Mon, Tue)</option>
        </select>
    </div>

    <div style="margin-top: 10px;">
        <select name="destination_format">
            <option value="">Enter destination format</option>
            <option value="timestamp">Timestamp (123456789)</option>
            <option value="Y-m-d">Y-m-d (2020-08-30)</option>
            <option value="Y-m-d H:i:s">Y-m-d H:i:s (2020-08-30 14:30:18)</option>
            <option value="Y-m-d h:i:s A">Y-m-d h:i:s A (2020-08-30 02:30:18 PM)</option>
            <option value="Y-m-d h:i:s a">Y-m-d h:i:s a (2020-08-30 02:30:18 pm)</option>
            <option value="d M, Y">d M, Y (30 August, 2020)</option>
            <option value="D">D (Mon, Tue)</option>
        </select>
    </div>

    <input type="submit" value="Generate Code" />

    <p id="output" style="display: none;"></p>
</form>

Then we will create a Javascript function that will write the PHP code to echo the date by converting it from source format to destination format.

<script>
    function generateCode(form) {

        var sourceFormat = form.source_format.value;
        var destinationFormat = form.destination_format.value;

        var html = "";
        var dateHtml = "";
        if (sourceFormat == "timestamp" && destinationFormat == "timestamp") {
            dateHtml = `echo $timestamp_value;`;
        } else if (sourceFormat == "timestamp") {
            dateHtml = `echo date('` + destinationFormat + `', $timestamp_value);`;
        } else if (destinationFormat == "timestamp") {
            dateHtml = `echo strtotime($date_value);`;
        } else {
            dateHtml = `echo date('` + destinationFormat + `', strtotime($date_value));`;
        }
        html += `&lt;?php<br />
            <span style='margin-left: 1em;'>&emsp;` + dateHtml + `</span><br />
        ?>`;
        
        document.getElementById("output").innerHTML = html;
        document.getElementById("output").style.display = '';

        return false;
    }
</script>

The resulting output will be displayed in a paragraph. Right now we are displaying a dropdown to select a date format. But what if the user wants to write a custom format ? So we will create 2 more fields for writing custom format for source and destination.

<input type="text" style="margin-left: 10px;" name="source_format_custom" placeholder="Custom source format" />

<input type="text" style="margin-left: 10px;" name="destination_format_custom" placeholder="Custom destination format" />

Then we need to update our custom date format variables if these fields have some value.

var sourceFormat_custom = form.source_format_custom.value;
if (sourceFormat_custom != "") {
    sourceFormat = sourceFormat_custom;
}

var destinationFormat_custom = form.destination_format_custom.value;
if (destinationFormat_custom != "") {
    destinationFormat = destinationFormat_custom;
}

Add/subtract from the date

Now we need to add or subtract months, years, or days from the new date format. For this, we will create 2 dropdowns to check if the user wants to add or subtract. And second to check if he wants to add/subtract days, months, or years.

<p style="text-align: center;">Add / subtract date (optional)</p>

<div>
    <select name="addsubtract_date">
        <option value="">None</option>
        <option value="add">Add</option>
        <option value="subtract">Subtract</option>
    </select>

    <select name="addsubtract_datetype">
        <option value="years">Years</option>
        <option value="months">Months</option>
        <option value="days">Days</option>
        <option value="hours">Hours</option>
        <option value="minutes">Minutes</option>
        <option value="seconds">Seconds</option>
    </select>
</div>

<input type="number" placeholder="Enter value" name="addsubtract_datevalue" />

Now when the form is submitted, we need to get the values of both these dropdowns.

var addsubtractdate = "";
if (form.addsubtract_date.value == "add") {
    addsubtractdate = "'+ " + form.addsubtract_datevalue.value + " " + form.addsubtract_datetype.value + "'";
} else if (form.addsubtract_date.value == "subtract") {
    addsubtractdate = "'- " + form.addsubtract_datevalue.value + " " + form.addsubtract_datetype.value + "'";
}

After that, we need to make changes in every echo statement in our Javascript code to handle this add/subtract feature.

if (sourceFormat == "timestamp" && destinationFormat == "timestamp") {
    if (addsubtractdate == "") {
        dateHtml = `echo $timestamp_value;`;
    } else {
        dateHtml = `echo strtotime(` + addsubtractdate + `, $timestamp_value);`;
    }
} else if (sourceFormat == "timestamp") {
    if (addsubtractdate == "") {
        dateHtml = `echo date('` + destinationFormat + `', $timestamp_value);`;
    } else {
        dateHtml = `echo date('` + destinationFormat + `', strtotime(` + addsubtractdate + `, $timestamp_value));`;
    }
} else if (destinationFormat == "timestamp") {
    if (addsubtractdate == "") {
        dateHtml = `echo strtotime($date_value);`;
    } else {
        dateHtml = `echo strtotime(` + addsubtractdate + `, strtotime($date_value));`;
    }
} else {
    if (addsubtractdate == "") {
        dateHtml = `echo date('` + destinationFormat + `', strtotime($date_value));`;
    } else {
        dateHtml = `echo date('` + destinationFormat + `', strtotime($date_value . ` + addsubtractdate + `));`;
    }
}

You can run the script now, and you will be able to generate the code for converting the date from one format to another. Moreover, you can also get the code for adding/subtracting days, months, or years from the resultant date. That’s how you can convert date format from one to another.

Learn how to calculate the time passed since the date in PHP and Javascript.

Calculate time passed since date – Javascript, PHP

Feel free to download the source code below.

[wpdm_package id=’1183′]

Laravel Blog (Website + Android app) with Admin Panel

A Laravel blog website along with an Android app is created with an admin panel. It uses the design template from https://bootstrapmade.com/. It has the following key features:

Google Adsense approved

The project is tested with Google Adsense and it was approved by Google for monetization. You just have to link with your Google account and you will start receiving money once you reach the Google payment threshold.

User side

  1. 70 built-in blog posts.
  2. Random quotations.
  3. Total users display.
  4. Custom advertisement to generate revenue.
  5. Share posts on Twitter and Facebook.
  6. Limit access to some features for registered users only.
  7. Registration with Email Verification.
  8. Secure Login.
  9. Comment on Post.
  10. Reply to the comment.
  11. Related Posts.
  12. Subscribe to the newsletter.
  13. Social Links.
  14. A section to sell items directly.
  15. Amazon affiliate links.
  16. Realtime Chat with admin (Firebase).
  17. Manage Profile.
  18. Change Password.
  19. Custom Advertisement.

Admin panel

  1. Dashboard Statistics.
  2. Add/Edit blog posts.
  3. Add/Edit items that sell directly.
  4. Manage Inbox.
  5. Manage Comments.
  6. Realtime Chat with users (Firebase).

Android app

We also developed an Android App for this project which your users can download from Google Play Store and read your blog posts from that app. Here is the demo of the Laravel blog android app:

Our TrustPilot reviews

TrustPilot-reviews
TrustPilot-reviews

Feedback pop-up bootstrap modal – Javascript, PHP & MySQL

In this article, we will teach you how to create a feedback pop-up bootstrap modal for users on the bottom right of the screen when the page is fully loaded. The pop-up modal will be created in bootstrap and will display a star rating and an input field to get the user’s feedback. Once submitted, it will display a thank you message and the user’s feedback will be saved in a database along with his IP address and browser information. If you are working on localhost, you might see the IP address as ::1. Don’t worry about it, on the live server it will save the actual IP address of the user.

Once the feedback is sent, the user will not see that pop-up again. If the user changes the browser or visits the site after a long time, then he will see that pop-up again.

Download jQuery, bootstrap, and font-awesome

First, you need to download jquery, bootstrap, and font-awesome. You can download all of them from the attachment below. Paste the CSS and JS files of bootstrap into your project. You may also need to copy-paste the jQuery JS file as well. Make sure to copy the font awesome fonts and CSS file as well.

Now you need to include these files in your project. Create <link> tag for adding CSS files and <script> tag for adding JS files.

<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="css/font-awesome.min.css" />

<script src="js/jquery-3.3.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>

Refresh the page, and if you did not see any error in the browser’s console window, then it means that it is included correctly.

Feedback modal/pop-up

Now, create a bootstrap modal for feedback. Give your modal a unique ID that will be used to show and hide the modal programmatically. The modal will have a heading, and one button to close the modal. And a body, where we will create the form to get the feedback.

<div class="modal custom" id="feedbackModal">
    <div class="modal-dialog">
        <div class="modal-content">

            <div class="modal-header">
                <h3>Rate your feedback</h3>
                <button type="button" class="close" data-dismiss="modal">
                    <span>×</span>
                </button>
            </div>

            <div class="modal-body">
                <form onsubmit="return saveFeedback(this);">
                    <div class='starrr'></div>

                    <div class="form-group" style="margin-top: 10px;">
                        <input type="text" name="feedback" class="form-control" />
                    </div>

                    <input type="submit" class="btn btn-primary pull-right" value="Submit" />
                </form>
            </div>
        </div>
    </div>
</div>

On form submit we will call an AJAX to save the feedback. A <div> to show stars, an input field to enter feedback in text, and a submit button. Right now, you will not see the stars, it will be done in the next section.

We need to show the modal on the bottom right, so apply some CSS styles to it.

#feedbackModal.modal.custom .modal-dialog {
    width: 50%;
    position: fixed;
    bottom: 0;
    right: 0;
    margin: 0;
}

This will give it some width for the form, set the position on the bottom right, and remove the margin from the bottom and right of the screen.

Star ratings (starrr)

Now, to create stars, we are going to use a library called starrr. Goto, this GitHub link to download the library and download it in your system. Inside this library, go to the distribution folder named dist and copy the CSS and JS files in your project’s CSS and JS folder separately. Now include its CSS and JS files and make sure to include the JS file after jQuery. I am putting it even after bootstrap JS.

<link rel="stylesheet" type="text/css" href="css/starrr.css" />
<script src="js/starrr.js"></script>

If you still see the stars missing, even though you have included font-awesome in your project, it is because you need to initiate this library using Javascript.

var ratings = 0;

window.addEventListener("load", function () {
    $(".starrr").starrr().on("starrr:change", function (event, value) {
        ratings = value;
    });

    if (localStorage.getItem("feedback_1") == null) {
        $("#feedbackModal").modal("show").on('hidden.bs.modal', function (e) {
            localStorage.setItem("feedback_1", "1");
        });
    }
});

Setting the default value of ratings to 0. We will initiate the library when the page is fully loaded. When the user selects any star, we are going to save its value in the ratings variable. Now I need to show the modal automatically but only if the user has not given his feedback. So I will use local storage for this purpose. In line #8, I am checking if the local storage has feedback_1 value. If not, then I am going to show the modal, and when this modal is closed by the user, I am going to save the feedback_1 value in local storage. So next time the user visits this page, the feedback_1 value will be found in local storage, thus it will not show the modal pop-up.

Refresh the page and now you will see that a feedback pop-up, that is created via a bootstrap modal, will be displayed automatically.

Submit form using AJAX

Now, we need to create a Javascript function named saveFeedback() which will be called when the form is submitted. This function will create an AJAX object, set its method to POST, and URL to the page that will save the feedback, and make the request asynchronous.

Then attach an event that will be called whenever the state of request is changed. The ready state will be 4 when the request is completed and a response is received. The status will be 200 if the response was OK and there was no error. You can simply show the response sent from the server using the responseText property. Then show the thank you message in the modal pop-up, and save the value in local storage.

If the request’s status is 500, it means there is an internal server error. In that case, you can view the error using the responseText property. To send the AJAX request, you need to create a FormData object using your form and append the ratings variable value in it, and then send the request and attach the form data object to it. return false at the end of the function will prevent the form from submitting and refreshing the page.

function saveFeedback(form) {
    var ajax = new XMLHttpRequest();
    ajax.open("POST", "save-feedback.php", true);

    ajax.onreadystatechange = function () {
        if (this.readyState == 4) {
            if (this.status == 200) {
                console.log(this.responseText);

                document.querySelector("#feedbackModal .modal-body").innerHTML = "Thank you for your feedback.";
                localStorage.setItem("feedback_1", "1");
            }

            if (this.status == 500) {
                console.log(this.responseText);
            }
        }
    };

    var formData = new FormData(form);
    formData.append("ratings", ratings);
    ajax.send(formData);

    return false;
}

Handle AJAX request

Now create a server file named save-feedback.php that will handle this request. As the requests need to be saved in the database, so we need to create a table in our database. I am creating a table named feedbacks. It will have 6 columns (id, IP, browser, ratings, feedback, created_at). The first will be auto increment ID. The second is an IP address whose data type is TEXT. Third is browser information, also TEXT. The fourth is ratings” data type DOUBLE. Next is feedback, also TEXT. And finally, created_at will store the date and time when the feedback is sent and its data type will be DATETIME.

You can find the SQL file in the attachment below.

Save feedback in MySQL

First, connect with the database. My database name is “tests”. Then get ratings and feedback from the request. You can get the user’s IP address using the PHP built-in global $_SERVER variable, and the associative index will be REMOTE_ADDR. You can get the user’s browser information by calling a PHP built-in function named get_browser() and it has a property named browser_name_pattern. Then run the INSERT query to save the record in the database. MySQL has a built-in function named NOW() that will return the current date and time of the server. PHP echo will send the response back to the client.

<?php

    $conn = mysqli_connect("localhost:8889", "root", "root", "test");

    $ratings = $_POST["ratings"];
    $feedback = $_POST["feedback"];
    $ip = $_SERVER["REMOTE_ADDR"];

    $browser = get_browser()->browser_name_pattern;

    mysqli_query($conn, "INSERT INTO `feedbacks`(`ip`, `browser`, `ratings`, `feedback`, `created_at`) VALUES ('" . $ip . "', '" . $browser . "', '" . $ratings . "', '" . $feedback . "', NOW())");

    echo "Done";

?>

Explanation

Initially, the feedback table will be empty. When you refresh the page, give ratings using stars, enter feedback and hit submit button, then a “Thank you” message will be displayed. When the response is successfully received from the server, close the modal and check your “feedbacks” table using phpMyAdmin. You will see a new row created in the database. If you refresh the page again, you won’t be able to see this modal because you have already given your feedback. You can try it in another browser. The first time you will see the pop-up on the bottom right corner. Once feedback is given, then you will not see that pop-up again. And your ratings will be saved in the database.

That’s how you can create a bootstrap modal that will be used to collect feedback from the users using a pop-up.

[wpdm_package id=’1030′]

Get URL query parameter value in Javascript

In this article, I am going to teach you, how you can get the URL query parameter value in Javascript. You can easily get the URL parameter in PHP using PHP’s built-in global $_GET array and pass the associative index as the name of the parameter. But getting that value in Javascript is a bit tricky. So I am going to show you a method that is very simple and will always work.

  1. Create a hidden input field.
  2. Give it a unique ID so it can be accessible in Javascript.
  3. Set its value as the variable you are receiving from the URL using the $_GET array.
  4. Array index will be the name of the parameter as in the URL

Suppose, you are receiving a parameter named “data” in your URL.

<input type="hidden" id="data" value="<?php echo $_GET['data']; ?>" />

Now, create a Javascript tag. You should execute all your Javascript code when the page is fully loaded. So, attach a “load” listener to the window object. You can also use the onload event but it will override the previous event. I have written a detailed post on the difference between onload event and “load” event listener.

window.addEventListener("load", function () {
    var data = document.getElementById("data").value;
    console.log(data);
});

The callback function in the second parameter of the addEventListener function will be called when the page is fully loaded. Now you simply need to get the value of this hidden input field using its ID and get the value attribute. I am logging the value of the data variable in the console.

Now, if you refresh the page then you will see this parameter (data) value in the console using Javascript. That’s how you can get the query parameter from the URL in your Javascript code.

Custom keyboard view for typing secure passwords using HTML and CSS

When people use someone else’s computer and try to log in to their account on any website, they are afraid that the other person might have installed a Keylogging software that tracks all the keys pressed, and hence they will know his password. But if you show a custom keyboard view for a password field on your website, it will create a whole new level of confidence in users towards your website.

So let’s learn how you can do this.

This is what we are going to create:

First, you need to create a password field, you probably also have a username or email field as well.

<input type="password" name="password" id="password">

Then create a table with a full keyboard view.

Numeric keyboard:

First, we will create a full numeric keyboard that appears on top of most laptops.

<table class="keyboard">
    <tr>
        <td>
            ~<br><span data-shift="~" data-initial="`">`</span>
        </td>
        <td>
            !<br><span data-shift="!" data-initial="1">1</span>
        </td>
        <td>
            @<br><span data-shift="@" data-initial="2">2</span>
        </td>
        <td>
            #<br><span data-shift="#" data-initial="3">3</span>
        </td>
        <td>
            $<br><span data-shift="$" data-initial="4">4</span>
        </td>
        <td>
            %<br><span data-shift="%" data-initial="5">5</span>
        </td>
        <td>
            ^<br><span data-shift="^" data-initial="6">6</span>
        </td>
        <td>
            &<br><span data-shift="&" data-initial="7">7</span>
        </td>
        <td>
            *<br><span data-shift="*" data-initial="8">8</span>
        </td>
        <td>
            (<br><span data-shift="(" data-initial="9">9</span>
        </td>
        <td>
            )<br><span data-shift=")" data-initial="0">0</span>
        </td>
        <td>
            _<br><span data-shift="_" data-initial="-">-</span>
        </td>
        <td>
            +<br><span data-shift="+" data-initial="=">=</span>
        </td>
        <td><span>delete</span></td>
    </tr>
</table>

We have written the normal key in the <span> tag and the shift key directly in <td>. Also, we have given data-shift and data-initial attribute so we can know which character to use when the shift key is pressed and which character to use when the shift key is released.

1st row (QWERTY):

This will be the very first row on your keyboard. You can compare it with your keyboard. Create a new <tr> tag in your table:

<tr>
    <td></td>
    <td data-shift="Q"><span>q</span></td>
    <td data-shift="W"><span>w</span></td>
    <td data-shift="E"><span>e</span></td>
    <td data-shift="R"><span>r</span></td>
    <td data-shift="T"><span>t</span></td>
    <td data-shift="Y"><span>y</span></td>
    <td data-shift="U"><span>u</span></td>
    <td data-shift="I"><span>i</span></td>
    <td data-shift="O"><span>o</span></td>
    <td data-shift="P"><span>p</span></td>
    <td>
        {<br><span data-shift="{" data-initial="[">[</span>
    </td>
    
    <td>
        }<br><span data-shift="}" data-initial="]">]</span>
    </td>

    <td>
        |<br><span data-shift="|" data-initial="\">\</span>
    </td>
</tr>

It also has data-shift and data-initial attributes that tell that the character will be uppercase when the shift key is pressed, and lowercase when the shift key is released.

2nd row (ASDF):

Similarly, we are going to create a second row of the keyboard.

<tr>
    <td></td>
    <td data-shift="A"><span>a</span></td>
    <td data-shift="S"><span>s</span></td>
    <td data-shift="D"><span>d</span></td>
    <td data-shift="F"><span>f</span></td>
    <td data-shift="G"><span>g</span></td>
    <td data-shift="H"><span>h</span></td>
    <td data-shift="J"><span>j</span></td>
    <td data-shift="K"><span>k</span></td>
    <td data-shift="L"><span>l</span></td>
    <td>
        :<br><span data-shift=":" data-initial=";">;</span>
    </td>

    <td>
        "<br><span data-shift='"' data-initial="'">'</span>
    </td>
</tr>

3rd row:

This will be the last row of the keyboard, usually right above the space bar.

<tr>
    <td id="key-shift"><span>shift</span></td>
    <td data-shift="Z"><span>z</span></td>
    <td data-shift="X"><span>x</span></td>
    <td data-shift="C"><span>c</span></td>
    <td data-shift="V"><span>v</span></td>
    <td data-shift="B"><span>b</span></td>
    <td data-shift="N"><span>n</span></td>
    <td data-shift="M"><span>m</span></td>
    
    <td>
        <<br><span data-shift="<" data-initial=",">,</span>
    </td>

    <td>
        ><br><span data-shift=">" data-initial=".">.</span>
    </td>
    
    <td>
        ?<br><span data-shift="?" data-initial="/">/</span>
    </td>
</tr>

Note that it has an ID attribute to the shift key, that is so we can highlight the shift key when it is pressed.

Applying styles

To make the keyboard view looks nice, we are going to apply some styles. But you can style the keyboard as you want.

.keyboard {
    margin-top: 10px;
    table-layout: fixed;
    width: 200px;
}
.keyboard,
.keyboard td {
    border: 1px solid black;
    border-collapse: collapse;
}
.keyboard td {
    padding: 25px;
    cursor: pointer;

    width: 30px;
    overflow: hidden;
}
.keyboard td:hover,
.keyboard td.active {
    background: black;
    color: white;
}

This will make your keyboard look nice and readable.

Javascript

The real power of this keyboard view comes with Javascript. Following is the complete Javascript code that will make this static keyboard view functional.

var isShift = false;

window.addEventListener("load", function () {
    var tds = document.querySelectorAll(".keyboard td");
    for (var a = 0; a < tds.length; a++) {
        tds[a].addEventListener("click", function () {

            /* get key node */
            var node = this.querySelector("span");
            if (node == null) {
                return;
            }

            if (node.innerHTML == "delete") {
                simulateBackspace(document.getElementById("password"));
            } else if (node.innerHTML == "shift") {
                /* toggle the isShift variable */
                isShift = !isShift;
                toggleShift();
            } else {
                /* check if it is an alphabet */
                if ((/[a-zA-Z]/).test(node.innerHTML)) {
                    document.getElementById("password").value += node.innerHTML;
                } else {

                    /* show initial value if the shift is off.
                     * show shift value if shift is on. */
                    if (isShift) {
                        document.getElementById("password").value += node.getAttribute("data-shift");
                    } else {
                        document.getElementById("password").value += node.getAttribute("data-initial");
                    }
                }
                
                if (isShift) {
                    isShift = false;
                    toggleShift();
                }
            }
        });
    }
});

function toggleShift() {
    /* make all alphabets capital or small based on new isShift value */
    var keys = document.querySelectorAll(".keyboard span");
    for (var b = 0; b < keys.length; b++) {
        /* keys must not be shift or delete */
        if (keys[b].innerHTML != "shift" && keys[b].innerHTML != "delete" && keys[b].innerHTML != "") {

            /* check if it is an alphabet */
            if ((/[a-zA-Z]/).test(keys[b].innerHTML)) {
                if (isShift) {
                    keys[b].innerHTML = keys[b].innerHTML.toUpperCase();
                } else {
                    keys[b].innerHTML = keys[b].innerHTML.toLowerCase();
                }
            }
        }
    }

    /* highlight the shift button if on. */
    if (isShift) {
        document.getElementById("key-shift").className = "active";
    } else {
        document.getElementById("key-shift").className = "";
    }
}

function simulateBackspace(element) {
    var start = element.selectionStart, end = element.selectionEnd, event;

    if (!element.setRangeText) { return; }
    if (start >= end) {
      if (start <= 0 || !element.setSelectionRange) { return; }
      element.setSelectionRange(start - 1, start);
    }

    element.setRangeText("");
    event = document.createEvent("HTMLEvents");
    event.initEvent("input", true, false);
    element.dispatchEvent(event);
}

That’s how you can create a fully functional custom numeric keyboard view. Comments have been added with each line for an explanation. You can customize this as much as you want. If you face any problems feel free to ask in the comment section below.

[wpdm_package id=’1173′]

Admin roles in admin panel – PHP & MySQL

Let’s say you have an admin panel of your website where you can manage your website’s data. Now you want to have a functionality where you can create sub-admins with access to limited features. For example, one admin can manage posts (add, edit and delete), another admin can manage customers, another admin can manage employees and so on. And they all will be managed by super admin. So we need to assign different admin roles to each admin.

In this article, we will be creating a sub admin to manage posts.

Create database table

Create a table for admins where we will have a column named “roles”, it’s type will be ENUM so you can specify the roles. No roles other than specified in ENUM will be accepted.

CREATE TABLE `admins` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `email` text NOT NULL,
  `password` text NOT NULL,
  `role` enum('all','manage_posts') NOT NULL
);

CREATE TABLE `posts` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `title` text NOT NULL,
  `created_by` int(11) NOT NULL,
  CONSTRAINT `fk_created_by_posts` FOREIGN KEY (`created_by`) REFERENCES `admins` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
);

Insert data in MySQL database

Super admin will be created manually and for once only. Give the role “all” to super admin:

INSERT INTO `admins` (`id`, `email`, `password`, `role`) VALUES
(1, 'admin@gmail.com', '$2y$10$e0qHrQw8irU1TPxjzfB2OOAQ/uUH/xq5jAP58f796jMAOLwEv2d9i', 'all')

Admin login form

You can generate password hash from here. First we will create a login form for all admins:

<?php
    // start session and connect with database
    session_start();
    $conn = mysqli_connect("localhost", "root", "root", "tutorials");
?>

<!-- check if admin is logged in -->
<?php if (isset($_SESSION["admin"])): ?>
    <!-- button to logout -->
    <p>
        <a href="?logout">Logout</a>
    </p>
<?php else: ?>
    <!-- form to login -->
    <form method="POST">
        <p>
            <input type="email" name="email" placeholder="Enter email" required>
        </p>

        <p>
            <input type="password" name="password" placeholder="Enter password" required>
        </p>

        <p>
            <input type="submit" name="login" value="Login">
        </p>
    </form>
<?php endif; ?>

This will show a button to logout if the admin is logged in and a login form if an admin is not logged in.

Log in the admin and start his session

Now we will write the code to login the admin and start his session:

// check if request is for login
if (isset($_POST["login"]))
{
    // get email and password
    $email = $_POST["email"];
    $password = $_POST["password"];

    // check if email exists
    $result = mysqli_query($conn, "SELECT * FROM admins WHERE email = '" . $email . "'");
    if (mysqli_num_rows($result) > 0)
    {
        // check if password is correct
        $admin = mysqli_fetch_object($result);
        if (password_verify($password, $admin->password))
        {
            // start session
            $_SESSION["admin"] = $admin;
            echo "<p>Logged in.</p>";
        }
        else
        {
            echo "<p>Wrong password.</p>";
        }
    }
    else
    {
        echo "<p>Email not found.</p>";
    }
}

This will first check if the email exists in the database. Then it will compare the hashed password with plain text from input field. If credentials are okay then it will save the admin object in session variable.

Logout admin

Now you will see the logout button.

// check if request is for logout
if (isset($_GET["logout"]))
{
    // remove from session and redirect back
    unset($_SESSION["admin"]);
    header("Location: " . $_SERVER["HTTP_REFERER"]);
}

When the logout button is clicked, we will remove this admin object from session variable and redirect the admin to the page where he came from. There are other methods to redirect the user to previous page and you will check those methods from here.

Add sub-admin form

Now if the logged-in admin is super admin then we will show him a form to add a new admin:

<!-- check if main admin -->
<?php if ($_SESSION["admin"]->role == "all"): ?>

    <!-- add admin form -->
    <h1>Add admin</h1>
    <form method="POST">
        <p>
            <input type="email" name="email" placeholder="Enter email" required>
        </p>

        <p>
            <input type="password" name="password" placeholder="Enter password" required>
        </p>

        <p>
            <label>Enter role</label>
            <select name="role" required>
                <option value="all">All</option>
                <option value="manage_posts">Manage posts</option>
            </select>
        </p>

        <p>
            <input type="submit" name="add_admin" value="Add admin">
        </p>
    </form>
<?php endif; ?>

This will ask for the admin’s email and password along with the role that you want to assign to him.

INSERT sub-admin in MySQL database

Now we will write the code to save his data in the database:

// check if request is for adding admin
if (isset($_POST["add_admin"]))
{
    // check if main admin
    if (isset($_SESSION["admin"]) && $_SESSION["admin"]->role == "all")
    {
        // get values
        $email = $_POST["email"];
        $password = password_hash($_POST["password"], PASSWORD_DEFAULT);
        $role = $_POST["role"];

        // check if email already exists
        $result = mysqli_query($conn, "SELECT * FROM admins WHERE email = '" . $email . "'");
        if (mysqli_num_rows($result) > 0)
        {
            echo "<p>Email already exists.</p>";
        }
        else
        {
            // save in database
            mysqli_query($conn, "INSERT INTO admins (email, password, role) VALUES ('" . $email . "', '" . $password . "', '" . $role . "')");
            echo "<p>Admin has been added.</p>";
        }
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

First, this will check that the logged-in admin must have an access to create sub-admin. Then it will get all the fields, it also converts the plain text password into hashed string. Then it checks if an admin with same email already exists, if not then it saves the data in database and display a success message.

Show all sub-admins to super admin

Now we need to show all sub-admins to super admin so he can know all his sub-admins along with their roles, and also an ability to delete any sub-admin. Below code should be written after the “Add admin” form:

<?php
    // show all admins
    $all_admins = mysqli_query($conn, "SELECT * FROM admins WHERE id != '" . $_SESSION["admin"]->id . "'");
    while ($admin = mysqli_fetch_object($all_admins)):
?>
    <p>
        <?php echo $admin->email . " - " . $admin->role; ?>

        <!-- button to delete admin -->
        <form method="POST" onsubmit="return confirm('Are you sure you want to delete ?');">
            <input type="hidden" name="id" value="<?php echo $admin->id; ?>">
            <input type="submit" name="delete_admin" value="Delete">
        </form>
    </p>
    <hr>
<?php endwhile; ?>

This will show all sub-admins to super admin only. When the delete button is clicked, it will first ask for confirmation. When confirm, it will submit the form. Now we need to handle the form submission in PHP:

// check if request is for deleting admin
if (isset($_POST["delete_admin"]))
{
    // check if main admin
    if (isset($_SESSION["admin"]) && $_SESSION["admin"]->role == "all")
    {
        // get value
        $id = $_POST["id"];

        // delete from database
        mysqli_query($conn, "DELETE FROM admins WHERE id = '" . $id . "'");
        echo "<p>Admin has been deleted.</p>";
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

This will simply check that the logged-in admin must be a super admin. Then it will delete the admin from database. When a sub-admin is deleted, all his created posts will also be deleted as well. If you want the sub-admin posts to stay after his removal, you need to remove the “ON DELETE CASCADE ON UPDATE CASCADE” clause from “posts” table during creation.

Sub-admins

Now we come to the sub-admin part. Sub admins can perform action based on their roles. For example, sub admin having role “manage_posts” can create, edit and delete posts. First we will create a form to add post:

<!-- check if admin has permission to manage posts -->
<?php if ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"): ?>
    <!-- form to add new post -->
    <h1>Add post</h1>
    <form method="POST">
        <p>
            <input type="text" name="title" placeholder="Enter title" required>
        </p>

        <p>
            <input type="submit" name="add_post" value="Add post">
        </p>
    </form>
<?php endif; ?>

This will check that the logged-in admin must either be a super admin or admin having role “manage_posts”. Now we need to handle its request in PHP:

// check if request is for adding post
if (isset($_POST["add_post"]))
{
    // check if admin has permission to manage posts
    if (isset($_SESSION["admin"]) && ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"))
    {
        // get values
        $title = $_POST["title"];
        $created_by = $_SESSION["admin"]->id;

        // save in database
        mysqli_query($conn, "INSERT INTO posts (title, created_by) VALUES ('" . $title . "', '" . $created_by . "')");
        echo "<p>Post has been added.</p>";
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

We need to validate the sub-admin role in server side as well. Get all fields from input fields, and logged in admin ID so we can know which sub-admin created that post. Then we will insert the data in database.

Now we need to show all posts of sub-admin created by him so he can perform further actions like updating or deleting post.

<?php
    // get all posts
    $all_posts = mysqli_query($conn, "SELECT * FROM posts WHERE created_by = '" . $_SESSION["admin"]->id . "'");
    while ($post = mysqli_fetch_object($all_posts)):
?>
    <p>
        <?php echo $post->title; ?>

        <!-- button to delete post -->
        <form method="POST" onsubmit="return confirm('Are you sure you want to delete ?');">
            <input type="hidden" name="id" value="<?php echo $post->id; ?>">
            <input type="submit" name="delete_post" value="Delete">
        </form>
    </p>
    <hr>
<?php endwhile; ?>

This will fetch all posts created by logged-in admin from database and display their titles along with a button to delete. When the delete form is submitted, it will ask for confirmation, once confirm it will submit the form. Now we need to handle the form request on server side:

// check if request is for deleting post
if (isset($_POST["delete_post"]))
{
    // check if admin has permission to manage posts
    if (isset($_SESSION["admin"]) && ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"))
    {
        // get value
        $id = $_POST["id"];

        // check if post is created by logged in admin
        $result = mysqli_query($conn, "SELECT * FROM posts WHERE id = '" . $id . "' AND created_by = '" . $_SESSION["admin"]->id . "'");
        if (mysqli_num_rows($result) == 0)
        {
            echo "<p>Sorry you cannot perform this action.</p>";
        }
        else
        {
            // delete from database
            mysqli_query($conn, "DELETE FROM posts WHERE id = '" . $id . "' AND created_by = '" . $_SESSION["admin"]->id . "'");
            echo "<p>Post has been deleted.</p>";
        }
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

Again it will check if the logged-in admin is either super admin or has a role to manage posts. Additionally, it will also check if the post he is trying to delete is created by him. That’s how it will be secured. If all validations are passed then it will simply delete the post from database.

That’s how you can create multiple admin roles and assign the roles by admin capability.

[wpdm_package id=’833′]