Jump to content

SamC

Members
  • Posts

    733
  • Joined

  • Last visited

  • Days Won

    9

Posts posted by SamC

  1. 23 hours ago, kongondo said:

    Because you are calling both code at the same time :)

    Lol, I wish. I just stuck them in that example above to show the two differences. Running them separately causes the issue.

    <?php
      $root = $pages->get("/");
      $children = $root->children();
      // insert the following line
      $children->prepend($root);
    ?>

    ...gives me:

    Home
    ----
    Home
    About
    Services
    Portfolio
    Contact

    and (even with changing the variable name in case of it being in memory already):

    <?php
      $root = $pages->get("/");
      $children2 = $root->and($root->children);
    ?>

    ...gives me:

    Home
    ----
    About
    Services
    Portfolio
    Contact

    The only way I can get 'Home' into the dropdown in this example is to add it in the inner loop:

    <?php foreach($homepage->and($c->children) as $cc):?>

    ...gives me:

    Home
    ----
    Home
    About
    Services
    Portfolio
    Contact

    Is the weirdest thing.

    == EDIT ==

    Been looking at these again tonight, tried the following on my demo site. The WireArrays are different (one is larger) for each $topLevels variable (depending on which one you uncomment).

    <?php
      $root = $pages->get("/");
      // Home link in inner loop
      // $topLevels = $root->children->prepend($root);
    
      // NO Home link in inner loop
      // $topLevels = $root->and($root->children);
    ?>
    
    <ul>
    
      <?php foreach($topLevels as $topLevel):?>
      <li>
        <a href="#"><?= $topLevel->title; ?></a>
    
              <ul>
                <?php foreach($topLevel->children as $topLevelChild): ?>
                <li><a href="<?= $topLevelChild->url ?>"><?= $topLevelChild->title ?></a></li>
                <?php endforeach; ?>
              </ul>
    
      </li>
      <?php endforeach; ?>
    
    </ul>

    I get (top option):

    Home
     - Home
     - About
     - Services
     - Contact
     - Cats
     - Type

    ...and (second option)::

    Home
     - About
     - Services
     - Contact
     - Cats
     - Type

    I would expect no home link to be in either and the resulting WireArrays in '$topLevels' to be the same. The inner loop, loops through the children of the top level page, 'Home' should not be in there twice

    It's something to do with the resulting WireArrays of those two lines. Am I just being super slow here or what? :undecided:

  2. 12 minutes ago, kongondo said:

    Why? In the first (using and()), you are adding children to the existing item (Home). In the second, you get the children and prepend their root to them.

    It's the dropdowns in the inner loop that are different:

    <?php
      // Home, About, Services, Portfolio, Contact
      foreach($children as $c):
    ?>
    <li>
      <a href="#"><?= $c->title; ?></a>
    
        <div class="uk-navbar-dropdown uk-navbar-dropdown-width-2 g8-theme-mkred">
          <div class="uk-navbar-dropdown-grid uk-child-width-1-2" uk-grid>
            <ul class="uk-nav uk-navbar-dropdown-nav">
              <?php
                // Children of home, Children of about, children of services...
                foreach($c->children as $cc): 
              ?>
              <li><a href="<?= $cc->url ?>"><?= $cc->title ?></a></li>
              <li class="uk-nav-divider"></li>
              <?php endforeach; ?>
            </ul>
          </div>
        </div>
    </li>
    <?php endforeach; ?>

    I have no idea why one version (with the prepend), the dropdown under home is:

    Home
    ----
    Home
    About
    Services
    Portfolio
    Contact

    ...and the other version (using and()):

    Home
    ----
    About
    Services
    Portfolio
    Contact

    The dropdowns under 'Services' however are the same with both versions. The differences only affect the 'Home' dropdown.

    12 minutes ago, kongondo said:

    What I don't understand is why and() has worked with the single page object ($root). I thought (as per docs) it should only work with WireArrays (also PageArrays). $root is a Page not a PageArray, or am I missing something?

    Beats me, but I've used this a few times.

  3. I was looking at the uikit menu issues here:

    ...when I stumbled across something weird that I can't get my head around. I'll paste in the code I used again to save checking that link out and the two commented parts of code I'm talking about:

    <?php
      /*
      // when this is used, dropdown under 'Home' has NO home link
      $root = $pages->get("/");
      $children = $root->and($root->children);
      // echo $children: 1|1086|1069|1081|1022
      */
    ?>
    
    <?php
      /*
      // when this is used, dropdown under 'Home' HAS home link
      $root = $pages->get("/");
      $children = $root->children();
      // insert the following line
      $children->prepend($root);
      // echo $children: 1|1086|1069|1081|1022
      */
    ?>
    
    <nav class="uk-navbar-container" uk-navbar>
      <div class="uk-navbar-left">
    
        <ul class="uk-navbar-nav">
    
          <?php foreach($children as $c):?>
          <li>
            <a href="#"><?= $c->title; ?></a>
    
              <div class="uk-navbar-dropdown uk-navbar-dropdown-width-2 g8-theme-mkred">
                <div class="uk-navbar-dropdown-grid uk-child-width-1-2" uk-grid>
                  <ul class="uk-nav uk-navbar-dropdown-nav">
                    <?php foreach($c->children as $cc):?>
                    <li><a href="<?= $cc->url ?>"><?= $cc->title ?></a></li>
                    <li class="uk-nav-divider"></li>
                    <?php endforeach; ?>
                  </ul>
                </div>
              </div>
          </li>
          <?php endforeach; ?>
    
        </ul>
        
      </div>
    </nav>

    Can anyone tell me what the difference is between the two commented parts at the top? Both $children variables appear to have the same pages but one results with no home link in the dropdown.

    However, using either of the versions, the top level menu is the same.

    Home | About | Services | Portfolio | Contact

    Can't get my head around this. I would expect both dropdowns not to have the 'Home' link.

    Any ideas?

  4. Thanks for the info. I used option 1 so I have the following files:

    suggest-form.php (the form)

    <?php namespace ProcessWire;
    
    // get recaptcha module
    $captcha = $modules->get("MarkupGoogleRecaptcha");
    
    // save current page id in session
    $session->currentPageId = $page->id;
    
    ?>
    
    <div id="form-top"></div>
    
    <h2>Suggest a tutorial</h2>
    
      <div id="form-alert" role="alert"></div>
    
      <form id="contact-form" method="post" class="ajax" action="<?= $pages->get(1832)->url; ?>">
    
        <div class="row">
            <div class="form-group col-sm-12 col-lg-6 py-2" id="name">
                <label for="name">Name (required)</label>
                <input class="form-control" name="name" type="text">
            </div>
    
            <div class="form-group col-sm-12 col-lg-6 py-2" id="email">
                <label for="email">Email (required)</label>
                <input class="form-control" name="email" type="text">
            </div>
        </div>
    
        <div class="form-group py-2" id="message">
            <label for="message">Message (required)</label>
            <textarea class="form-control" name="message" id="message" rows="8"></textarea>
        </div>
    
        <div class="form-group py-1" id="recaptcha">
            <label for="recaptcha">Recaptcha (required)</label>
            <!-- Google Recaptcha code START -->
            <?php echo $captcha->render(); ?>
            <!-- Google Recaptcha code END -->
        </div>
    
        <div class="form-group">
            <button type="submit" id="sendMe" class="btn btn-primary mt-3" name="sendMe" value="1">Suggest tutorial <i class="fa fa-angle-right" aria-hidden="true"></i></button>
        </div>
    
      </form>
    
    <?php
      echo $captcha->getScript();
    ?>
    
    
    

    suggest.php (process and echo JSON or output plain messages to '/suggest/' for no JS fallback)

    <?php namespace ProcessWire;
    
    // is form submitted
    $isSubmitted = ($input->post->sendMe) ? true : false;
    
    if ($isSubmitted) {
    
      // default out string for no JS
      $out = "<h2>Please fix the following errors</h2>";
    
      // require Valitron class
      require_once("./vendor/vlucas/valitron/src/Valitron/Validator.php");
    
      // where to send email
      $myEmail = "EMAIL_HERE";
    
      // sanitize input variables
      $name = $sanitizer->text($input->post->name);
      $email = $sanitizer->email($input->post->email);
      $message = $sanitizer->textarea($input->post->message);
    
      // create new valitron
      $v = new \Valitron\Validator(array(
        "name" => $name,
        "email" => $email,
        "message" => $message
        )
      );
    
      // create valitron rules
      $v->rule("required", ["name", "email", "message"]);
      $v->rule("email", "email");
    
      // does form validate
      $formValidates = $v->validate();
    
      // fields array if error, otherwise false
      $nameHasError = $v->errors("name");
      $emailHasError = $v->errors("email");
      $messageHasError = $v->errors("message");
      
      // multidimensional array of errors
      $errors = $v->errors();
    
      // loop errors and concat for 
      // no JS output
      foreach ($errors as $error) {
        foreach ($error as $value) {
          $out .= "<p>" . $value . "</p>";
        }
      }
    
      // get recaptcha module
      $captcha = $modules->get("MarkupGoogleRecaptcha");
    
      // does recaptcha validate?
      $recaptcha = $captcha->verifyResponse();
    
      // if not, concat for no JS output
      if (!$recaptcha) {
        $out .= "<p>Recaptcha is required</p>";
      }
    
      // default form submitted value
      $isSent = false;
    
      // if fields validate and recaptcha complete
      if ($recaptcha && $formValidates) {
    
        // create email message
        $msg = "
          <html>
            <body>
              <p><b>Name:</b> {$name}</p>
              <p><b>Email:</b> {$email}</p>
              <p><b>Message:</b></p>
              <p>{$message}</p>
            </body>
          </html>";
    
        $mail = wireMail();
        
        $mail->to($myEmail)
        ->from($email, $name)
        ->subject('Email from website...')
        ->bodyHTML($msg);
    
        // did email send?
        $isSent = ($mail->send()) ? true : false;
    
        // success message for no JS
        if ($isSent) {
          $out = "<h2>Thanks for the suggestion!</h2>";
        }
      }
    
      // form sent via ajax
      if ($config->ajax) {
        $result = [
          "name" => $name,
          "email" => $email,
          "message" => $message,
          "nameHasError" => $nameHasError,
          "emailHasError" => $emailHasError,
          "messageHasError" => $messageHasError,
          "fieldsValidate" => $formValidates,
          "recaptchaComplete" => $recaptcha,
          "formSent" => $isSent
        ];
        
        // output JSON for ajax
        echo json_encode($result);
      }
      // output for basic no JS page
      else {
        include("./includes/header.php");
        echo "
          <div class='container py-5'>
            <div class='row justify-content-center '>
              <div class='col-lg-7'>
                  {$out}
                <h4><a href='{$pages->get($session->currentPageId)->url}'>Go back to previous page</a></h4>
              </div>
            </div>
          </div>
        ";
        include("./includes/footer.php");
      }
    
    }
    // if url accessed with no form submission
    else {
      $session->redirect("/");
    }

    app.js (ajax)

    // ajax form
    $("form.ajax").on("submit", function() {
      var self = $(this),
          url = self.attr("action"),
          type = self.attr("method"),
          data = {};
    
      self.find("[name]").each(function(index, value) {
        var self = $(this),
            name = self.attr("name"),
            value = self.val();
            
        // add field values to data obj
        data[name] = value;
      });
    
      // send data obj to /suggest/ php page for processing
      $.ajax({
        url: url,
        type: type,
        data: data,
        dataType: "json",
        success: function(res) {
          var nameField = self.find("#name"),
              emailField = self.find("#email"),
              messageField = self.find("#message"),
              alert = $("#form-alert");
              dataObj = res;
    
          function errors(obj, key, field) {
            // if key value is empty string or array
            if (obj[key].length === 0 || Array.isArray(obj[key])) {
              alert.removeClass("alert alert-success");
              field.addClass("has-danger");
              alert.addClass("alert alert-danger");
              alert.text("Please complete required fields");
            }
            else {
              field.removeClass("has-danger");
            }
          }
    
          // copy function and set default value of 1st parameter
          // just for revision of bind function...
          var generateErrors = errors.bind(this, dataObj);
          
          // false = no error
          // array of errors = error
          generateErrors("nameHasError", nameField);
          generateErrors("emailHasError", emailField);
    
          // string = no error
          // empty string = error
          generateErrors("message", messageField);
    
          // return value of $mail->send() in suggest.php
          if (dataObj["formSent"]) {
            var form = $("form.ajax").html();
            $("form.ajax").html(form);
            alert.removeClass("alert alert-danger");
            alert.addClass("alert alert-success");
            alert.text("Thanks for the suggestion!");
          }
        }
      });
    
      return false;
    });

    Ignoring the goofy code itself (unless it presents a security risk), this all works.

    The question I have, are there any security issues associated with ajax itself rather than just posting straight to a php file? I mean, the values in the fields are unsanitized when they get added to the data object just after ajax on("submit").

    I noticed jquery serialize() for getting data from a form but not looked into that yet, could possibly replace the manual looping input fields in app.js.

    Anyway, security is my biggest concern right now so any info would be awesome, thanks.

  5. 4 hours ago, FrancisChung said:

    Wait till you get to Factory Methods, Contravariance, Covariance, Builder + Specification patterns .... haha

    Ouch

    4 hours ago, FrancisChung said:

    So you should probably make multiple smaller classes that has a very distinct and easily identifiable singular purpose rather than 1 monolithic one that tries to do everything.

    Ok, that makes sense.

    One underlying issue I have is what happens once the form is submitted. The form is on every blog post at the bottom, so when it's submitted, the user has nowhere to be redirected to, they need to stay on the same page, and once sent successfully, the form needs to:

    1) Show feedback if there's a problem (field validation and recaptcha) - might ditch recaptcha because it's so annoying for users
    2) Be empty if send was successful (with success feedback)
    3) Be checked server side, not too fussed about client side validation right now

    Currently, this kind of works, but because the page has not been refreshed, or redirected, the page can be refreshed in the browser and the form sends again, possibly a problem with the code itself, but this is why I was looking at ajax forms that could refresh just the form.

    The blog pages are also cached which meant when the form was submitted, a cached version of the page is served, when you navigate away to another page, the success message is above the form on that page. Not ideal! Disabling caching works but I can't leave it off.

    == EDIT ==

    Caching is off at the moment (and the form appears to be working, I receive emails ok). It's at the bottom of each post:

    https://www.pwtuts.com/processwire-tutorials/url-segments-and-the-page-reference-field/

  6. 17 minutes ago, FrancisChung said:

    Wow, it's like Night & Day compared to your first iteration. Looking very good.

    That's kind of you to say, thanks. Was on it for quite a few hours today. At the present time, it looks like this, but I wanted to be able to get more of the logic into the class, still seems a bit of a mess to me.

    <?php namespace ProcessWire;
    
    class FormChecker {
    
      // array to hold feedback messages
      public $feedback = [];
    
      // 3rd party validator object
      private $validator;
    
      public function __construct($validator) {
        $this->validator = $validator;
      }
    
      public function checkForm() {
        return $this->validator->validate(); 
      }
    
      public function checkField($name) {
        return $this->validator->errors($name);
      }
    
      public function displayFeedback() {
        $str = "";
    
        if (count($this->feedback)) {
          $str = '<ul class="mb-0">';
          foreach ($this->feedback as $value) {
            $str .= '<li>' . $value . '</li>';
          }
          $str .= '<ul>';
        }
    
        return $str;
      }
    }
    
    // require Valitron class
    require_once("./vendor/vlucas/valitron/src/Valitron/Validator.php");
    
    // get recaptcha module
    $captcha = $modules->get("MarkupGoogleRecaptcha");
    
    // get sanitized variables
    $name = $sanitizer->text($input->post->name);
    $email = $sanitizer->email($input->post->email);
    $message = $sanitizer->textarea($input->post->message);
    
    // create new valitron
    $v = new \Valitron\Validator(array(
      "name" => $name,
      "email" => $email,
      "message" => $message
      )
    );
    
    // create valitron rules
    $v->rule("required", ["name", "email", "message"]);
    $v->rule("email", "email");
    
    // save as property on new $checker object
    $checker = new FormChecker($v);
    
    // has form been submitted?
    $isSubmitted = $input->post->sendMe;
    
    // default mail sent value
    $isSent = false;
    
    if ($isSubmitted) {
      // does form validate after submission
      $formValidates = $checker->checkForm();
    
      // recaptcha result
      $recaptcha = $captcha->verifyResponse();
    
      if (!$recaptcha) {
        array_push($checker->feedback, "Recaptcha must be complete.");
      }
    
      if (!$formValidates) {
        array_push($checker->feedback, "Please fill out the fields correctly.");
      }
    
      if ($recaptcha && $formValidates) {
        $msg = "
          <html>
            <body>
              <p><b>Name:</b> {$name}</p>
              <p><b>Email:</b> {$email}</p>
              <p><b>Message:</b></p>
              <p>{$message}</p>
            </body>
          </html>";
    
        $mail = wireMail();
        
        $mail->to("sam@woodenfoxdesign.com")
        ->from($email, $name)
        ->subject('Email from website...')
        ->bodyHTML($message);
    
        $isSent = $mail->send();
    
        if ($isSent) {
          array_push($checker->feedback, "Thanks for your message!");
        }
        else {
          array_push($checker->feedback, "Sorry, an error occured. Please try again.");
        }
      }
    }
    ?>
    
    <div id="form-top"></div>
    
    <h2>Suggest a tutorial</h2>
    
      <?php if($isSubmitted):?>
        <div class="alert <?= ($isSent) ? 'alert-success' : 'alert-danger'?>" role="alert">
          <?= $checker->displayFeedback(); ?>
        </div>
      <?php endif;?>
    
      <form id="contact-form" method="post" action="#form-top">
    
        <div class="row">
            <div class="form-group col-sm-12 col-lg-6 py-2 <?= $checker->checkField('name') ? 'has-danger' : ''?>">
                <label for="name">Name (required)</label>
                <input class="form-control" name="name" id="name" type="text">
            </div>
    
            <div class="form-group col-sm-12 col-lg-6 py-2 <?= $checker->checkField('email') ? 'has-danger' : ''?>">
                <label for="email">Email (required)</label>
                <input class="form-control" name="email" id="email" type="text">
            </div>
        </div>
    
        <div class="form-group py-2 <?= $checker->checkField('message') ? 'has-danger' : ''?>">
            <label for="message">Message (required)</label>
            <textarea class="form-control" name="message" id="message" rows="8"></textarea>
        </div>
    
        <div>
            <label for="recaptcha">Recaptcha (required)</label>
            <!-- Google Recaptcha code START -->
            <?php echo $captcha->render(); ?>
            <!-- Google Recaptcha code END -->
        </div>
    
        <div class="form-group">
            <button type="submit" class="btn btn-primary mt-3" name="sendMe" value="1">Suggest tutorial <i class="fa fa-angle-right" aria-hidden="true"></i></button>
        </div>
    
      </form>
    
    <?php
      echo $captcha->getScript();
    ?>
    
    

    I think I'll have another crack over the weekend, I've learned a few new things about classes so that's good. My brain hurts.

    • Like 1
  7. Trying to make an ajax form (I think this option would work better than my current one, plus I've never used ajax before so wth), got the following:

      <form id="contact-form" method="post" class="ajax" action="<?= $templatesUrl . 'controller/suggest-form-controller.php'; ?>">
    
        <div class="row">
            <div class="form-group col-sm-12 col-lg-6 py-2">
                <label for="name">Name (required)</label>
                <input class="form-control" name="name" id="name" type="text">
            </div>
    
            <div class="form-group col-sm-12 col-lg-6 py-2">
                <label for="email">Email (required)</label>
                <input class="form-control" name="email" id="email" type="text">
            </div>
        </div>
    
        <div class="form-group py-2">
            <label for="message">Message (required)</label>
            <textarea class="form-control" name="message" id="message" rows="8"></textarea>
        </div>
    
        <div>
            <label for="recaptcha">Recaptcha (required)</label>
            <!-- Google Recaptcha code START -->
            <?php echo $captcha->render(); ?>
            <!-- Google Recaptcha code END -->
        </div>
    
        <div class="form-group">
            <button type="submit" class="btn btn-primary mt-3" name="sendMe" value="1">Suggest tutorial <i class="fa fa-angle-right" aria-hidden="true"></i></button>
        </div>
    
      </form>

    ...and the JS:

    // ajax form
    $("form.ajax").on("submit", function() {
      var self = $(this),
          url = self.attr("action"),
          type = self.attr("method"),
          data = {};
    
      self.find("[name=name], [name=email], [name=message]").each(function(index, value) {
        var self = $(this),
            name = self.attr("name"),
            value = self.val();
            
        // create object with name/value pairs
        data[name] = value;
      });
    
      $.ajax({
        url: url,
        type: type,
        data: data,
        success: function(res) {
          console.log(res);
        }
      });
    
      return false;
    });

    Problem is, when I click the submit button, I get a 403 on the post url (so at least the JS works so far).

    No idea how I'm going to integrate recaptcha and valitron into all of this but need to get passed this hurdle first.

    Do I have to create a matching PW path or something with a hidden page? The file is currently in:

    /site/templates/controller/suggest-form-controller.php

    Any hints would be awesome, thanks.

  8. Thanks for the info on this thread, it's appreciated greatly. I'm moving forward on this now, the composition example makes sense to me. I'm at this stage:

    <?php namespace ProcessWire;
    
    class FormChecker {
    
      // 3rd party validator object
      private $validator;
    
      public function __construct($validator) {
        $this->validator = $validator;
      }
    
      public function checkForm() {
        return $this->validator->validate(); 
      }
    
      public function checkField($name) {
        return $this->validator->errors($name);
      }
    
    }
    
    // require Valitron class
    require_once("./vendor/vlucas/valitron/src/Valitron/Validator.php");
    
    // get sanitized POST values
    $name = $sanitizer->text($input->post->name);
    $email = $sanitizer->email($input->post->email);
    $message = $sanitizer->textarea($input->post->message);
    
    // create new valitron
    $v = new \Valitron\Validator(array(
      "name" => $name,
      "email" => $email,
      "message" => $message
      )
    );
    
    // create valitron rules
    $v->rule("required", ["name", "email", "message"]);
    $v->rule("email", "email");
    
    // save as property on new $checker object
    $checker = new FormChecker($v);
    
    // has form been submitted?
    $isSubmitted = $input->post->sendMe;
    
    // does form validate after submission
    $formValidates = $checker->checkForm();
    
    $result = ($isSubmitted && $formValidates) ? "SUCCESS" : "FAIL";
    echo "Form validation was a: " . $result;
    
    ?>
    
    
    
    
    <div id="form-top" class="mb-5"></div>
    
    <h2>Send me a message</h2>
    
      <form id="contact-form" method="post" action="#form-top">
    
        <div class="row">
            <div class="form-group col-sm-12 col-lg-6 py-2 <?= $checker->checkField('name') ? 'has-danger' : ''?>">
                <label for="name">Name (required)</label>
                <input class="form-control" name="name" id="name" type="text" value="<?php if ($name) echo $name; ?>">
            </div>
    
            <div class="form-group col-sm-12 col-lg-6 py-2 <?= $checker->checkField('email') ? 'has-danger' : ''?>">
                <label for="email">Email (required)</label>
                <input class="form-control" name="email" id="email" type="text" value="<?php if ($email) echo $email; ?>">
            </div>
        </div>
    
        <div class="form-group py-2 <?= $checker->checkField('message') ? 'has-danger' : ''?>">
            <label for="message">Message (required)</label>
            <textarea class="form-control" name="message" id="message" rows="8"><?php if ($message) echo $message; ?></textarea>
        </div>
    
        <div>
            <label for="recaptcha">Recaptcha (required)</label>
            <!-- Google Recaptcha code START -->
            <?php // echo $captcha->render(); ?>
            <!-- Google Recaptcha code END -->
        </div>
    
        <div class="form-group">
            <button type="submit" class="btn btn-primary" name="sendMe" value="1">Message me!</button>
        </div>
    
      </form>

    And behold, so far so good!

    form-example-new.jpg

    Excitement levels rising now, I like where this is heading.

    Gonna get my feedback messages in there now.

    • Like 2
  9. So one problem I'm having this morning is that the validation happens via another class:

    wireIncludeFile("./vendor/vlucas/valitron/src/Valitron/Validator.php");

    ...which I wanted to use in my own class:

    class FormChecker {
    
      // where to send the email
      public $contactFormRecipient = "";
    
      // every available notice
      public $notices = [
        "errorSend" => "Sorry, an error occured. Please try again.",
        "errorValidation" => "Please fill out the fields correctly.",
        "errorRecaptcha" => "Recaptcha must be complete.",
        "success" => "Thanks for your message!"
      ]; 
    
      // push required notices into array
      public $feedback = [];
    
      // where to send email
      public function setContactFormRecipient($emailAddress) {
        $this->contactFormRecipient = $emailAddress;
      }
    
      public function checkField($name) {
        // $v is undefined here
        return $v->errors($name);
      }
    
    }

    ...and quick test:

    $checker->setContactFormRecipient("myemail@test.com");
    echo $checker->contactFormRecipient; // myemail@test.com
    
    $checker->checkField("name"); // $v is undefined in class

    Can I extend or implement the Valitron class so the variables are available in my own class rather than passing everything as parameters? i.e. it works like this but there must be a better way:

    class FormChecker {
    
      public function checkField($name) {
        return $v->errors($name);
      }
    
    }
    
    $v = new \Valitron\Validator(array(
      "name" => $name,
      "email" => $email,
      "message" => $message
      )
    );
    
    $v->rule("required", ["name", "email", "message"]);
    $v->rule("email", "email");
    
    $checker->checkField("name", $v);

    It's making more sense now I'm getting into it but variable scope seems to be my biggest issue right now.

  10. You're both right, I'm gonna try a class, my code is just getting ridiculous now lol, seems miles worse than my original one.

    <?php namespace ProcessWire;
    
    function generateFlashMessage($isValidated, $recaptchaResponse, $session, $feedback, $feedbackEmptyArr) {
      $str = "";
    
      if (!$isValidated) {
        array_push($feedbackEmptyArr, $feedback["errorValidation"]);
      }
      
      if (!$recaptchaResponse) {
        array_push($feedbackEmptyArr, $feedback["errorRecaptcha"]);
      }
    
      if ($isValidated && $recaptchaResponse) {
        if (!$session->sent) {
          array_push($feedbackEmptyArr, $feedback["errorSend"]);
        }
        else {
          array_push($feedbackEmptyArr, $feedback["success"]);
        }
      }
    
      if (count($feedbackEmptyArr)) {
        $str = '<ul class="mb-0">';
        foreach ($feedbackEmptyArr as $value) {
          $str .= '<li>' . $value . '</li>';
        }
        $str .= '<ul>';
      }
    
      return $str;
    }
    
    function createMessage($name, $email, $message) {
      $msg = "
        <html>
          <body>
            <p><b>Name:</b> {$name}</p>
            <p><b>Email:</b> {$email}</p>
            <p><b>Message:</b></p>
            <p>{$message}</p>
          </body>
        </html>
      ";
      return $msg;
    }
    
    function sendMail($contactFormRecipient, $name, $email, $message) {
      $mail = wireMail();
      
      $mail->to($contactFormRecipient)
      ->from($email, $name)
      ->subject('Email from website...')
      ->bodyHTML($message);
    
      return $mail->send();
    }
    
    function checkField($name, $v, $isSubmitted) {
      if($isSubmitted) {
        return $v->errors($name);
      }
    }
    
    wireIncludeFile("./vendor/vlucas/valitron/src/Valitron/Validator.php");
    $captcha = $modules->get("MarkupGoogleRecaptcha");
    $contactFormRecipient = "EMAIL_RECIPIENT";
    $isSubmitted = $input->post->sendMe;
    
    $feedbackEmptyArr = [];
    $session->flashMessage = $feedbackEmptyArr;
    
    $feedback = [
      "errorSend" => "Sorry, an error occured. Please try again.",
      "errorValidation" => "Please fill out the fields correctly.",
      "errorRecaptcha" => "Recaptcha must be complete.",
      "success" => "Thanks for your message!"
    ];
    
    $name = $sanitizer->text($input->post->name);
    $email = $sanitizer->email($input->post->email);
    $message = $sanitizer->textarea($input->post->message);
    
    $v = new \Valitron\Validator(array(
      "name" => $name,
      "email" => $email,
      "message" => $message
      )
    );
    
    $v->rule("required", ["name", "email", "message"]);
    $v->rule("email", "email");
    
    // validate form - true/false
    $isValidated = $v->validate();
    // verify recaptcha - true/false
    $recaptchaResponse = $captcha->verifyResponse();
    
    // if form is submitted
    if($isSubmitted) {
    
      if ($isValidated && $recaptchaResponse) {
        $msg = createMessage($name, $email, $message);
        sendMail($contactFormRecipient, $name, $email, $msg);
        $session->sent = true;
      }
    
    }
    ?>
    
    
    
    
    <div id="form-top" class="mb-5"></div>
    
    <h2>Send me a message</h2>
    
      <?php if($isSubmitted):?>
        <div class="alert <?php echo $session->sent ? 'alert-success' : 'alert-danger'?>" role="alert">
            <?= generateFlashMessage($isValidated, $recaptchaResponse, $session, $feedback, $feedbackEmptyArr); ?>
        </div>
      <?php endif;?>
    
        <form id="contact-form" method="post" action="#form-top">
    
          <div class="row">
              <div class="form-group col-sm-12 col-lg-6 py-2 <?= checkField('name', $v, $isSubmitted) ? 'has-danger' : ''?>">
                  <label for="name">Name (required)</label>
                  <input class="form-control" name="name" id="name" type="text" value="<?php if ($name) echo $name; ?>">
              </div>
    
              <div class="form-group col-sm-12 col-lg-6 py-2 <?= checkField('email', $v, $isSubmitted) ? 'has-danger' : ''?>">
                  <label for="email">Email (required)</label>
                  <input class="form-control" name="email" id="email" type="text" value="<?php if ($email) echo $email; ?>">
              </div>
          </div>
    
          <div class="form-group py-2 <?= checkField('message', $v, $isSubmitted) ? 'has-danger' : ''?>">
              <label for="message">Message (required)</label>
              <textarea class="form-control" name="message" id="message" rows="8"><?php if ($message) echo $message; ?></textarea>
          </div>
    
          <div>
              <label for="recaptcha">Recaptcha (required)</label>
              <!-- Google Recaptcha code START -->
              <?php echo $captcha->render(); ?>
              <!-- Google Recaptcha code END -->
          </div>
    
          <div class="form-group">
              <button type="submit" class="btn btn-primary" name="sendMe" value="1">Submit suggestion</button>
          </div>
    
        </form>
    
    <?php
        $session->remove("flashMessage");
        $session->sent = false;
        echo $captcha->getScript();
    ?>

    It's one of those time where I learn best... by seeing how not to do something! I still got some ways to go here methinks. Terrible code, great learning experience. This will for sure be one of those posts that I look back on and laugh.

    On a plus note, it works (and I'm actually writing code and thinking about structure now which is awesome)

    form-example.jpg

    • Like 3
    • Haha 1
  11. 1 hour ago, FrancisChung said:

    @SamC, if you're struggling with any aspects no matter how trivial. give me a shout and I can try and help out. I've quite enjoyed the discussions we're having so far.

    It's comments like this that make me appreciate this awesome forum! Thanks for the offer :) 

    One quick question which is quite in line with the thing I'm struggling with at the moment:

    function CheckSendMe($errorMsg) {
      if ($input->post->sendMe) {
        return true
      }
      else
      {
      // how to use $errorMsg outside this function?
      $errorMsg = $errorMsg."Error MSg here"; //appending error msgs here
      return false;
      }
    }

     

  12. I'm trying this out this evening and tomorrow. Having a hard time at the moment, but this is simply down to inexperience in organising code so the practice will do me good. I'm trying to break things down, so once I've cracked this, I'll be much happier with my progress.

  13. 4 minutes ago, FrancisChung said:

    Should never be too lazy for learning. Keep feeding that curious inner child inside you!

    Totally this.

    The problem with the if (a && b && c) approach (I'm on a phone, best I can do!) is that the errors are different depending on what's wrong, hence the nesting.

    I don't like it so will def think of a way that suits me better. I like the idea of smaller functions as this is similar to the functional (JS) programming I'm learning at the mo. I'm becoming quite fond of it and finally made it to some lightbulb moments! I studied java for 6 months a couple of years ago and actually liked the OO approach too.

    I guess experience will highlight my personal preferences.

    • Like 1
  14. This is a general programming question. Is there a better way to structure something like this?

    if ($input->post->sendMe) {
      if ($v->validate()) {
        if ($captcha->verifyResponse() === true) {
    
          $message = "
            <html>
              <body>
                <p><b>Name:</b> {$name}</p>
                <p><b>Email:</b> {$email}</p>
                <p><b>Message:</b></p>
                <p>{$message}</p>
              </body>
            </html>
            ";
    
          $mail = wireMail();
    
          $mail->to($contactFormRecipient)
          ->from($email, $name)
          ->subject('Email from website...')
          ->bodyHTML($message);
    
            if ($mail->send()) {
              $session->flashMessage = "Thanks for your message!";
              $session->sent = true;
              $session->redirect($pages->get($contactPageID)->url);
            }
            else {
              $session->flashMessage = "Sorry, an error occured. Please try again.";
            }
    
          }
        else {
          $session->flashMessage = 'Recaptcha must be complete.';
        }
      }
      else {
        $session->flashMessage = 'Please fill out the fields correctly.';
      }
    }

    It's probably trivial to someone with more experience but I find it tricky to follow (even though it's short). Is four nested ifs considered too many?

    Just after some advice about different techniques on how to write something like this.

  15. On 04/01/2018 at 11:10 AM, szabesz said:

    The images alias is needed and it points to .../bower_components/uikit/src/images so that site.less compiles with the hard coded icon paths in UIkit.

    This is something I'm having a problem with now, the images are all two levels up from the css file. However, if using their build tools, something funky happens to the svg.

    Mine:

    .uk-offcanvas-bar .uk-divider-icon {
      background-image: url("../../images/backgrounds/divider-icon.svg");
    }

    Theirs:

    .uk-offcanvas-bar .uk-divider-icon {
      background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.2%29%22%20stroke-width%3D%222%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%3E%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A");
    }

    Seems if you use your own build tools and the images don't work unless you alias or copy the images folder somewhere. No mention of this here though:

    https://getuikit.com/docs/less#use-your-own-build-process

    • Like 1
  16. 40 minutes ago, szabesz said:

    I have CodeKit 3 because I do not have the time to learn the command-line in this case.

    Aha, I think I still got a license for that. I'm the opposite, I found the command line easier, and it makes me feel awesome cos I'm a geek.

    40 minutes ago, szabesz said:

    I tested including separate components only, but it is a pita to do so and I did not gain too much regarding the file size of site.css so I simply include them all.

    Sorry, I meant JS components here.

    I was messing about with webpack simply so I could just include certain parts but getting the uikit less to compile and output in the right place was a pita (even though I can do it easily with gulp, but I'm always experimenting, maybe my downfall that one tbh).

    Webpack is pretty simple if you're only using it for grabbing bits of JS and chucking it all together into a bundle.js file, probably why some people use gulp and webpack for their respective strengths. The 'uikit/build/' directory is difficult for me to understand what's going on. The web development landscape appears like this to me:

    if (easierWay()) {
      extractFun();
      makeMoreComplex();
      makeNewFramework();
    }

    :P

    • Like 2
×
×
  • Create New...