I'm creating a web app with Codeigniter, and I've created some edit forms (which pull current values from a mysql database). The user can edit the current database data by editing the data in the form.
What I want to do is perform certain actions if the user changes certain values. So, I don't just want to perform the action when a field has a certain value, but only at the point when the user changes the value and submits the form. (Specifically, when the user indicates that she's performed a certain task by changing a value from "no" to "yes", then I want to do things like set a timestamp for the completion of the task, etc.)
I've tried googling a solution, but I'm having trouble finding what I need. Can anyone point me in the right direction?
I haven't used CodeIgniter, but I've certainly done what you're doing in pure PHP-based sites.
I've followed two ways of thinking, in different projects.
Strategy #1: Multiple writes are cheap.
If a user clicks "Submit" rather than "Cancel", they've changed at least one field. So the cost of doing an UPDATE table SET name=%s,email=%s,gender=%s WHERE id=%d isn't much more than a simple UPDATE table SET gender=%s WHERE id=%d. If you're going to the expense of a WHERE and a write, making the write a few extra fields doesn't matter, especially with the frequency that it'll happen.
So: don't worry about it, just update everything with what you get back in the form. If you overwrite a field with the same data, it doesn't matter. When it comes down to it, you want your database to reflect everything that came back in the form, regardless of what was in the db before.
Strategy #2: Use a session variable.
If you've populated the form with current data, you've already likely pulled it into an array. So copy the array into $_SESSION, and compare the fields after the POST.
Strategy 1 is easier to program, but does use slightly more database CPU and bandwidth. Strategy 2 is has slightly less database impact at the expense of quite a bit more CPU used on your web server, and it's more prone to development error.
I still don't know which way is better, but the arguments for both seem valid. These days, I tend to go for whatever solution is the most simple. But I realize that it's easier to scale your web cluster than your database cluster, so if you're developing something that will be very large, it's probably better to put more effort into optimizing things in favour of your database rather than your web servers.
Note that if you're storing your session data in a database instead of temp files, then #2 is actually more costly in terms of database server impact.
You're saying that the users can edit entries from a database, so just send the record id as a hidden input field.
By the time the user submits the form, you retrieve the database record using the hidden field and make the necessary comparisons.
Btw, to prevent users from trying to modify other's records it's advisable to add a checksum to the id field that only you can verify, something that can be done using hash_hmac. Alternatively, you could verify the record ownership if they're logged in.
The only real solution here is knowing the initial value of the form input and then comparing it to the submitted value. You could pass the original value to the browser as a hidden form field and a slightly different name and then compare the two server-side though -- that should net you the desired effect if you don't know what the original value is already.
Related
I have a input form where people can add data about media to the SQL database via web. My question is - is there a way to confirm all the added data?
Example:
Unknown person fills out the form with spam and presses the submit button, I get a message about (or must confirm) the information before it is send to the sql database.
If you are properly building your SQL inserts such that you are protected against SLQ injection, etc., and your goal is to moderate the content, then this is as simple as adding a column to your table called "confirmed" with a default value of "0". Your viewing functions are then checking this value (if $confirmed)...) and no input is viewable until after you've set the confirmed flag. You can delete during moderation or even have a garbage collector cron job that deletes unconfirmed entries older than a preset time.
If you are doing this because you're worried about data getting into your database that can corrupt the database (SQL injection and other nefarious problems), then moderating the input is a very time-consuming solution. You should read up on preparing queries to avoid these problems.
There's three basic options here depending on your particular needs.
Insert into the table with a conditional flag that needs to be set by "confirming" the entries. This requires periodically purging unconfirmed entries after some amount of grace time, typically a day, to avoid cluttering up data with garbage. This can make searching difficult as you'll have to include this flag in all indexes in order to maintain performance and avoid showing non-confirmed data.
The second option is to take all the parameters, package them up as either a singular JSON injected in a hidden field, or each element rendered as a hidden field with identical names from the previous submission. The confirmation process will then re-post these parameters with an additional "confirmed" step.
The third option is to do this all client-side using JavaScript, jQuery, or some kind of front-end framework like Angular or React. This avoids the round-trip to the server, and the server does not have to do any additional work to verify that things are confirmed.
How to fix the security flow of this code:
<a href="http://localhost/mypay/admin/company/#"
data-pk="26" data-url="http://localhost/mypay/admin/company/update/profile"
id="name"
name="name"
data-type="text"
data-source="">dfg</a>
This uses x-editable [bootstrap] - http://vitalets.github.io/x-editable/
I am just curious how to improve my security for my application if ever the client changes the "PK" primary key of the data. Where should the ID must be placed or what other security measures I would create to prevent them in doing it?
I use: Bootstrap and Codeigniter.
You'll have to check on the server side if the current user is allowed to edit this entry. Do not try to secure the client side, it is impossible. People will always be able to edit data on their computer, it is your responsibility to control it when it arrives at a place you control.
We, PHP coders, don't always see that weakness in our code. We do a big fuss about identifying the users (pw, sessions, etc.) and we clean up the code to prevent SQL injection of all sort. OK ! But what if a perfectly identified user, who is legitimate to update HIS OWN record (say he's ID 24) just send back his update form to your app with all empty fields and ID=23, then ID=22, etc.
One could easily wipe out all your records in a few minutes (even faster with a little loop: for(id=1, id < 10000, etc.)
So, very good question INDEED !
Here is my solution (certainly not the best one):
When an identified user click on a button to edit HIS record, I do this:
I read his record (SELECT) to get the data.
I UPDATE that record putting two temporary informations in it: a timestamp and a random string (a 35 characters long string, like an MD5 but randomly created).
Then I send to that user an edit form with all data, including a hidden field {name="id" value="24"} and another hidden field {name"UpdatableOnlyBy" value="ks3kms36di7eur94k3n..."}
Whenever a form comes back by $_POST[] to update a record, say this #24, I accept to do it ONLY IF the {UpdatableOnlyBy} string equals the one temporarily stored in that record AND if the timestamp is not older than 20 minutes. Then, and only then, I do the UPDATE (which by the way sets that special {UpdatableOnlyBy} field back to Null).
Any data coming in FOR ANOTHER ID will not lead to any UPDATE, because the records aimed at do not have the same random {UpdatableOnlyBy} string.
I leave up to you to decide when and how to clean those two fields, if they are left with
old MD5-strings and timestamps. For my part, I have a cronjob at 0h25 that clears all those fields for yesterday and before. But in the past, I have kept those data to see how many records pulled for being updated where left "orphans" (edit forms who never came back...). In one of my app, it was less than 6 %.
Here is what my table looks like: http://jsfiddle.net/Draven/kGtx7/14/
Each header cell is clickable to be able to sort by that field and by ASC / DESC.
I want to be able to store that sorting option somewhere, so it get's remembered, but am not sure the best way to do that.
Here are the options I was thinking...
Making sort_field and sort_order fields in the users table
Making a new table that has their userid along with sort_field, and sort_order fields
Or Cookies, but I assume this is the worst option
I don't think we are clear on what you want, but I think you are wanting something like this:
Step 1: Run a query than will populate the sorting values into 2 session variables.
Step 2: Do something like this code.
$sortHeadClicked = $_SESSION['headClicked']
$sortReturnDirection = $_SESSION['returnDirection']
//TODO: validate data before query
if ($result = $mysqli->query("SELECT * FROM table ORDER BY $sortHeadClicked $sortReturnDirection))
{
//TODO: Get results
}
Step 3: Smile like your awesome
Alternatively, you could use some sort of sub select query.
NOTE: This is the simplistic logic so they won't be remember with this example. However, you can put the gets into session variables if you only want them to be remember temporarily
However, if you want these remembered permanently, you need two columns in your user's table where you would either puts them into sessions or use a subselect query
This is left up to interpretation, but each case would have its own uses.
1) Adding two fields to your users table will make the calls to retrieve these values easier but it is a much uglier approach to the problem than..
..2) Relational databases are built to be used as such. I'm not sure in terms of performance, but I do know that using the power of relational databases can make your db easier to navigate and understand/manipulate. While you may need some more complex calls (ie joins and whatnot), I believe the tradeoff is worth it.
and 3) Cookies are a very meh solution. They could be used in temporary cases, but if you are trying to save info for later, cookies can easily be deleted or not even enabled, at which point your site can suffer drastically.
Actually, using a cookie to store the user session id and then keeping session data in a database, flat file, or memcached is pretty common way to solve this. It would help to set up a reusable mechanism for this, like registry of sorts, that you can retrieve per user values at will. This only works if you have a user login of course. Otherwise there is no point in storing that data, as the users identity will be lost once they end the session (close the browser window). Most web apps will use cookies to identify you. If you delete that cookie, it forgets you and you are logged out.
Your first solution will suffer if you ever want to add another per user "preference" as you'd need to modify the underlying table.
Have a look at Zend Session for ideas if you are using PHP. If not the concepts still apply.
To save the sorting order to their profile, make the table inside a form and have the sortable field names be inputs. When they click one of the field names (sort by Location, for example), have the form's action run a PHP snip that updates a field in their profile on the database.
Alternately, if able to use ajax, you could simply add the database updating to an ajax call and skip the form.
I'm afraid that depends on your needs. How I see this problem:
good if you must share this settings between browsers, PCs, in
case if user delete cookies in browser. But it is not flexible - if
you will need to add another table, you will also add two additional
fields
the same as 1 in term of how it is shared between computers, browsers etc, but it is more flexible. You may add a column with table name easily.
If this setting is not so important and you may allow to loose setting in some cases. This solution is simplest, but it may not work for you
I have a very complicated form which has multiple "stages" in it.
Each stage has forms where the user adds indefinite numbers of rows. Each time the user clicks "continue" these forms save to the database (each "stage" has its own table in the database) and the next stage is shown.
I'm worried about users pressing "back" in order to get the the previous form (if they've made a mistake or something).
The only three ways I can think to solve this problem is:
Disallow people to press back. If they do send them straight to the first form to start again.
Check the database when the form page is requested. If there are rows already then delete those rows and start the user again on the form they're on.
[potential - This is going to be harder because I'm using codeigniter's form_validation library] Check the database when users press back. Get any current values out and put into the form as default values. Then on submit, remove any rows and replace with form values.
How do you guys do this kind of form? Its getting really ridiculous the amount of logic just for some forms! haha, is there a better way to do this? I suspect that number 3 above is the best option?
Thanks
I have used the third option on various occasions, though depending on the size of the form, this can certainly get overwhelming, not to mention the fact that I haven't used CodeIgniter's validation before.
Many issues arise from the first two options that should be avoided if possible [Including, but not limited to: Mistakes that aren't able to be corrected, hairy database logic, etc.], but the reward of having happy users is invaluable.
Hope this helps!
Mason
To give a visual summary of what users may think if they can't go back, I have included this image.
http://momentumshift.com/wp-content/uploads/2011/05/senior_woman_using_computer_mon142089.jpg
1 and 2 are both very bad for UX. 3 is your best bet, and depending on your db structure you might be able to update instead of delete and re-insert.
If you have a key, which i assume you do, in order to differentiate multiple form submissions, you can use something like insert on duplicate key update depending on you db engine.
I work on a market research database centric website, developed in PHP and MySQL.
It consists of two big parts – one in which users insert and update own data (let say one table T with an user_id field) and another in which an website administrator can insert new or update existing records (same table).
Obviously, in some cases end users will have their data overridden by the administrator while in other cases, administrator entered data is updated by end users (it is fine both ways).
The requirement is to highlight the view/edit forms with (let’s say) blue if end user was the last to update a certain field or red if the administrator is to “blame”.
I am looking into an efficient and consistent method to implement this.
So far, I have the following options:
For each record in table T, add another one ( char(1) ) in which write ‘U’ if end user inserted/updated the field or ‘A’ if the administrator did so. When the view/edit form is rendered, use this information to highlight each field accordingly.
Create a new table H storing an edit history containing something like user_id, field_name, last_update_user_id. Keep table H up-to-date when fields are updated in main table T. When the view/edit form is rendered, use this information to highlight each form field accordingly.
What are the pros/cons of these options; can you suggest others?
I suppose it just depends how forward-looking you want to be.
Your first approach has the advantage of being very simple to implement, is very straightforward to update and utilize, and also will only increase your storage requirements very slightly, but it's also the extreme minimum in terms of the amount of information you're storing.
If you go with the second approach and store a more complete history, if you need to add an "edit history" in the future, you'll already have things set up for that, and a lot of data waiting around. But if you end up never needing this data, it's a bit of a waste.
Or if you want the best of both worlds, you could combine them. Keep a full edit history but also update the single-character flag in the main record. That way you don't have to do any processing of the history to find the most recent edit, just look at the flag. But if you ever do need the full history, it's available.
Personally, I prefer keeping more information than I think I'll need at the time. Storage space is very cheap, and you never know when it's going to come in handy. I'd probably go even further than what you proposed, and also make it so the edit history keeps track of what they changed, and the before/after values. That can be very handy for debugging, and could be useful in the future depending on the project's exact needs.
Yes, implement an audit table that holds copies of the historical data, by/from whom &c. I work on a system currently that keeps it simple and writes the value changes as simple name-value string pairs along with date and by whom. It requires mandatory master record adjustment, but works well for tracking. You could implement this easily with a trigger.
The best way to audit data changes is through a trigger on the database table. In your case you may want to just update the last person to make the change. Or you may want a full auditing solution where you store the previous values making it easy to restore them if they were made in error. But the key to this is to do this on the database and not through the application. Database changes are often made through sources other than the application and you will want to know if this happened as well. Suppose someone hacked into the database and updated the data, wouldn't you like to be able to find the old data easily or know who did it even if he or she did it through a query window and not through the application? You might also need to know if the data was changed through a data import if you ever have to get large amounts of data at one time.