Create an Impressive Content Editing System with jQuery and PHP

It's been a while since we had a good development tutorial, so let's reawaken the tradition again and have some coding fun today! I'm going to show you how to use jQuery and PHP to build a content editing system that will allow you or your client to easily edit .html pages visually.

If this sounds like something you're interested in, read on. I hope you'll enjoy it!

How It's Going to Work

Of course, the first thing to do is set up a plan for how we're going to make this happen. Here's what I'm thinking:

  • We'll use PHP's file_get_contents() to load the selected HTML file into a textarea.
  • In order to keep the system secure, we'll limit editing capabilites to an array of files that we know are OK to edit.
  • We can then traverse and edit the file contents with jQuery and the open source WYSIWYM editor WYM Editor.

Our file structure is going to be really simple - just admin.php, the wymeditor package and jquery together in the demo directory. You're welcome to check out the demo and download the files before you get started:

DownloadDemo

The PHP

We're going to need a few things in the PHP file. First, we've got to have our HTML structure ready to drop the dynamic stuff into. That's going to look like this:

[html]
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">






    Site Editor



    [/html]

    Now we'll create an associative array where the keys are handles and the values are filenames. What this does is help keep us far out of the way of an RFI attack, since the includable files are limited to only what's inside the array. File accessing commands and GET variables are a very dangerous combination, so we've got to be extra wary of that. Once the array is ready, we're going to loop through it and create a link for each editable page so that when the user clicks on it, a GET request will be sent and that page will be loaded. We'll worry about loading the content in a minute, for now let's just take care of the links (this goes between the two ul tags):

    [php]
    < ?php $editable_pages = array( 'home' => 'index.html',
    'about' => 'about.html',
    'services' => 'services.html',
    'contact' => 'contact.html'
    );

    foreach($editable_pages as $page){
    echo "

  • " .$page. "
  • \n";
    }
    ?>
    [/php]

    What remains is check the GET variable 'page' to see if we're supposed to load a page, and if there's a value there to attempt to match it up with an element from our editable files array and load that file's contents into a textarea. If no page has been specified, we'll provide instructions on how to use the editor. We also need to wrap all of that in an if statement that executes only if the user is logged in. If you'd like to read about how to set up a login function like that, you're welcome to check out our previous tutorial on creating a secure login system with PHP. Anyways, here's the code (this goes just inside of the #editor form):

    [php]
    if( is_logged_in() ){
    /*If a page is specified and it's in our array of editable pages*/
    if(isset($_GET['page']) &amp;&amp; isset($editable_pages[$_GET['page']])){

    /*Get the filename from $editable_pages and the content from that file*/
    $page_content = file_get_contents($editable_pages[$_GET['page']]);

    /*If it worked*/
    if($page_content){
    /*obfuscate any textarea tags that would mess up the display and print content inside a textarea*/
    echo "","",$page_content). "
    ";

    /*Now create a save button*/
    echo "";
    }
    else{
    /*If it didn't work, display an error message*/
    echo "

    Uh-oh - that page was unable to be accessed. Please try again.

    ";
    }
    }
    }
    [/php]

    And the last thing we'll need to do is create a conditional (also after we know the user has been logged in) that will save submitted data from the textarea:

    [php]
    if(!empty($_POST['page-code'])){
    /*Get filename from $editable_pages and update file with new content*/
    $saved_file = file_put_contents($editable_pages[$_GET['page']],$_POST['page-code']);

    /*Display a response based on whether or not the changes were able to be saved*/
    if($saved_file){
    echo "

    Your page was updated successfully!

    ";
    }
    else{
    echo "

    uh -oh, your changes were unable to be saved.

    ";
    }
    }
    [/php]

    Pretty neat, huh? We now have the PHP part working - but who wants to edit a textarea filled with a bunch of code? There's not much point. That why we need jQuery, so let's see how it's going to work.

    The jQuery

    We've already loaded WYMEditor, so that makes our visual display/editing a breeze. We've just got to do a bit of go-between work with jQuery to get the editor loaded with the content it needs, and then to return that edited content to the page-code textarea. Of course, you could use another editor - something more lightweight and jQuery based like markItUp! For this tutorial though, WYMEditor is simple and powerful - just what we need.

    What we'll do is initiate the editor and then set up a trigger system so that when the user clicks 'save', the modified content will be retrieved from the editor and inserted back into the textarea. We'll also have to save all the head content and put it back in manually since the editor doesn't pay any attention to it and won't return that part of the code. Here's the script, complete with comments (remember to put this back up at the top inside the script tags):

    [javascript]
    $(function(){
    /*initiate the editor*/
    $("#page-code").wymeditor({
    skin: "minimal",
    skinPath: 'wymeditor/skins/minimal/',
    toolsHtml: '',
    classesHtml: ''
    });

    /*Create a trigger link to ensure that the textarea is updated before the form is submitted*/
    $("#page-code-save").hide().after("save");

    /*When the user clicks 'save': */
    $("#pseudo-submit").click(function(){
    /* Get anything before the body tag (which will be lost by the editor) */
    var before = $("#page-code").text().split("")[0] + " ";
    /* And anything after the body tag */
    var after = " " . $("#page-code").text().split("")[1];
    /*And the code inside the wymeditor frame*/
    var code = jQuery.wymeditors(0).xhtml();
    /*Now take it all and slap it into the page code textarea*/
    $("#page-code").text(before + code + after);
    /*Submit the form*/
    $("#editor").submit();
    return false;
    });
    });
    [/javascript]

    Update ... One More Feature!

    A couple of you folks asked about limiting the editing functionality to certain portions of the page, which is definitely a very useful feature. Thanks for suggesting it, Gregor and Patrick - I should've included this in my first version. Anyways, as I explained in the comments, the most straightforward way to go about this would probably be to add marker comments (like and ) in the html files that will mark the start and end of the part of the page you want to be editable.

    Here's a modified version of the Javascript code above that will make this work and even continue working in the absence of marker comments (like if you wanted one page to be fully editable and another to be partially editable):

    [javascript]
    $(function(){
    /*define your start and ending markers for the editable area*/
    var startEdit = "";
    var endEdit = "";

    /*If those markers are present, grab only the editable content and save the rest to put back in later*/
    if( $("#page-code").text().indexOf(startEdit) > 0 && $("#page-code").text().indexOf(endEdit)>0 ){
    var before = $("#page-code").text().split(startEdit)[0] + startEdit;
    var after = endEdit + $("#page-code").text().split(endEdit)[1];
    var editableContent = $("#page-code").text().split(startEdit).pop().split(endEdit)[0];
    }
    /*if they aren't, we'll assume the whole page is editable*/
    else{
    var before = $("#page-code").text().split("").shift() + " ";
    var after = " " + $("#page-code").text().split("").pop();
    var editableContent = $("#page-code").text().split("").pop();
    }

    /*update the textarea with only the editable content*/
    $("#page-code").text(editableContent);

    /*load the editor*/
    $("#page-code").wymeditor({
    skin: "minimal",
    skinPath: 'wymeditor/skins/minimal/',
    toolsHtml: '',
    classesHtml: ''
    });

    /*add a trigger link for submitting the form*/
    $("#page-code-save").hide().after("save");

    /*when it's clicked, put the code all back together and sumbit the form*/
    $("#pseudo-submit").click(function(){
    $("#page-code").text(before + jQuery.wymeditors(0).xhtml() + after);
    $("#editor").submit();
    return false;
    });
    });
    [/javascript]

    And that's it - pretty clean, effective and efficient if you ask me! I've implemented the controlled editing feature in the index.html file of the demo, so you can see how it works on that page if you want, and feel free to let me know if you have any questions.

    Wrapping Up

    And there you go! Of course, there are a billion and 1 ways to accomplish something like this, and in many cases it would be more advantageous just to set the site up with a CMS rather than using a jQuery/PHP system like this. The one we've just made, though, is small and fast (besides being degradable and a lot of fun to make!) and will work very well for a smaller static site where a full CMS would be overblown. Again, you're welcome to check out the demo and/or download a working set of files:

    DownloadDemo

    Hopefully you've enjoyed reading this tutorial as much as I enjoyed writing it, and I'd love to hear your opinions and any thoughts on how it could be improved!

    27 Comments

    1. Sung March 4, 2010
    2. Nick Parsons March 4, 2010
    3. Silviya March 7, 2010
    4. HD March 7, 2010
    5. Gregor March 7, 2010
    6. Dilly March 8, 2010
    7. Nick Parsons March 8, 2010
    8. Patrick March 9, 2010
    9. Andrew Champ March 10, 2010
    10. Jared March 11, 2010
    11. Jan March 12, 2010
    12. Anja May 4, 2010
    13. Anja May 4, 2010
    14. dev June 19, 2010
    15. dev June 19, 2010
    16. Janosch July 21, 2010
    17. Janosch July 21, 2010
    18. kreuzritter July 21, 2010
    19. Cory Marsh July 22, 2010
    20. Dimitris August 26, 2010
    21. cut out people December 11, 2010
    22. Rob Macintosh January 15, 2011
    23. Helen Neely August 9, 2011
    24. Mika @ muscle warfare August 9, 2011
    25. Churro August 20, 2011
    26. Qaysar Akbar April 11, 2012
    27. Dustin August 13, 2013

    Leave a Reply