Leaderboard
Popular Content
Showing content with the highest reputation on 09/04/2017 in all areas
-
Also the CSS property hyphens can come in handy, although it is not yet supported without prefixes.3 points
-
Here's how I did it. Searching, sorting, filtering, pagination all works <?php namespace ProcessWire; /* @var $config Config */ /* @var $pages Pages */ /* @var $input WireInput */ /* @var $sanitizer Sanitizer */ // fields to return $fields = ['id', 'name', 'publishedStr', 'createdStr']; if ($config->ajax) { // sanitize inputs $search = $sanitizer->selectorValue($input->get->queries['search']); $sorts = []; if ($input->get->sorts) { foreach ($input->get->sorts as $f => $direction) { $key = $sanitizer->fieldName($f); $direction = $sanitizer->int($direction, ['min' => -1, 'max' => 1]); if ((!$key) || (!$direction)) continue; $sorts[$key] = $direction > 0 ? '' : '-'; // sort=field or sort=-field } } $page = $sanitizer->int($input->get->page, ['min' => 0, 'blankValue' => 1]); $perPage = $sanitizer->int($input->get->perPage, ['min' => 10, 'max' => 100, 'blankValue' => 10]); $offset = $sanitizer->int($input->get->offset, ['min' => 0]); // base selector $selector = [ "id>0", "include=all", ]; $selectorFiltered = array_merge($selector, [ "name|title*=$search", // change fields to search ]); $selectorFilteredLimited = array_merge($selectorFiltered, [ "limit=$perPage", "start=$offset" ]); // include sorts as sort=(-)fieldName foreach ($sorts as $f => $d) { $selectorFilteredLimited[] = "sort={$d}{$f}"; } // perform database query $totalCount = $pages->count(join(', ', $selector)); // # of all pages $queryCount = $pages->count(join(', ', $selectorFiltered)); // # of filtered pages $pageData = $pages->find(join(', ', $selectorFilteredLimited))->explode($fields); // data to return // output json header("Content-type: application/json"); $data = [ 'records' => array_values($pageData), 'queryRecordCount' => $queryCount, 'totalRecordCount' => $totalCount ]; echo json_encode($data); // stop return $this->halt(); } ?> <?php if (!$config->ajax): ?> <table class="table"> <thead> <tr> <?php foreach ($fields as $field): ?> <td><?= $field ?></td> <?php endforeach; ?> </tr> </thead> <tbody></tbody> </table> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Dynatable/0.3.1/jquery.dynatable.min.js"></script> <script> $('table').dynatable({ dataset: { ajax: true, ajaxUrl: '.', // current page ajaxOnLoad: true, records: [] } }); </script> <?php endif; ?> Here's a screenshot Although I think the code is clear, feel free to ask me if you have any questions. https://processwire.com/api/ref/wire-array/explode/ https://processwire.com/api/ref/sanitizer/ https://processwire.com/api/ref/pages/count/ https://processwire.com/blog/posts/processwire-2.6.8-brings-new-version-of-reno-admin-theme-and-more/#new-this-gt-halt-method-for-use-in-template-files2 points
-
How do you determine the $mode? What do you mean by selecting image? Also, where will you display images? On page edit screen? Frontend? Would a hook work for you? <?php namespace ProcessWire; // /site/ready.php wire()->addHookMethod('Pageimage::mode', function (HookEvent $e) { $img = $e->object; $mode = $e->arguments(0) $size = $e->arguments(1) ?? 400; if ($mode === 'summary') { $e->return = $img->size($size, $size)->url; // 400x400 image } else { $e->return = $img->width($size)->url; // 400x600 (or longer) image } }); // In your templates ?> <img src="<?= $page->images->first->mode('summary') ?>" /> <img src="<?= $page->images->first->mode('summary', 600) ?>" />2 points
-
Hej, some ideas: If you would consider such input as spam which does not really deserve proper display you could just cut it off inside the parent box with CSS: overflow: hidden; Another quick hack would be to insert the invisible character ­ after every so many characters in that "word", so that the layout does not break / the word breaks so it fits into the box. This character is invisible but it tells the browser that the word is allowed to break at that point. Have a look here and see what it does: https://jsbin.com/pipefid/edit?html,output ! I'd do this on the server side ... You could enhance this to some "real" hyphenation algorithm ... more or less sophisticated. As time and money allows ;P EDIT: I think often it is URLs that blow up the boxes - you could create a Textformatter which replaces long urls with a shortened version <a href="actual URL">shorter URL</a> cheers!2 points
-
Howdy, i was just wondering if there is already workaround in core that would automatically sniff for composer.json up on module installation and install dependencies? What i'm currently doing is that i include vendor folder into modules, but somehow this feels stupid as of core could just handle this part up on module installation and place dependencies under ROOT_PATH/vendor/? i tried quickly to look around for previous posts about this topic, but couldn't find anything. Cheers!1 point
-
Although PW has composer support, not every setup has composer. This means some modules cannot function as their dependencies cannot be installed during module installation. When developing a module, if I need a library, I install it with composer inside module directory and require __DIR__ . '/vendor/autoload.php' inside __construct(), or init() or ready(). All references with use keyword resolve automatically.1 point
-
1 point
-
1 point
-
You're right, thanks for the heads up. I imagined that as a linear sequence, you're getting to know the company and at the end you get to the services but now that you mention it, the transition comes as a surprise and feels unsolicited. I'll have to improve that.1 point
-
It's good practice to use this any time you have a container of limited width and content from an external source (like comments or an RSS feed) or perhaps where a content author may include a link, which can often be long enough to break a layout.1 point
-
The issue is that the script won't be imported if only text fields in a repeater are using a slider. The field is not rendered until AJAX-time, and so the script cannot be imported. I'd recommend that you simply load the assets at page-edit time (separate hook). Alternatively, and this is just an untested theory because I need to rush out, maybe you should rather hook to the page edit event instead of the text render event. You could then check for all fields (repeaters too) on the page to see if they need a range slider, and then import the assets. That way, all fields are considered, and the event-fired will bubble from the repeater, thus setting the slider.1 point
-
It is solved finally. Used the multisite by @Soma I did not find in the documentation that I had to add all domains also to site/config.php. When I did that, all worked like a charm.1 point
-
Thanks @flydev and @kongondo Github issue posted: https://github.com/processwire/processwire-issues/issues/3601 point
-
put this in your ready.php wire()->addHookBefore('Page::addable', null, function ($e) { if ($e->object->parents->count > 1) $e->object->template->noChildren = 1; });1 point
-
Hi guys so currently am writing a detailed tutorial about creating Modules, I have never created a module because i don't know all the classes and interfaces required, so this is like a detailed research for me, this is how i learn things by writing articles. However I might make some mistakes so i decided to make it on Google Docs to get comments and feedback, before posting on my website and Processwire tutorial site, this is going to be one heck of a detailed tutorial. Here is the link I will be updating it https://docs.google.com/document/d/1VA_WK-5qbnq3Ux_EOW3p92IcjbAcVZJ0aewIiFxmv2Q/edit# However I wanted to get a clear picture of the following Process Class and ConfigurableModule i noticed some modules require it and some don't My interpretation is that Modules with admin setting pages uses ConfigurableModule and Process are modules who require access to $this->pages and that sort Thanks all1 point
-
Thanks @adrian! I rerecorded the video many times before I could make it watchable. Trust me, you wouldn't say the same thing for the very first ones About the field access rules. Yeah that's true. By default the behavior is the opposite to the one in ProcessWire. I think it would be better for security if the module initially treats everything private. But I get what you mean. In cases where you have dozens of fields in one template, it would be too tedious to configure access for each of them. That's why there is an option to reverse the behavior in the advanced section of the module configuration. You can learn more about it here. This option basically makes all fields without Access rules available to the public and you can restrict access by enabling rules only to couple ones.1 point
-
I am sending the NEW version of Simple Contact Form. This new version uses module MarkupGoogleRecaptcha in order to render the captcha. If you have downloaded Valitron validator through composer then add the following line to your _head.php: include(dirname(__FILE__) . "/../../vendor/autoload.php"); or include the old-way on the top of "partials/contact/_controller.php": require(dirname(__FILE__) . "/../../vendor/vlucas/valitron/src/Valitron/Validator.php"); There are 3 files currently: contact.php (template) partials/contact/_controller.php partials/contact/_email.php First file contact.php: <?php namespace ProcessWire; include('partials/contact/_controller.php'); ?> <h2><?php echo __('Contact Form') ?></h2> <?php if($session->flashMessage):?> <div class="alert <?php echo $session->sent ? 'alert-success' : 'alert-danger'?>" role="alert"> <?php echo $session->flashMessage;?> </div> <?php endif;?> <form id="contact-form" action="<?php echo $page->url;?>" method="post"> <div class="form-group <?php echo $v->errors('name') ? 'has-error' : ''?>"> <div class="input-group"> <span class="input-group-addon"><i class="fa fa-user"></i></span> <input required class="input-lg form-control" name="name" id="name" type="text" value="<?php echo $name?>" placeholder="<?php echo __('Name') ?>"> </div> </div> <div class="form-group <?php echo $v->errors('email') ? 'has-error' : ''?>"> <div class="input-group"> <span class="input-group-addon"><i class="fa fa-envelope"></i></span> <input required class="input-lg form-control" name="email" id="email" type="email" value="<?php echo $email?>" placeholder="<?php echo __('Email') ?>"> </div> </div> <div class="form-group <?php echo $v->errors('phone') ? 'has-error' : ''?>"> <div class="input-group"> <span class="input-group-addon"><i class="fa fa-phone"></i></span> <input class="input-lg form-control" name="phone" id="phone" type="tel" value="<?php echo $phone?>" placeholder="<?php echo __('Phone') ?>"> </div> <div class="field-notice" rel="phone"></div> </div> <div class="form-group <?php echo $v->errors('message') ? 'has-error' : ''?>"> <div class="input-group"> <span class="input-group-addon"><i class="fa fa fa-quote-left"></i></span> <textarea rows="7" class="input-lg form-control" placeholder="<?php echo __('Message') ?>" name="message" id="message"><?php echo $message?></textarea> </div> </div> <div class="form-group"> <?php echo $captcha->render()?> </div> <button id="contact_send" class="btn btn-default" type="submit"><i class="fa fa-paper-plane" aria-hidden="true"></i> <?php echo __('SEND') ?></button> </form> <?php $session->remove('flashMessage'); $session->sent = false; echo $captcha->getScript(); ?> Then file partials/contact/_controller.php: <?php namespace ProcessWire; use Valitron\Validator; $captcha = $modules->get("MarkupGoogleRecaptcha"); $name = $sanitizer->text($input->post->name); $email = $sanitizer->email($input->post->email); $phone = $sanitizer->text($input->post->phone); $message = $sanitizer->text($input->post->message); $v = new Validator([ 'name' => $name, 'email' => $email, 'message' => $message, ]); $v->rule('required', ['name', 'email', 'message']); $v->rule('email', 'email'); $contactFormRecipient = 'your@company.com'; if ($input->post->name) { if ($v->validate()) { if ($captcha->verifyResponse() === true) { $subject = 'Contact Form'; $messageHTML = include_once('partials/contact/_email.php'); $mail = wireMail() ->to($contactFormRecipient) ->header('Reply-To', $email) ->subject($subject) ->bodyHTML($messageHTML); if ($mail->send()) { $session->flashMessage = __('Thank you for your message! We will get back to you.'); $session->sent = true; $session->redirect($page->url); } else { $session->flashMessage = __('Mail not sent. Error occured.'); } } else { $session->flashMessage = __('Recaptcha Validation Error'); } } else { $session->flashMessage = __('Please correct the errors and try again.'); } } ?> And finally partials/contact/_email.php: <?php ob_start();?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title><?php echo $subject; ?></title> </head> <body> <h1>New message from <i><?php echo $name; ?></i></h1> <p><?php echo $message; ?></p> <hr> <h3>Contact Info:</h3> <p><strong>Name:</strong> <?php echo $name; ?></p> <?php if(!empty($phone)):?> <p><strong>Phone:</strong> <?php echo $phone; ?></p> <?php endif;?> <p><strong>Email:</strong> <a href="mailto:<?php echo $email; ?>"><?php echo $email; ?></a></p> </body> </html> <?php return ob_get_clean();?> Hope you like it!1 point
-
I am happy that you like it @Sebastian, thank you for your support. I started the thread here because I thought this would be more like a discussion on how GraphQL and ProcessWire could fit together and wanted to get some feedback first. But this thread quickly become this module's official place here in the ProcessWire forums. Also @teppo included the link to this thread as the "dedicated support forum thread" in the 143 issue of the weekly.pw (which I was flattered to see ). Now I don't really know how to go on with this thread. Should we abandon it and start new thread in the modules section? Or maybe this thread could be moved to modules section? What @moderators think of this? Meanwhile, for those who are following this thread I wanted to mention that there are some additions in the dev branch, such as mutations that allows you to create/update pages and there is also support for FieldtypeMapMarker field. I stopped developing the module for some time because I thought that it needed a good testing before moving further with it and decided to built an SPA using this module, to see if there is something that need to be added or changed. But then I got carried away and started to make usage of third-party APIs such as Wikipedia and GoogleMaps. As a result the app does not make heavy usage of the ProcessGraphQL module, but it is still relevant to showcase the module's abilities. It is a US Skyscrapers app, duh... You can see it live here and the source code here (though I doubt that the code will interest you if you are not a React developer). I was finished with this demo SPA just couple of days ago. Now I will be back to continue to work on this module again.1 point
-
$input->whitelist does not store values. It does just mark $input variables of the current request, so they can be appended to (pagination) links as ?my_data=xyz variables, which on the next request can be read again. If non of those links is clicked the values are gone. The most prominent use case is preserving any user filter values while going through pages of results. Having the values in the url also makes sure you can send that url to your peer and he or she can open it as well. Anything you don't want the user to see/modify you'd use the session.1 point
-
Thanks - I wasn't aware of that system tab before. I'm sure Ryan must have had a reason for disabling the trashing of user pages by default but in testing so far it seems okay. I had to uncheck the "Don't allow pages to be moved" option for the user template too in order to allow trashed users to be restored.1 point
-
If you enable $config->advanced you can have a look at the system tab in each of your templates. There you can prevent pages of a template to be trashable. But I'd be cautious with enabling the trash for the user template as you might find issues with trashed users not behaving exactly like non-existing ones.1 point
-
Interesting to see someone else using PW + Vue.js. Building components in Vue and using things like webpack and hot reloading has been kind of eye opening for me. I've just finished a small website/shop that uses Vue to render some elements. Things like the cart made a lot of sense, calculating/updating prices as quantities are edited for example. The cart in this instance is essentially a popover and doesn't need to be rendered immediately or visible for SEO purposes, which was a primary concern with this site. Where Vue was used to render actual content, I ended up writing duplicate templates in both PHP and using Vue components (the latter overwrites the former) which is obviously not ideal. I'm looking to eventually transition to using Vue/Vuex/Vue Router, moving away from writing PHP based templates and just using PW to expose JSON data via some kind of custom API. But as I almost exclusively build websites and not apps, things like SEO have always been a bit of a concern. I need to read a lot more about how to handle server side rendering with Vue for example. I wonder if there are enough people here who would be interested in some kind of PW/Vue/Webpack project template for use with Vue CLI or similar?1 point
-
Sure – where I work at we did exactly that, and haven't looked back since. First of all, it's important to understand that at it's core ProcessWire is a (web) application framework. We prefer to call it a content management framework, but that's not very far from what most web applications do: they store, process, and output content. The way I see it, the main difference to so-called traditional frameworks is that modelling and handling data is a built-in feature, not something you have to reinvent on a case-by-case basis. I have rebuilt a couple of old projects from the scratch using ProcessWire, and in all of those cases this has saved me a lot of time and made most of the model layer obsolete. Before we started using ProcessWire we were doing sites with another CMS and custom applications with Zend Framework. At the beginning I had this idea that we would use ZF for "apps" and ProcessWire for "sites", but in just a few short months we realised that there just wasn't anything we could achieve with ZF that couldn't be done, usually with less work, with ProcessWire. Sure, sometimes we pull other libraries into the mix, but that's not a shortcoming; in my opinion it's just good design Regarding some of the things that have been mentioned here: One thing I was originally missing from Zend Framework was a clearly defined structure. Built-in "one file per template" concept is great for simple sites, but that's just about it. This is why I built the original version of my MVC project. It's not perfect, but it has served us well for years. To get most out of ProcessWire you really should be using it's data modelling abilities. Sure, you can still mock up your own data structures and write SQL to fetch content from the database etc. but that's kind of missing the point: ProcessWire makes data modelling a breeze and the selector engine is both flexible and secure. Some users prefer to build custom management panels, but in my opinion that's another thing you should try to avoid. ProcessWire's admin GUI is flexible and extendable (see Process modules), and again: in most moderately sized projects it can easily save you days of work. For routing you can use page URLs, but I'd also suggest looking into URL segments. For me, coming from the world of Zend Framework, templates are a lot like controllers and URL segments make it easy to implement the concept of actions Try not to invent your own access management system. ProcessWire has a very good implementation of RBAC already in place, and if you need more flexibility, I'd suggest looking into modules such as Dynamic Roles and/or User Groups. Rolling out your own solution is risky, tends to cost a lot, and just generally speaking is a very bad idea. Form validation has been mentioned twice here already. I don't have much insight into this, except that in the beginning we used to build forms using Zend Form, which has it's own validation built-in. That was always a bit tricky (not because of ProcessWire), and these days we use Form Builder for pretty much every form-related need. Sure, it's a commercial module, but it has saved us so much time that the price is absolutely not an issue. In my opinion the answer to your original question is yes and no: ProcessWire can't substitute an application framework because it is an application framework1 point
-
I must admit i dont know that much about encryption myself, so I have used the following example with a little modification. http://stackoverflow.com/questions/16600708/how-do-you-encrypt-and-decrypt-a-php-string/16606352#16606352 Do not use BLOWFISH and EBC (this it too predictable) as in the example, instead of that i'm using RIJNDAEL_128 (or 256 if you like) and CBC I also recommend to use a long encryption key, maybe something above 1024 characters, or even 2048 long function encrypt($pure_string, $encryption_key) { $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); $encrypted_string = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_CBC, $iv); return strtr(base64_encode($iv.$encrypted_string), '+/=', '-_.'); } function decrypt($encrypted_string, $encryption_key) { $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $encrypted_string_dec = base64_decode(strtr($encrypted_string, '-_.', '+/=')); $iv_dec = substr($encrypted_string_dec, 0, $iv_size); $decrypted_string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $encryption_key, substr($encrypted_string_dec, $iv_size), MCRYPT_MODE_CBC, $iv_dec); return trim(utf8_decode($decrypted_string)); } update: The base64 encode/decode i threw in so that it should be possible to use the encrypted strings as url segments and so are able to use it together with procache1 point
-
TL;DR: I know, there is already an Validation Module. And it wasn't my intend to build another module. But I needed it in so many places which lead to duplicate code and suddenly there was a module Perhaps it's useful to someone. At the moment there are only validators I need in my current project, if you miss one, please tell me or (preferred) send a pull request! ------ This module provides a set of useful validation methods using ProcessWire sanitizers. Example Usage: $conf = array( 'username' => array('isEmpty', 'isUnique' => array('ident' => 'name', 'sanitize' => 'username')), 'pass' => array('range' => array('min' => 6, 'max' => 20)), 'pass_confirm' => array('isEqual' => array('equal' => 'pass')) ); $validator = new Validator; $validator->setConfig($conf); if (!$validator->isValid()) $errors = $validator->getErrors(); You can also use it to validate POST requests: $validator = new Validator; $validator->setConfig($validatorConf); // validation failed if (!$validator->isValid()) { $data = array( 'success' => false, 'errors' => $validator->getMessages() // $validator->getErrors() ); $this->returnJson($data, 400); } ❯ http -f POST http://pw.dev/v1/user/ email=info@com username=exampleUser pass=short { "errors": { "email": [ "Please enter a valid email address." ], "pass": [ "This field must be at least '6' characters.", "'short' must contain at least one digit character" ] }, "success": false } For a detailed documentation please have a look at the guides: Installation Usage Available Validators Error Messages phpunit Testing1 point
-
This should work. create a date field besides the counter field. and put change the code to this: if (!$user->isSuperuser()) { if( date('Ymd', strtotime($page->day)) != date('Ymd') ) { $page->counter = 0; $page->day = today(); } else { $page->counter += 1; } $page->of(false); $page->save('counter'); $page->of(true); } echo $page->counter; written in the browser and not tested. But it should give you an idea.1 point
-
The trick with counters is to remove yourself from skewing the count. I find that when writing blog posts I load the page several times and this would increase the count several times. You could go a step further with diogo's code and do this to exclude your account (superuser) from increasing the count: if (!$user->isSuperuser()) { $page->counter += 1; $page->of(false); $page->save('counter'); $page->of(true); } echo $page->counter; Or you could do the same for multiple roles if you have different editors for your site (my example has some fake ones named "news" and "sports" below): if (!$user->hasRole('news') && !$user->hasRole('sports')) { $page->counter += 1; $page->of(false); $page->save('counter'); $page->of(true); } echo $page->counter; Just a thought1 point
-
Process is just an abstract module designed for extending. Its behavior is pretty simple in that it calls a method in the module matching the first URL segment. If there is a URL segment, it calls execute[urlSegment] in your Process module (if it exists), with the first character of the url segment in uppercase. For example, if your URL segment was "new", it would call executeNew(). If there is no URL segment, then it just calls execute(). So it is just a basic mapping of URL segments to methods with execute() being the default. I suppose this is kind of similar to code igniter except for the naming (with execute… being part of it) and PW doesn't pass any arguments to the execute() functions... rather you can retrieve them from $input->urlSegment($n) if you want them. All of those execute[?] methods just return the content (markup) to be output. It is output directly in the #content div of the admin template. Unless the call was initiated by ajax, in which case just your output is sent (without the HTML document). Another differentiating point of Process modules is that if you have a .css or .js file in the same directory as the .module, with the same name as the module, it will be automatically loaded. Lastly, if you edit any admin page, you'll see it lets you select what Process module to execute on that page. This logic can be applied beyond just the admin template if you want it to. It's a bit late here and I may be forgetting some things, so let me know if I'm not making sense or can provide any more info. But just wanted to reiterate that Process modules are very simple and there's not much to it.1 point