1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Creating an add-on called Change Date

Discussion in 'XenForo Development Discussions' started by AndyB, Sep 24, 2013.

  1. AndyB

    AndyB Well-Known Member

    Description:

    The purpose of this add-on is the allow superAdmins to move a post within a thread. For example if you want to move post #3 to be after post #5.

    To change the date of a post, click the Change Date link located below each post.

    pic001.jpg
    When the superAdmin clicks the Change Date link, the following overlay is displayed:

    pic002.jpg
    Enter the date and time you would like the post to be and click the Submit button.
     
    Vahab, Veer, Pereira and 4 others like this.
  2. AndyB

    AndyB Well-Known Member

    This tutorial covers the following:

    1. Create an add-on in Xenforo
    2. Create a controller
    3. Create and use a Listener
    4. Understand a route and access data from it (post_id)
    5. Create a custom template and assign it to the add-on
    6. Use {xen:link} to create links with the correct route
    7. Use {$...} to display a variable in a template
    8. Create a <form> in a template to submit data to the Controller
    9. Create a Template Modification
    10. Use basic security functions (isSuperAdmin, assertPostOnly)
    11. How to access functions from other files (classes)
    12. Access Xenforo's model functions
    13. How to return a custom template from the controller
    14. How to create an AJAX Overlay with a custom template
    15. Return data from the controller so it can be used in templates
    16. How to access data from a <form> field
    17. How to get additional data from a datawriter after you saved it ($threadId)
    18. Use the datawriter to change an existing database entry
    19. How to return a redirect with a message ("Post Date Changed")
     
    Hugilore111, maszd, TheBigK and 3 others like this.
  3. AndyB

    AndyB Well-Known Member

    Understanding the different parts of a link:

    http://www.site.com/forums/posts/1234/changedate

    1. The /posts/1234/changedate part of the link is called the route.

    2. Xenforo always listens to routes. For the route "posts", there is the controller XenForo_ControllerPublic_Post, which is always executed if a link with /posts/ is executed.

    3. So to do something with posts like changing a post date, one has to first create a link with this route "/posts/", after the user clicks on this link, the file library/Xenforo/ControllerPublic/Post.php is executed.

    4. The Controller has an action for each /posts/1234/this_is_the_action_function. Like actionChangedate or actionLike or actionReply.

    5. So if you have a link like /posts/1234/changedate, there is always a function actionChangedate.

    6. Adding an action function for changedate is referred to as "extending the controller".

    7. Every route is set with a basic variable that is equal to the /1234/ in this example. For posts, it's post_id. For threads, it's thread_id. For forums, it's forum_id and so on.
     
    Sami Jalal and Amaury like this.
  4. AndyB

    AndyB Well-Known Member

    The first step is to create a new add-on:

    Admin CP -> List Add-ons -> Create Add-on

    pic003.jpg
     
    Hugilore111 and Amaury like this.
  5. AndyB

    AndyB Well-Known Member

    Create your add-on directories and files:

    libarary
    --Andy
    ----ChangeDate
    ------ControllerPublic
    --------Post.php
    ------Listener.php

    Initial contents of the /library/ChangeDate/ControllerPublic/Post.php file.

    PHP:
    <?php

    class Andy_ChangeDate_ControllerPublic_Post extends XFCP_Andy_ChangeDate_ControllerPublic_Post
    {
       public function 
    actionChangedate()
       {
         
    $post['post_id'] = $this->_input->filterSingle('post_id'XenForo_Input::UINT);

         
    $viewParams = array('post' => $post);
       }
    }
    ?>
     
    Amaury likes this.
  6. AndyB

    AndyB Well-Known Member

    The Listener.php file:

    Complete contents of the /library/Andy/ChangeDate/Listener.php file.

    PHP:
    <?php

    class Andy_ChangeDate_Listener
    {
       public static function 
    Post($class, array &$extend)
       {
         
    $extend[] = 'Andy_ChangeDate_ControllerPublic_Post';
       }
    }

    ?>
     
    Last edited: Sep 24, 2013
    Amaury likes this.
  7. AndyB

    AndyB Well-Known Member

    Create the code event listener:

    Admin CP -> Development -> Code Event Listeners -> Create New Code Event Listener

    pic004.jpg
     
    Amaury likes this.
  8. AndyB

    AndyB Well-Known Member

    Testing what we have done so far:

    At this point you are done extending the post controller. Entering in a URL like this into your browser will call your Post.php file:

    http://www.site.com/forums/posts/1858732/changedate

    You can verify this by adding an echo to the /library/Andy/ChangeDate/ControllerPublic/Post.php file.

    PHP:
    <?php

    class Andy_ChangeDate_ControllerPublic_Post extends XFCP_Andy_ChangeDate_ControllerPublic_Post
    {
       public function 
    actionChangedate()
       {
         
    // if you are not a super admin, return
         
    if (!XenForo_Visitor::getInstance()->isSuperAdmin())
         {
           return;
         }

         
    // echo "test" to verify code is working
         
    echo 'test';
       
         
    // get post_id from route, example /forums/posts/1859225/changedate
         
    $postId $this->_input->filterSingle('post_id'XenForo_Input::UINT);

     
    Last edited: Sep 24, 2013
    Hugilore111 and Amaury like this.
  9. AndyB

    AndyB Well-Known Member

    Create a template:

    Create a new Master template called andy_changedate as seen in post #1.

    Admin CP -> Appearance -> Templates -> Create New Template

    Code:
    <div class="formOverlay">
    
    <xen:title>Change date for post #{xen:calc '{$post.position} + 1'}</xen:title>
    
    <form action="{xen:link 'posts/changedate-save', $post}" method="post" class="xenForm AutoValidator" data-redirect="on">
    
    <fieldset>
    
    <dl class="ctrlUnit">
      <dt><label>Enter new date:</label></dt>
      <dd><input type="text" name="new_post_date" size="33" value="{$post.formatted_date}" class="textCtrl titleCtrl"></dd>
    </dl>
    
    <dl class="ctrlUnit submitUnit">
      <dt></dt>
      <dd><input type="submit" value="Submit" class="button primary" accesskey="s" /></dd>
    </dl>
    </fieldset>
    
    <input type="hidden" name="_xfToken" value="{$visitor.csrf_token_page}" />
    </form>
    
    </div>
    
    
     
    Amaury likes this.
  10. AndyB

    AndyB Well-Known Member

    Template Modification:

    The next step is to create a Template Modification for the post template. This will put the Change Date link at the bottom of each post.

    Admin CP -> Appearance -> Template Modifications -> Create Template Modification

    pic005.jpg
     
    Amaury likes this.
  11. AndyB

    AndyB Well-Known Member

    The complete Post.php file:

    Complete contents of the /library/Andy/ChangeDate/ControllerPublic/Post.php file.

    PHP:
    <?php

    class Andy_ChangeDate_ControllerPublic_Post extends XFCP_Andy_ChangeDate_ControllerPublic_Post
    {
       public function 
    actionChangedate()
       {
         
    // if you are not a super admin, return
         
    if (!XenForo_Visitor::getInstance()->isSuperAdmin())
         {
           return;
         }
        
         
    // get post_id from route, example /forums/posts/1859225/changedate
         
    $postId $this->_input->filterSingle('post_id'XenForo_Input::UINT);

         
    // get all post data variables
         
    $post $this->getModelFromCache('XenForo_Model_Post')->getPostById($postId);

         
    // format date and time for use in template   input
         
    $userLanguage XenForo_Visitor::getInstance()->getLanguage();
         
    $timeFormat $userLanguage['date_format'] . ' ' $userLanguage['time_format'];
         
    $post['formatted_date'] = date($timeFormat$post['post_date'] + XenForo_Locale::getTimeZoneOffset());

         
    // display andy_changedate template
         
    $viewParams = array('post' => $post);
         return 
    $this->responseView('Andy_DateChange_ViewPublic_Post','andy_changedate',$viewParams);
       }

       public function 
    actionChangedatesave()
       {
         
    // make sure data comes from $_POST
         
    $this->_assertPostOnly();

         
    // if you are not a super admin, return
         
    if (!XenForo_Visitor::getInstance()->isSuperAdmin())
         {
           return;
         }

         
    // get post_id
         
    $post['post_id'] = $this->_input->filterSingle('post_id'XenForo_Input::UINT);

         
    // get new_post_date from overlay
         
    $newPostDate $this->_input->filterSingle('new_post_date'XenForo_Input::STRING);

         
    // convert $newPostDate to unix timestamp
         
    $dateline strtotime($newPostDate ) - XenForo_Locale::getTimeZoneOffset();

         
    // make sure $dateline has a value
         
    if (!$dateline)
         {
           return 
    $this->responseError('Incorrect date format entered');
         }

         
    //########################################
         // start database operations
         //########################################

         // update xf_post table (post_date)
         
    $dw XenForo_DataWriter::create('XenForo_DataWriter_DiscussionMessage_Post');
         
    $dw->setExistingData($post['post_id']);
         
    $dw->set('post_date'$dateline);
         
    $dw->save();

         
    // update xf_post table (position) for each post in the thread
         // also updates other tables with first post and last post information
         
    $threadId $dw->get('thread_id');
         
    $dw XenForo_DataWriter::create('XenForo_DataWriter_Discussion_Thread');
         
    $dw->setExistingData($threadId);
         
    $dw->rebuildDiscussion();
         
    $dw->save();

         
    //########################################
         // return with response redirect
         //########################################
        
         
    return $this->responseRedirect(
         
    XenForo_ControllerResponse_Redirect::SUCCESS,
         
    XenForo_Link::buildPublicLink('posts'$post),'Post Date Changed');  
       }
    }

    ?>

     
    Last edited: Sep 24, 2013
    Lil.Tee and Amaury like this.
  12. AndyB

    AndyB Well-Known Member

    The Change Date add-on is complete:

    The Change Date add-on is complete and ready to use.
     
    Amaury and borbole like this.
  13. borbole

    borbole Well-Known Member

    Nice work Andy. Have you considered to package it up and release it as a mod in the RM?
     
  14. AndyB

    AndyB Well-Known Member

    maszd, Vahab, Sheratan and 1 other person like this.
  15. Marcus

    Marcus Well-Known Member

    20. Extending the class by giving a "hint" (only available from xenforo 1.2.x and above). It's faster than the older method with if/else, switch in the Listener function.
     
  16. 0ptima

    0ptima Well-Known Member

    Thanks for posting this!
     

Share This Page