abdus
Members-
Posts
743 -
Joined
-
Last visited
-
Days Won
42
Everything posted by abdus
-
Since the module hooks into Pages::saveReady and Pages::saveFieldReady, it should be called whenever $somePage->save() function is called and a change to DB is necessary.
-
How to find usernames by userid array, return as key value array
abdus replied to modifiedcontent's topic in General Support
When building selectors, you can combine multiple values by OR (pipe | character). This is a way to say "find me users with id 1 or 2 or 3 ...", and PW should return every user whose id is in that list. Since "id=1|2|3" itself is a selector, yes, you can combine it with other terms as well. array_combine takes in two parameters, first one is for keys, second is values, and creates a new array from those keys and values. So I used it with usernames as keys and ids as values to get [username => userid] type of array. But you can achieve the same result with plain foreach loop as well. Using your sample code you'd use <?php function qa_get_public_from_userids($userids) { // userids is an array of integer ids [13, 456, 74] etc // global $users; // you can use wire() function instead of globals to access PW variables inside functions $userlist = wire()->users->find('id=' . join('|', $userids)); // usernames are stored in 'name' property // Unless you need [fullname => id] array, you should use 'name' // create an array of [username => userid] $nameWithId = []; foreach ($userlist as $u) { $nameWithId[$u->name] = $u->id; } return $nameWithId; } Also the reason you're getting errors from users() function is they're a part of Functions API, which is disabled under a switch. -
How to find usernames by userid array, return as key value array
abdus replied to modifiedcontent's topic in General Support
This is untested, but you can use something along the lines of this <?php $userIds = [1, 2, 3]; // list of ids // build a selector like id=1|2|3|4 $userList = users()->find("id=" . join('|', $userIds)); $names = $userList->getProperty('name'); $ids = $userList->getProperty('id'); // create an array of [username => userid] $namesToIds = array_combine($names, $ids); // or use array reduce // you need basic PHP arrays, which you can get with WireArray::getArray() method $namesToIds = array_reduce($userList->getArray(), function ($carry, $user) { // perform some other checks // ... $carry[$user->name] = $user->id; // fill array with name => id pairs return $carry; }, []); // start with an empty array -
This topic has some useful links on building custom button/command
- 1 reply
-
- 1
-
-
How to get 404 exception message inside template file?
abdus replied to abdus's topic in API & Templates
Huh, turns out there's this method inside ProcessPageView class <?php // /wire/core/ProcessPageView.php /** * Hook called when the pageview failed to finish due to an exception. * * Sends a copy of the exception that occurred. * */ public function ___failed(\Exception $e) { $this->wire()->setStatus(ProcessWire::statusFailed); } which I should be able to hook and access the exception and message with <?php wire()->addHookBefore('ProcessPageView::failed', function(HookEvent $e) { $ex = $e->arguments(0); $message = $ex->getMessage(); }) But, it's not firing when hooked inside /site/ready.php. In fact it does not appear to be used anywhere inside the core at all. -
While coding some templates I need to throw some Wire404Exceptions, which, expectedly, sets correct headers and renders 404 page. Since all these exceptions are extending Exception class, I can call its constructor with a message like this <?php $demoName = sanitizer()->pageName(input()->urlSegment1); $demo = pages('template=demo, name=$sanitizedPagenameFrom'); if(!$demo->id) { throw new Wire404Exception("Demo named $demoName cannot be found, but check out its source page instead"); } // inside template 404.php // somehow get the error $message echo "<h1>Page does not exist</h1>" echo "<p>$message</p>" What I want to do is to somehow catch this exception, or get inside 404 template the message that I set earlier in some module or another function, so that I can notify the user with a better message than "Error 404: Page not found". There's some parts inside core that handles this sort of things but they're not utilized. <?php // inside /wire/core/ProcessPageView.php public function ___execute($internal = true) { // ... try { // try to render the page } catch(Wire404Exception $e) { return $this->pageNotFound($page, $this->requestURL, false, '404 thrown during render'); } // ... } protected function ___pageNotFound($page, $url, $triggerReady = false, $reason = '') { // reason is not used, or passed to 404 template } So, is there a way to manually catch the exception thrown? For instance, with Silex all exceptions can be intercepted using <?php $app = new Silex\Application(); $app->error(function (\Exception $e, $code) { exit('asd'); });
-
Which module methods are called from front-end rendered forms
abdus replied to rick's topic in General Support
Glad to be of help @rick -
Which module methods are called from front-end rendered forms
abdus replied to rick's topic in General Support
Process modules (ones that extend Process class) can have execute[Action] methods that are called with their respective urlSegments. In case of your example, the method comments portrait the exact conditions where ___executeFields is executed with /fields urlSegment, for instance. The problem with this approach is that you cannot (or should not) use them on frontend, as Process modules are intended for backend use (unless I'm mistaken). However, calling the method depending on urlSegment is quite simple with the snippet I posted earlier. You can use something similar to following to call execute[Action] methods <?php public function ready() { // accept only single urlSegment if($this->input->urlSegment2) throw new Wire404Error(); // always sanitize user input $method = $this->sanitizer->alpha($this->input->urlSegment1); if(!$method) return; // or perform more sophisticated checks // respond to execute[Action] methods // such as executeList() with /list urlSegment // or executeFields() for /fields urlSegment // create camelCase method name // /json urlSegment will be intercepted by executeJson() function $method = "execute" . ucfirst($method); if (method_exists($this, $method)) { $this->{$method}($param1, $param2); } else { // method does not exist throw new Wire404Exception(); } } public function executeJson() { echo json_encode(['a' => 1, 'b' => 2]); } Put this inside your init() or ready() method in your module to relay the request to specific functions. -
How are you using it? Is it like this one? https://processwire.com/docs/tutorials/using-custom-page-types-in-processwire/
-
You can use this hook to remove Settings tab from page edit pages <?php wire()->addHook('ProcessPageEdit::buildFormSettings', function (HookEvent $e) { // get the page currently being edited $page = $e->object->getPage(); // check its template if($page->template->name !== 'some-template') return; // remove settings tab $e->object->removeTab('ProcessPageEditSettings') })
-
Which module methods are called from front-end rendered forms
abdus replied to rick's topic in General Support
I am not sure you can access Process modules (pages with admin template) as a guest. If you need to know what page the request is sent to, and perform operations depending on the page, or need some functionality from other autoload modules, then you'd do your checks under ready() function. If you don't, then both init() and ready() is fine. To intercept the requests, you can create a new template called api in your install() function (and remove in uninstall() function), then set it to accept urlSegments (as many as you want), then create a page with that template under root page, then inside your module check if request is made to that page and to that specific urlSegment. Optionally return 404 for all other urlSegments you're not interested in like this <?php $method = $this->sanitizer->alpha($this->input->urlSegment1); if(!$method) return; if (method_exists($this, $method)) { $this->{$method}($param1, $param2); } else { // method does not exist throw new Wire404Exception(); } You can build such a module like this <form action="/api/sendmail"> ... </form> <?php namespace ProcessWire; class MyModule extends Wire implements Module { public static function getModuleInfo () { return [ // ... 'autoload' => true // ... ] } public function ready() { if ($this->wire()->input->requestMethod() === 'POST') { // perform other checks if($this->input->url !== '/api/sendmail') return; // this is the request we should be intercepting $data = $this->wire()->input->post; $name = $this->sanitizer->text($data->name); $email = $this->sanitizer->email($data->name); if($this->sendMail($name, $email)) { // email sent successfully } else { // email failed } } } public function sendMail($name, $email) { $mailer = $this->wire()->mail; // check if any mail is sent successfully return 0 < $mailer->send( 'test@example.com', // to address 'pw@myhost.com', // from address 'Test post, please ignore', // subject 'Hey there, this is a test mail' ); } } -
Check out the module's support page, it has a fairly detailed tutorial
-
Warning about Server Locale after update from 3.0.52 > 3.0.53 - Help!
abdus replied to EyeDentify's topic in General Support
You won't be modifying core files. You'll be working on translation files. From the top dropdown menu, go to Setup > Languages > [en/de] > Core Translation Files > Find Files To Translate > (pick language support module) > Submit > replace the values- 40 replies
-
- 3
-
-
- serverlocale
- 3.0.52
-
(and 1 more)
Tagged with:
-
In case someone else encounters this issue, here's how I solved it. You will need sudo access to the server. Go to https://curl.haxx.se/docs/caextract.html You'll see on the bottom a command to download the latest CA certificate bundle. curl --remote-name --time-cond cacert.pem https://curl.haxx.se/ca/cacert.pem Download the bundle to some directory and copy it to /etc/ssl/certs/cacert.pem with sudo cp cacert.pem /etc/ssl/certs/cacert.pem Find out where php.ini is stored using locate php.ini # check for ..../fpm/php.ini then open php.ini inside some editor sudo nano /etc/php/7.0/fpm/php.ini Find where CA certs are defined with CTRL + W (search) and type cafile and hit enter, cursor will jump to the correct line Uncomment the line (remove semicolon) and type /etc/ssl/certs/cacert.pem after = sign. Restart php service with sudo service php7.0-fpm restart Hopefully this will be enough to get rid of fsockopen(): SSL operation failed with code 1 error.
-
You can use strpos() function <?php if (strpos($page->path, "/blog/posts/") === 0){ echo "..."; } ?>
-
If you have SSH access to the server, run dpkg-reconfigure tzdata and pick the correct timezone (no root access is needed)
-
mini.css at 7KB gzipped is not much different than Foundation framework at 12KB gzipped or Foundation at 19KB gzipped, both of which are a lot more feature packed. This one is much smaller at 2KB gzipped that you can just inject it into HTML and not worry about extra bloat. Great if you're building something small and dont want to override much. https://milligram.github.io/
-
I am following a similar approach, but I don't reduce objects to simpler states, I just pass them along to partial templates and use PW specific properties. Your approach is more portable with better separation, but unless you're thinking of migrating your code/template to other systems, where you'll be passing around arrays with same set of same keys, it doesn't make much difference. And not mapping objects to arrays presumably reduces process time/RAM usage as well. I think of my partial templates as components. Although much of hard work is abstracted away from them inside actual template files, components have specific functions available to them. Here's my navigation # Navigation is defined in YAML Blog: href: /blog/ class: highlight Labs: href: /labs/ Works: href: /works/ GitHub: href: https://github.com/abdusco target: _blank and here's its partial <?php namespace Processwire; /* @var $links WireArray */ $linkList = []; if (!function_exists('renderLinks')) { function renderLinks($links, $linkClass) { $out = []; foreach ($links as $name => $attrs) { if(page()->path === $attrs['href']) continue; $anchor = ''; // generate attributes string $attrs = $links[$name]; $class = $attrs['class'] ?? ''; $attrs['class'] = $class . $linkClass; foreach ($attrs as $attr => $val) $anchor .= " $attr='$val'"; $anchor = "<a $anchor>$name</a>"; $out[] = $anchor; } return $out; } } ?> <nav class="pad t--bold"> <?php foreach (renderLinks($links, 'footer-nav__link pad inline-block link link--b-no') as $link) echo $link; ?> </nav> Since I'll be passing the same YAML array, and not use renderLinks anywhere else, I keep them inside my partial. If I need to use this navigation elsewhere, I'd probably abstract link class (footer-nav__link) as a parameter I pass inside wireRenderFile, or refactor that class into a more general one.
- 1 reply
-
- 1
-
-
Inside /wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module there's a commented section (with @todo comments) for enabling textformatter options, again inside /wire/modules/Fieldtype/FieldtypeComments/Comment.php, textformatter settings are bypassed and comment is presented as is. When I uncomment these sections, and add a few formatters, they work just fine :). Is there any reason why these sections are commented out? Also, is there a way for people to delete their comments?
-
You're absolutely correct. If the request (AJAX or plain HTTP) is made with a query string (x-www-form-urlencoded) or file input (multipart/form-data) $_POST is populated as usual, but not when it's anything else. http://php.net/manual/en/reserved.variables.post.php
-
Glad to help, @ryanC You do not have to set it to 1, you can also get the first image ($match->result_pic->first()->url) or use tags ($match->result_pic->getTag('some-tag')). And if you rename your field to a more generic name, you can reuse it across many templates, reducing the work DB engine has to do.
-
@neonwired when I use $.ajax({ url: '/endpoint/', type: 'GET', data: { id: 4, ajaxfunction: 'someaction' }, success: function(data) { console.log(data); } }) where endpoint just echoes the request back <?php header('Content-Type: application/json'); echo json_encode(input()->get->ajaxfunction); return; and it works (I get my request back, and can access $input->get->ajaxfunction just fine) Long shot, but can you try using capital GET as ajax type (instead of get)?
-
How do you make the AJAX request, can you post the relevant JS code? Are you using jQuery/Zepto etc
-
For user generated content, I use textarea fields with Parsedown (a flavor of Markdown) module in tandem with this module for images. This provides a simple syntax for semantic HTML (accepts raw HTML as well). For the parts that that are not open to user modification, I simply write HTML inside PHP files, and sprinkle PHP echo (<?= $var ?>), foreach, if constructs etc here and there. While starting out, I put everything in one PHP file, and as I build the layout and styles, I gradually refactor some parts into their own partial templates. I heavily utilize partial templates with wireRenderFile() and region() functions (you'll need to enable Functions API inside config.php as $config->useFunctionsAPI = true). I renamed wireRenderFile as partial(), (you can go even shorter with p()) like this. <?php /** * Renders partial with given data and returns generated markup * * @param string $template template to use. must be located in templates/partial * @param array $data associative array to pass to template * @return bool|string generated markup */ function partial(string $template, $data = []) { return wireRenderFile($template, $data, [ 'defaultPath' => paths()->templates . 'partial' ]); } and employ it inside template files and other partial templates as <?php // /site/templates/blog.php $posts = pages("template=post, parent=$page, sort=-published"); region('header+', partial('page-header', ['class' => 'header--large wrap'])); region('content+', partial('post-list', ['posts' => $posts])); include_once('layouts/basic.php'); For navigation, I use a custom template called nav, which only has a YAML field like this, and switch to other navigations (specified using other pages with nav template) on different contexts. - title: Blog url: /blog/ - title: Somewhere url: https://google.com external: true
- 1 reply
-
- 1
-
-
$this->input->post is not populated for AJAX requests (caused by PHP, see this). I use something like this. <?php $raw = file_get_contents('php://input'); // optionally check for Content-Type headers if(is_array($parsed = json_decode($raw, true))) { $this->wire->input->post->setArray($parsed); $this->config->ajax = true; } // use $this->input->post->varName as usual