Please upgrade here. These earlier versions are no longer being updated and have security issues.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

PLUGIN : Animated forum "tour".

MrVladMrVlad New
edited September 2015 in Vanilla 2.0 - 2.8

Hi gents !

As you may know, my previous attempt to create a vanilla plugin has been quite unsuccessful, but I did not give up hope yet ^^ !

I switched from smf to vanilla 2 months ago and my community seems to enjoy it, however, some people are still confused with a few of the new functionnalities.

So, I thought of developping a sort of "guided tour" plugin in order help our newest members, and I attached a gif to show you the result (working prototype on my local installation ... yeah I know it's a mess).

As you may see despite the size of the animation, there is a "tutorial mode" triggered when the user press the "Besoin d'aide" (Need help) button. The background is darkened and some modals are triggered to describe various elements of the forum.

What I've done so far (the easy stuff):

  • js, html & css (all responsive yeaaaaah !).
  • created a plugin to enable/disable tutorial mode. With profile extender, I created a dropdown (yes/no) so user can display/hide the tutorial button.

Why I need help (the hard stuff):

  • As for now, my plugin loads an html template which is (to sum up quickly) a ul **list containing 3 **li elements. Each element is in fact a step in the tutorial you can see above (little popups).
  • I'd like to make things more dynamic so I thought of creating an admin interface. Using @R_J useful article, I created a setting page with some inputs for each of the three steps ... but I can't figure how to use their values :/

What I'd like to do (the awesome yet harder stuff):

  • @hgtonight's application yaga features a great system where users can create badge and other entities. What I'd like to do is to create an interface where admins can create custom tutorials.
  • The structure would be :
    • gdn_tutorials -> contains tutorial objects
    • each tutorial objects has a number of properties (nb of steps, where to display the tutorial, enabled/disabled)
    • each steps has a number of fields, like 'title', 'description', 'picture', etc.

I used symfony to create a collection of forms once, but now it's way harde ... anyway to sum up, I need to create an interface where I can add tutorials (elements) and another interface to add steps inside a tutorial .... pfeeeew !

I (tried to) start from the yaga code, but to be honest I don't understand a damn thing (well ... a little bit actually) even if it is very well written.

Any help from you guys would be awesome ! Btw: don't hesitate to give your advice on the plugin itself !

«1

Comments

  • @MrVlad said:
    What I've done so far (the easy stuff):

    • js, html & css (all responsive yeaaaaah !).

    If that was easy, the rest couldn't be too hard!

    @MrVlad said:
    Why I need help (the hard stuff):

    • As for now, my plugin loads an html template which is (to sum up quickly) a ul **list containing 3 **li elements. Each element is in fact a step in the tutorial you can see above (little popups).

    What's your question?

    • I'd like to make things more dynamic so I thought of creating an admin interface. Using R_J useful article, I created a setting page with some inputs for each of the three steps ... but I can't figure how to use their values :/

    You can access config values by using c('Your.Plugin.Key').

    Maybe you have to show more of your plugin (code) so that we can help more.

    What I'd like to do (the awesome yet harder stuff):

    • hgtonight's application yaga features a great system where users can create badge and other entities. What I'd like to do is to create an interface where admins can create custom tutorials.
    • The structure would be :
      • gdn_tutorials -> contains tutorial objects
      • each tutorial objects has a number of properties (nb of steps, where to display the tutorial, enabled/disabled)
      • each steps has a number of fields, like 'title', 'description', 'picture', etc.

    I used symfony to create a collection of forms once, but now it's way harde ... anyway to sum up, I need to create an interface where I can add tutorials (elements) and another interface to add steps inside a tutorial .... pfeeeew !

    This one will help you to create a page: http://vanillaforums.org/addon/howtovanillapage-plugin and this one how to use a form: http://vanillaforums.org/addon/howtoformvalidate-plugin

  • Thanks for your answer :D ! I think I'll dig into the code you just sent me before posting mine, just to see if I can figure it out by myself !

  • @MrVlad Are you using a JS library for your tutorial functions?

    I started work on a feature guide a while ago (2 years...damn). If the code is of any use to you, use it: https://github.com/hgtonight/Plugin-FeatureGuide

    If your plugin works well, I would love to integrate it into my addons. A quick step through for even mildly complex addons would do wonders for usability.

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • @R_J : I carrefuly read the code of the two addons you sent me. It's quite clear, but now I'm even more confused (... yup). Which method should I use to create a form on the admin side (and here only) ?

    • The one you mentionned in a previous post seems to be easier to implement, but is it suitable for my case ?
    • What are the benefits of the methode descibed in howtoformvalidate ?

    I won't post my code here in order to avoid flooding the forum, but I attached a zip with what I have so far (except css/js). To sum up : The plugin is using a module to render the tutorial. The module itself calls a view (php file) so I can easily customize what I want to render. The code must be messy as hell but once again, I'm a php beginner ...

    My main issue for now is to connect the admin data fields with the content of the view. I had no success using the "c('Your.Plugin.Key')" method ...

    @hgtonight : i'm using jquery ... and that's it (I like to keep things as simple as I can). You can use it whenever you want if it manage to make it work ... well the front end part is doing ok but there's no customization at the moment, except if you edit the assets directly.

    Thx for your time guys !

  • peregrineperegrine MVP
    edited September 2015

    in your zip....

    just the tip of the iceberg.

    since you are using vanilla 2.1 - you have to cognizant of a few things.

    • vanilla crew started changing cases on variables, etc between the 2 versions.
    • you need to be consistent
    • use var_dump or print_r or echo or logging to determine things.

    e.g.

    you have
    public function Base_Render_Before($sender, $Args) {

    and

    public function Base_GetAppSettingsMenuItems_Handler($Sender) {

    this is a sure-fire way to confuse yourself.

    $sender is the new way naming convention.

    $Sender was the old way

    using both ways is inconsistent.

    better to choose one or the other throughout.


    // I thought it was supposed to disable the plugin for the admin pannel ... but it doesn't work.
    if ($sender->masterView != 'admin') {

    well you should test by dumping sender.

    public function Base_Render_Before($sender, $Args) {
    // I thought it was supposed to disable the plugin for the admin pannel ... but it doesn't work.

    // debug so you can learn what's happening.
    var_dump($sender->MasterView);
    var_dump($sender->Menu);

    if ($sender->Menu && $sender->MasterView != 'admin') {

    // don't use this, use above.
    if ($sender->MasterView != 'admin') {


    include_once(PATH_PLUGINS.DS.'ForumTour'.DS.'class.forumtourmenumodule.php');
    $SimplePagesMenuModule = new ForumTourMenuModule($sender);
    $sender->AddModule($SimplePagesMenuModule);

    instead maybe ...

    // you don't need include
    include_once(PATH_PLUGINS.DS.'ForumTour'.DS.'class.forumtourmenumodule.php');
    /// perhaps be consistent or more consistent with naming.
    // not SimplePagesMenu maybe something more related to plugin name
    $FTMenuModule = new ForumTourMenuModule($sender);
    $sender->AddModule($FTMenuModule);

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • MrVladMrVlad New
    edited September 2015

    Wow thx a lot ! My bad for the simplepages typo, I looked into the code of this plugin to see how things were handled and I must have forgotten to edit the variables !

  • In the plugin info include a license to avoid irritations

    class ForumTour extends Gdn_Plugin { must be class ForumTourPlugin extends Gdn_Plugin {

    Check if the module should be displayed in the plugin and only load the module for users who want to see it

    In your module you create a variable $Session just to get the user ID. Depending on an advice I got recently, you should avoid creating unnecessary variables => $UserID = Gdn::Session()->UserID;

    I would try to load the view with something like $Sender->Render($this->GetView('homeTutorial'));

    You do not need the T() function for LabelCode since that text will be passed to that function later on automatically => 'LabelCode' => 'Titre de la fenêtre 1',


    What I'd like to do is to create an interface where admins can create custom tutorials.

    >

    I think the configuration module isn't the right way to do this, but certainly it can be done...

        public function settingsController_aah_create($sender) {
            $sender->permission('Garden.Settings.Manage');
            $sender->title(t('Aah Settings'));
            $sender->addSideMenu('dashboard/settings/plugins');
            $sender->description('Aah Description');
    
            $configurationModule = new ConfigurationModule($sender);
    
            $configValues = c('Aah');
            $initValues = array();
            $counter = 1;
            do {
                $initValues['Aah.Title'.$counter] = array(
                    'Control' => 'TextBox',
                    'LabelCode' => 'Title '.$counter,
                    'Options' => array('maxlength' => '50')
                );
                $initValues['Aah.Description'.$counter] = array(
                    'Control' => 'TextBox',
                    'LabelCode' => 'Description '.$counter,
                    'Options' => array('maxlength' => '50')
                );
    
                if ($configValues['Title'.$counter] != '' && $configValues['Description'.$counter] != '') {
                    $counter++;
                    $loop = true;
                } else {
                    $loop = false;
                }
            } while ($loop);
    
            $configurationModule->initialize($initValues);
            $configurationModule->renderAll();
        }
    

    That way, whenever the settings page is opened, there will be a new and empty input box

  • @R_J : your advice was SO USEFUL !!!

    Here is the updated version, ready to be activated (well at least it won't crash your forum if you test it), looking better isn't it ??! You can add a step in the dashboard if all the fields of the previous one are filled. Strangely, when I hit the "save" button on the dashboard pannel, page doesn't reload and I have to press f5, which triggers an alert "are you sure to send the form data again ?" ... damned.

    Another question if I may, is there a way to customize how inputs are displayed on the dashboard interface ? For now, each field is set in a li element of an ul list, but I was hopping to make things a bit simpler by changing the html structure ... I did not find anything useful related to this topic and the method I'm using.

    Well, enough talking for me, just let me know what you think about the plugin (and my code, please tell me if I made some silly mistakes) !

    Cheers !

  • peregrineperegrine MVP
    edited September 2015

    It's a creative plugin. it looks pretty nice.

    Although the config interface for adding items is workable. it is not forgiving.

    some thoughts.

    for example if you enter 5 items

    then blank the title in item 1 and save it, the other items get reinitialized on the next load.

    do you want some validation????

    what if some one has 5 items, there is no real easy way to delete the first item, without renumbering the config.

    As far as better html design on setting screen, the only way to do it that I know of is just to create your own setting screen view, as hundreds of other plugins have used before the "trend" not to create your own settings view. I know its not the craze or the trend. But I personally don't see it as a bad thing and wonder why not having one is the considered the greatest thing since sliced bread (it just lessens your flexibility).

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • peregrineperegrine MVP
    edited September 2015


    you might want to play around with the FAQ application for some presentation ideas within the settings dashboard. (easy to add delete and change).

    and look at the visual layout of the FAQ settings screen from the dashboard.

    make sure after you add the application - you go to roles and permissions and give yourself all the manage edit delete etc permissions for the FAQ application so you can see how it works.

    http://vanillaforums.org/addon/faq-application

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • Thx ! This looks really close to what I need ! I'll check that asap !

  • Ok a few questions (the structure of the application seems to be different from what I've seen so far) :

    • Do I have to create a new model, controllers and so on in order to achieve the same result ? If so ... farewell admin dashboard !
    • To render the form, I'm using :

      $configurationModule->initialize($initValues);
      $configurationModule->renderAll();

    Where $initValues is an array containing data for each field, for example :

    $initValues['ForumTour.YpositionType'.1] = array(
    'Control' => 'radiolist',
    'Items' => array('px', '%'),
    'LabelCode' => 'Type de positionnement sur Y',
    'Default' => '0',
    );

    Now, it renders elements inside an

    and I'd like to wrap them inside a table for example ... FAQ application does this, but in a way I don't understand quite well yet ... Is there anyway to change configurationModule rendering method ?

    "then blank the title in item 1 and save it, the other items get reinitialized on the next load." ... yeah I know ... but I have no clue how to prevent this. The thing that bothers me right now is trying to make the page refresh when I press "save".

  • peregrineperegrine MVP
    edited September 2015

    as far as my comment about FAQ app.

    I was talking visual layout. (appearance).

    for settings ....

    take a look at some of x00's karma plugin as another example of settings screen.

    or even addmenuitem plugin for static.

    or pockets plugin.

    and perhaps add an enable checkbox to make items active or not for easier changing

    As far as writing an app (simple apps) there are some tutorials I believe, but it is not necessary to do to write a more robust settings screen.

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

  • All right, here we go again ! So ... I had a look to the pocket plugin, and it's definitely what I'm looking for ! BUT ... damned it's hard to undertand.

    So, I have successfuly created a view to display steps in a table, and a button which trigger a modal with a form in it. What I'm struggling with now is how to save and fetch data, do I have to create a new Gdn entry to store all of my steps objetcs ?

    You'll find the updated verion attached to this message.

  • The configuration module has only one purpose: simplify creating a standardized config screen. If you have advanced requirements, you need to go the hard way ;)

    That piece of code is pretty useless:

      private function GetStepTitle($Index) {
         $StepTitle = C('ForumTour.Title.'.$Name);
         return $StepTitle;
      }
    

    The parameter is called $Index and the variable later on is $Name - that doesn't match ;)
    Since the functions only usage is returning a config value, you do not need it at all.

    This post might be helpful: http://vanillaforums.org/discussion/comment/233579/#Comment_233579
    But don't use the ConfigurationModule. Use your view instead. The rest can be nearly copied.

    In principle, in the function that renders the view, there should be an if block that tests if the form has been posted back. If it has been posted back, you can check the values and save them with SaveToConfig('Your.Plugin.Key', 'TheValue');.
    In the comment linked above, the saving is done automatically by the framework (because the configuration model is linked with the form.

  • MrVladMrVlad New
    edited September 2015

    @R_J : great advice, thx a lot ! I followed the instruction you gave me, now I can add and edit my tutorial steps in a pretty way ! So, let's begin with the cool stuff :pleased: :

    • User can press "ajouter une étape" to add a step.
    • Each step has a "éditer l'étape" button which is used to edit a specific step (I used url parameters to make this work).

    I have yet many things to learn, so my code is quite messy, and I can't figure out how to remove a step whithout affecting everything else ... but I'm pretty happy to have gone so far with your help.

    The thing I'm the less happy with is how I create the array used for setting up the ConfigurationModel ... Too make things short, I use loops in order to fetch data but this method is quite limited (with my code) because, let's say, if $data[i] is null and data[i+1] isn't, the loops break at 'i' and the rest of the data isn't displayed :/ ... Would you guys have any advices to give on that matter ?

    Cheers

    btw : the front end side of the plugin is broken, I'll fix this after everything else

  • If your ForumTour config values are a bunch of information, it might make more sense to store them as an array: array('title' => 'description', 'title' => 'description', 'title' => 'description')

    // Get the array from the config
    $ForumTourInfo = c('ForumTour.Info');
    
    // Add one value
    $ForumTourInfo = c('ForumTour.Info');
    $ForumTourInfo['new title'] = 'description';
    SaveToConfig($ForumTourInfo);
    
    // Delete one value
    $ForumTourInfo = c('ForumTour.Info');
    unset($ForumTourInfo['old title']);
    SaveToConfig($ForumTourInfo);
    
    // build your form (just pseudo code)
    echo $form->open();
    echo $form->errors();
    foreach ($ForumTourInfo as $title => $description) {
        echo $form->label($title);
        echo $form->input($description);
    }
    
    
    // Do some validation
    foreach ($ForumTourInfo as $title => $description) {
        if ($title == '' && $description != '') {
            $form->AddValidationResult('Please provide a title!');
        } elseif ($title != '' && $description == '') {
            $form->AddValidationResult('Please provide a description!');
        }
    }
    
  • @R_J thx a lot, as usual. Just to make things clear for me (I've been trying to implement what you sent me and crashed everything 3 times already) : I should change the way data is stored, to make things look like :

    Plugin.Data -> Step1 -> title1
                                           description1
                                           xcoord1
                                           ycoord1
    
                            Step2 -> title2
                                           description2
                                           xcoord2
                                           ycoord2
                            ....
    

    If so, I'll have to find a way to make configurationModel work with this new method ... I tried to proceed like so ::

    $ForumTourInfo = c('Plugins.ForumTour');
    // This is a test to see if I can inject an array in the configurationModel
    $ForumTourInfo['newStep'] = array('title' => 'test title');
    
    $ConfigurationModel->setField($ForumTourInfo);
    $sender->Form->SetModel($ConfigurationModel);
    

    ... unsucessfuly as you may imagine ...

    Again, thx for your time I guess this should be boring as hell ...

  • If you store all information in one array variable, you wouldn't need the configuration model. From the source you would find "Represents and manages configuration data" in the class.configurationmodel.php. If you only need to handle one config value only, you would be able to use the two functions c() and saveToConfig().

    I'm not sure if those complex arrays could be saved easily with the two functions mentioned above, but you could always either serialize or json encode them if there are problems.

    The structure of the array is completely up to you. Model it as you need it.

    The program flow would look like the following (roughly drafted)

    public function settingsController_ForumTour_create($sender) {
        if ($sender->Form->authenticatedPostBack()) {
            $formPostValues = $sender->Form->formValues();
            $data = array();
            foreach ($formPostValues as $key => $value) {
                // validate and then
                $data[] = however you like to store the information
            }
            saveToConfig('ForumTour.Info', $data);
        }
        $sender->setData('ForumTourInfo', c('ForumTour.Info'));
        $sender->Render('settings', '', 'plugins/ForumTour');
    }
    

    In the view you have to loop through $this->Data('ForumTourInfo') and build your form accordingly.

  • @R_J : wow ... I think I've reached the limit of what I can understand at my level. So, once again, I followed you instructions. I created a "Data" array containing steps (each step as a title and a descritpion for now). I can use the form I created to edit values, but for now I just can't figure out how this works because all values are edited at the same time ...

    I think I might have to restart the all thing from scratch. Am I using a good method anyway ? I mean, I went back into the code of HowToFormValidate plugin and this looks very different from what I've done. Maybe I should create a Gdn model, like @hgtonight did for yaga ... anyway, as usual, any help from you guys is very appreciated, I've come to a point where I'm not even understanding what I'm doing ... yup ... hope isn't high :/

Sign In or Register to comment.