SamC

Members
  • Content count

    609
  • Joined

  • Last visited

  • Days Won

    3

SamC last won the day on December 28 2017

SamC had the most liked content!

Community Reputation

500 Excellent

2 Followers

About SamC

  • Rank
    Hero Member

Profile Information

  • Location
    Surrey, UK

Recent Profile Visitors

1,116 profile views
  1. This sounds promising. Will have to check this out. Looking for a one stop solution as I don't want to get distracted from my current activities, learning plain PHP and JS (without any frameworks).
  2. Hi @BitPoet, thanks for the info. Is there an alternative for me re: debugging? I installed xdebug just to use it in visual code but I'm always willing to change tactics. Or maybe change the server. Do the same problems exist with windows WAMP?
  3. Did you try my suggestions? What's the output of the stripped down code I posted? Seems a bit extreme changing platforms and buying a theme just to get a menu working. There must be a reason why it's happening, just gotta find it (as annoying as it may be).
  4. My localhost sites suddenly slowed right down and I noticed whenever I clicked a link or refreshed the page, the ports on the XAMPP contol panel for apache keep changing, like flicking through a bunch of ports before settling on '80, 443'. I tried changing the port to 8080, then 8888, but still the same thing. never had this issue before and been using it for years. I use vhosts but I've never experienced this before. I've attached a vid to show what I'm talking about. I'm trying to avoid reinstalling xampp because I have a bunch of local databases in there. https://www.dropbox.com/s/59jayjquuwrntgp/apache-ports.flv?dl=0 Does anyone know what might be causing this? The only thing I've change is adding xdebug to php.ini for visual code, not changed any apache conf settings.
  5. 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.
  6. 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. Beats me, but I've used this a few times.
  7. Hi @Donald Sounds weird. So they work on the homepage but not on any other page? Do you mean don't get rendered as in (a) they are in the html source but don't show up on the page or (b) not in the source (dropdown for items 2 to 5). Are the pages hidden or unpublished? Does the same thing happen when logged in and logged out? i.e. is it some kind of permissions issue? What if you just take out all the uikit stuff for now and do it basic and try and work through it bit at a time. I find that useful sometimes, like: <?php $root = $pages->get("/"); $children = $root->children(); // insert the following line $children->prepend($root); ?> <ul> <?php foreach($children as $c):?> <li> <a href="#"><?= $c->title; ?></a> <ul> <?php foreach($c->children as $cc): ?> <li><a href="#"><?= $cc->title ?></a></li> <?php endforeach; ?> </ul> </li> <?php endforeach; ?> </ul> Just throwing a few ideas out
  8. 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?
  9. I just tried this with the cdn on my local site: <!-- UIkit CSS --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.38/css/uikit.min.css" /> <?php $root = $pages->get("/"); $children = $root->children(); // insert the following line $children->prepend($root); ?> <nav class="uk-navbar-container" uk-navbar> <div class="uk-navbar-left"> <ul class="uk-navbar-nav"> <? 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"> <? foreach($c->children as $cc): ?> <li><a href="<?= $cc->url ?>"><?= $cc->title ?></a></li> <li class="uk-nav-divider"></li> <? endforeach; ?> </ul> </div> </div> </li> <? endforeach; ?> </ul> </div> </nav> <!-- UIkit JS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.38/js/uikit.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.38/js/uikit-icons.min.js"></script> Menu is the same on every page. I'm not sure how items an disappear off the menu on different pages. Maybe another member would be more eagle eyed.
  10. 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.
  11. I'll look into them, thanks @rick Using visual studio code at the mo.
  12. Ouch 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/
  13. 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.
  14. 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.
  15. Yeah, still working on it. I'll check that link out, looks interesting, thanks