Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Try Vanilla Forums Cloud product
Vanilla 2.6 is here! It includes security fixes and requires PHP 7.0. We have therefore ALSO released Vanilla 2.5.2 with security patches if you are still on PHP 5.6 to give you additional time to upgrade.
Please upgrade to 2.3 here. The 2.2 and earlier branches are no longer being updated.

PLUGIN : Animated forum "tour".

MrVladMrVlad New
edited September 2015 in Vanilla 2.0 - 2.3

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 !

unixfoxLinc
«1

Comments

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    @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

    BleistivtMrVladAdrian
  • 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 !

    R_J
  • hgtonighthgtonight ∞ · New Moderator

    @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.

    Bleistivt
  • @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.

    MrVladvrijvlinder
  • 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 !

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    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

    Bleistivt
  • @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 !

    vrijvlinder
  • 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.

    vrijvlinder
  • 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.

    R_J
  • 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.

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    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.

    MrVlad
  • 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

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    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 ...

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    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 :/

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    Okay, I'll install your plugin and try to help with real code.

    Could you just give me some information of what is needed in config.php?
    a) Here is what I see (and what I guess would be the best form element)
    ForumTour.Title (string in TextBox)
    ForumTour.Description (string in TextArea)
    ForumTour.Xposition (integer in TextBox)
    ForumTour.Yposition (integer in TextBox)
    ForumTour.TooltipPosition (string: t('top,right,bottom,left') in DropDown)
    ForumTour.XpositionType (string: px/% in DropDown)
    ForumTour.YpositionType (string: px/% in DropDown)

    Is that correct and are all those and only those information needed?

    Given that you have two entries, would you need all of the informations mentioned above for each entry or should XPositionType be px or % for every entry?
    Here's an example. Should it be like that

    array(
      array(
        'Title' => 'One Title',
        'Description' => 'Some Description',
        'XPosition' => '2',
        'YPosition' => '50',
        'TooltipPosition => 'right',
        'XPositionType' => 'px',
        'YPositionType' => '%'
      ),
      array(
        'Title' => 'Second Title',
        'Description' => 'Another Description',
        'XPosition' => '97',
        'YPosition' => '3',
        'TooltipPosition => 'left',
        'XPositionType' => '%',
        'YPositionType' => '%'
      )
    );
    

    or like that:

    array(
      array(
        'Title' => 'One Title',
        'Description' => 'Some Description',
        'XPosition' => '2',
        'YPosition' => '50',
        'XPositionType' => 'px',
        'YPositionType' => '%'
      ),
      array(
        'Title' => 'Second Title',
        'Description' => 'Another Description',
        'XPosition' => '97',
        'YPosition' => '3',
        'XPositionType' => '%',
        'YPositionType' => '%'
      ),
      'TooltipPosition => 'left'
    );
    
  • @R_J thanks a lot for all the time you spend helping me ^^ ! The information needed is all and only what you mentionned, and it needs to be step specific (like your first example).

    In order to make things clear :
    title -> step title (pretty obvious)
    description -> step description
    xPosition -> value used to define the position of the step indicator on the screen (x=0 is on the left side)
    xPositionType -> sometimes it's better to use % than px, so I'd like to give users a choice. For example, if xPosition is set to 50 and xPositionType to %, the step indicator will have a CSS style : left : 50%;
    TooltipPosition -> the position of the text relative to the step indicator. It's useful in order to improve layout/ease of use.

  • hgtonighthgtonight ∞ · New Moderator
    edited September 2015

    It would be great if you would fire some event. Then plugins could handle the event and register their own walkthroughs.

    If the user doesn't have the tour plugin, no harm, no foul.

    This wouldn't require a front-end either, assuming your registration function is nice.

    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_JR_J Cheerleader & Troubleshooter Munich Moderator

    Sorry, I also have problems to store the desired information and now I do not have any more time to look at it. I attach what I have done so far. it doesn't solve your save problems, though... :-(

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    This is much better than the first try! Look through the code and just ask if you have questions.

  • @R_J Wow ! This is by far much better than what I've done ! I'll look into the code right away ! Thank you so much !

  • @R_J : I made some modification to my old code, and it's now shorter and easier to understand, using your method ! I'm now trying to make a delete function based on the code I found on "Pockets" plugin.

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    That would be quite easy. You would have to create function that loops through the config values and unsets the array value. You would have to create such a link like the link to the add/edit page so that the title is passed to the function.

      public function SettingsController_ForumTourDelete_Create($sender, $args) {
        $sender->Permission('Garden.Settings.Manage');
    
        $ForumTour = array();
    
        $title = $sender->Request->get('title');
        if ($title != '') {
            // First, we fetch _all_ steps from the config.
            $ForumTourConfig = c('ForumTour', array());
            // Then we walk through the steps one by one
            foreach ($ForumTourConfig as $key => $value) {
                // Check if one title matches the title we wanted to change
                if ($value['Title'] == $title) {
                    unset($ForumTourConfig[$key]);
                }
            }
            // Write back the new values to the config
            saveToConfig('ForumTour', $ForumTourConfig);
    
            // "Redirect" to the list view to show the short and cleaned list
            redirect(Gdn::request()->url('settings/forumtour'));
        }
    

    By now it is not possible to use the same title for more than one step. I hope that is no show stopper...

  • @R_J Everything is working just fine now (add/suppress/edit) ! I think I can release a stable version of the tool :D ! Would you like to be credited as one of the authors, because you did all the hard stuff after all ^^

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    Congratulations!

    I would suggest you do a massive code cleanup before you publish it. You can find the coding standard here: http://docs.vanillaforums.com/developers/contributing/coding-standard/

    And no, no need for credits. I just helped with the settings. It was your idea and you did the hardest part (the presentation layer) all alone!

    If you like to, you could point to this discussion so that interested people could see how it did grow, but I do not think that anyone is really interested in that, so I guess you even skip that ;)

«1
Sign In or Register to comment.