HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

Form submission help

So time to move into the Dev category :)


So i am trying to add more form elements to the new discussion page. I am modifying 's EventCalendar plugin to allow for people to specify a type of Poker Game IE Cash or Tournament. So looking at his code i am not sure how this little code produces so much output :) Some how this inserts the date into the EventCalendarDate column. Can anyone point me to a document to help me understand the submitting of form data and storing of that data in the database?


public function postController_beforeBodyInput_handler($sender) {
     
// If user does not have the permission to add or manage EventCalendar dates
if (!checkPermission(['Plugins.EventCalendar.Add', 'Plugins.EventCalendar.Manage'])) {return;}
$categoryID = valr('Category.CategoryID', Gdn::controller());
$cssClass = 'P EventCalendarInput';
if (!in_array($categoryID, c('EventCalendar.CategoryIDs'))) {$cssClass .= ' Hidden';}
    $year = date('Y');
    $yearRange = $year.'-'.($year + 1);
    $fields = explode(',', t('EventCalendar.DateOrder', 'month,day,year'));
    echo '<div class="', $cssClass, '">';
    echo $sender->Form->label('Date of Game', 'EventCalendarDate');
    echo $sender->Form->date('EventCalendarDate', [
      'YearRange' => $yearRange,
      'Fields' => $fields
    ]);
    echo '</div>';
}

Comments

  • KasparKaspar ✭✭✭

    I am in over my head here so I might be totally wrong but I think this

    https://docs.vanillaforums.com/developer/framework/models/

  • It's beautifully simple. If a discussion is saved (because it is created or edited), every field in the submitted form is validated. Therefore, if you have a plugin which has created an additional column in the Discussion table and your plugin has extended the new discussion view so that this field can be filled out by the user, that field automatically gets saved to the database :-)


    To explain with some code, look at DiscussionModel->save(). It starts with $this->defineSchema(); and that method is in class.model.php:

       /**
        * Connects to the database and defines the schema associated with $this->Name.
        *
        * Also instantiates and automatically defines $this->Validation.
        *
        * @return Gdn_Schema Returns the schema for this model.
        */
       public function defineSchema() {
           if (!isset($this->Schema)) {
               $this->Schema = new Gdn_Schema($this->Name, $this->Database);
               $this->PrimaryKey = $this->Schema->primaryKey($this->Name, $this->Database);
               if (is_array($this->PrimaryKey)) {
                   $this->PrimaryKey = $this->PrimaryKey[0];
               }
    
               $this->Validation->setSchema($this->Schema);
           }
           return $this->Schema;
       } 
    


    It defines a schema and a validation, which means that the database table is analyzed and there will be a validation based on the table scheme: checking if required fields are filled, if maximum length fields have their maximum, if enum fields fields are filled with a valid value etc.

    In the DiscussionModel->save method there are those two lines which are doing all those validation work:

          // Validate the form posted values
           $this->validate($formPostValues, $insert);
           $validationResults = $this->validationResults();
    

    So if you want to simply add some fields, it could be easy as that:

    1. add a column to the Discussion table in your plugins structure
    2. look at /applications/vanilla/views/post/discussion.php for fireEvent calls and decide where to add the form field for your additional fields

    That will be enough to save them.

  • MrCaspanMrCaspan
    edited February 2020

    So wait, you set your validation on the MySQL database definition of the column ie if its enum it will pull that info form the database and then validate your data also max length and if its required!?! what a backwards and amazing way of doing things. I would always just know my database structure then make sure my date conformed to it by coding validation rules that conform to my database layout. This is just smart to go to the source and ask what it is then use that information to validate the date! Man the developers at Vanilla Forums build one beautiful piece of code here!!

  • @R_J Just so you know I am adding to your event calendar plugin to allow for users to say Yes, No, Maybe to an event.

    Can i ask you opinion of how to do this. I was thinking of creating a database called EventCalendarResponces

    then it's schema would be

    Index, DiscussionID, UserID, ENUM (Yes, No, Maybe)


    does this make sense to you to keep the data separate or should i just create 3 new columns in the database table for Discussions called EventCalendarYes, EventCalendarNo, EventCalendarMaybe and just store a comma separate list of UserIDs? I know this way is easier but its not database correct..

    Also if i want to have a button submit the userID,DiscussionID,enum(yes,no,maybe) when they click yes no or maybe but not have the whole page refresh, like an ajax call? is this possible or do i have to submit and refresh the whole page?

  • @R_J Also how would i get your code to save the event date as an epoch time in the EventCalendarDate field and not YYYY-MM-DD?

  • You can validate already before things are passed to the models save method and sometimes you need to do it. But sometimes you can have it quite simple.

    Answer 1

    I would suggest using the UserDiscussion table. It holds some "meta" information per user per discussion, e.g. DateLastViewed, Bookmarked, Participated. It has been a long time ago since I coded that plugin, but I think events are simply discussions in a dedicated category. So if they are normal discussions, there will be an entry for each user who looks at an event. Your plugin could create a nullable column in the UserDiscussion discussion, maybe "EventParticipation" with the options you want (just my personal opinion: "maybe" is worthless).

  • Answer 2

    There are some ajax "tools" in Vanilla. For participating in a event, you can think about creating a module for the panel, eventually showing the current participants and a button to particiapte. Following code will give you a nice button:

    echo anchor(
       Gdn::translate('Participate'),
       '/plugin/eventcalendar/participate',
       'Hijack Button'
    );
    

    That will make an ajax call to a method pluginController_eventCalendar_create($sender, $args). In that method you should do some routing based on $sender->RequestMethod (or $args[0])


    Your buttons should be in a div#EventCalendarParticipationButtons. Then there is another ajax helper. That should be the last line in your method: $sender->jsonTarget('#EventCalendarParticipationButtons', $buttons, 'ReplaceWith');

    $buttons should be html showing the div#EventCalendarParticipationButtons including the buttons html with just the buttons you need, the appropriate button disabled or whatever you like for your UI. It is quite complex but it might get you started.

    You need to pass the DicussionID along with the link in the button.

    Since that way participation links can be spoofed, you should think about using JavaScript to make a post request instead of that more simple solution.

  • You want to give a time with it? I would advice to use "YYYY-MM-DD HH:MM:SS" since that is the format which is used by Vanilla anywhere.


    Not sure about it. There are at least two places to look at. The structure() method defines the column type as date. Does that allow saving dates together with time? If not, column type should be changed to "datetime".

    The other place is the discussionModel_beforeSaveDiscussion_handler method, but the validation rule for "date" allows either date or date and time. This is taken form the code:

    // Dates should be in YYYY-MM-DD or YYYY-MM-DD HH:MM:SS format

  • For answer 2: just as rbrahmson said, it is always best to look at examples.

    Here is a simple one which shows that it is not required to write a line of javascript for a post request: https://github.com/vanilla/addons-patches/blob/master/plugins/Bump/class.bump.plugin.php

    A link with the class Hijack will "automatically" do an ajax call to the href url and opposed to what I said, the render call you see in the linked example should be the last line.

  • And here is another example for the "ReplaceWith" variant: https://github.com/R-J/rj-like/blob/master/LikePlugin.php#L226

  • Giving that a second thought: if you extend it quite a lot, you might better use tables like "Event" for event specific information and "UserEvent" for all user/event related information.

    Events will make up only a small percentage of all discussions and adding columns to tables which are most of the time empty is a bad design, I'd say.

    If you have a dedicated table Event, the only info in the Discussion table should be the Type and the ForeignID. You would have to hook into the disscussionModel_afterSaveDiscussion event and save the Event information "manually":

    $values = $args['FormPostValues'];

    $values['DiscussionID'] = $args['DiscussionID'];

    $eventModel = new Gdn_Model('Event');

    $eventID = $eventModel->save($values);

    $sender->setField($args['DiscussionID'], 'ForeignID', $eventID);


    That would be much cleaner, I'd say... If I were writing that plugin today, I would use that approach. It would be easier to extend that for "location", "duration" or whatever.

    A UserEvent table could hold information about participations, managers and owners

  • I concur with @R_J . Here are some fields I suggest for this purpose (for use with the structure function)

               ->column('EventCalendarDate', 'date', true)

               ->column('EventCalendarTime', 'varchar(60)', true)

               ->column('EventCalendarVenue', 'varchar(180)', true) /* optional event venue*/

               ->column('EventCalendarRepeats', 'varchar(12)', true) /* optional: daily/weekly/biweekly/monthly/list*/

               ->column('EventCalendarList', 'varchar(500)', true) /* optional: recurrence list of dates/times */

               ->column('EventCalendarUntil', 'date', true)       /* optional: event expiration date*/

  • SO what I am going to do is the following:

    New Tables

    Games

    ID (Auto INT), DicussionID (FK), DateTime (Date), Cancelled (BOL)

    GamesAttendance

    ID (Auto Int), GamesID (FK), UserID (INT), Yes (BOL), No (BOL), Maybe (BOL), Guests


    The thought is that I can keep expanding the Games table to add more event details if I need to then the Attendance table just needs to be a UserID that is going and if they are bringing guests.. Seems simple enough :) I can't wait to try and program this!

  • Unless you intend to change this plugin for a very specific use case I'd think that there can be multiple types of events (game just one of them) and therefore I'd change the table name and perhaps add event-type or foreign key to event-types table. Just a thought.

  • Why do you create 3 columns for yes, no, maybe? It's an either or decision, isn't it?

  • @R_J For these poker games it's important for some player to say maybe as a seat will be held for them as they are regulars but if by the day of if they have not firmed up they will be replaced with lower status players

  • From the description of the table I assumed that you have 3 columns for yes, no & maybe. I was just wondering why you didn't use one column only

  • I like to make easy to read tables and they are only Boolean so they don't take up too much space.. I find some tables hard to read because it holds a value of 0-2 0 is no, 1 is yes, 2 is maybe... etc just makes it harder to read

  • R_JR_J Admin

    0 - 2 is ugly, I agree. But you can make use of the words yes, no and maybe instead. I would just think that it is an additional source for errors (you need to ensure that only one option is chosen) and although I'm not sure about this point, I have the feeling that all db queries get an extra layer of complexity by that.

    But that's all your design and you have to deal with that 😁

  • haha And yeah you are right i could use enum to set these.. This will be for version 2! i just want version 1 up and running !

Sign In or Register to comment.