-
Posts
1,331 -
Joined
-
Last visited
-
Days Won
61
Everything posted by BitPoet
-
width or height exceeds limit in OpenPixelCache/3911
BitPoet replied to bernhard's topic in General Support
Are there actual width or height limits (usually given in KP) in policy.xml that your image might hit? -
Just a typo, a missing ">" in this line, $item->children-count() vs. $item->children->count(). <?php if($item->children-count()): // if there is a child page, loop through all the child page ?>
-
Here's a somewhat versatile, albeit not overly beautiful to look at (in the backend) approach using Hanna Code. It's not really wysiwyg in the backend, but it's easier to maintain than hardcoding stuff in the template. A short step-by-step instructions (looks more complicated than it really is): Download and install Textformatter Hanna Code module Download and install Hanna Code Dialog module Go to "Setup" -> "Hanna Code" and create a new Hanna code, let's name it "languagelink", with type "PHP" and the two arguments "targetlink" and "linktext". In the "Code" tab, add the following PHP code: <?php if(! empty($targetpage)) $tgt = $pages->get($sanitizer->pagePathName($targetpage)); else $tgt = $page; $url = $tgt->localUrl($user->language); echo "<a href='{$url}'>{$linktext}</a>"; Go into the configuration of your CKEditor field. On the "Details" tab, add the Hanna Code Textformatter, which will then be applied to the field content each time it is rendered: On the Input tab, go to the CKEditor Toolbar settings and add HannaDropdown on its own line, which will add the menu entry for CKEditor: Now you have everything set up and can start inserting your languagelinks. Edit the page in question. You'll now find the menu entry "Insert Hanna tag". Click that and select "languagelink". Enter the path to the target page and the text for the link there. Your Hanna Code is now in the CKEditor field. Save the page and view it in the frontend. Page viewed in the default language: Page viewed in language "en":
- 4 replies
-
- 2
-
-
- multilingual
- pageurl
-
(and 1 more)
Tagged with:
-
I did a bit of digging in the code, and it seems the example in the blog post is outdated. PW nowadays always increments page names for new pages if a page with the same name and parent already exists (that appears to have changed with release 3.0.127). ?
-
There's a small but important difference between wire() and $anything->wire(). The first is the wire() function defined in Functions.php and documented here. The other is the Wire::wire() method you linked to. The first one just retrieves values but can't set them. So it's more of an inconsistency between those two than a bug.
-
Does custom.css mean you have set the path to a custom CSS file in your field's CKEditor settings? If yes, that should be all that is necessary to see it in the backend. If the class isn't applied, use the browser's developer console to make sure the path is correct and PW isn't pulling an old version of the css from the cache.
-
Just to make sure I understand this correctly: you're looking for a way to insert a link to a page in a certain language, no matter what the current language is when the link is being clicked?
- 4 replies
-
- multilingual
- pageurl
-
(and 1 more)
Tagged with:
-
I'm very much with @Jan Romero there. Create reservations and expire them after a while (making sure that any shopping cart entries client side are invalidated as well in that case). I'm not so sure if anything needs to be pre-created, as you'll have a race condition anyway between availability check and actual reservation. There's realy only one solution to that race condition, and that's using database transactions. Thankfully, you can just wrap your relevant PW code in $database->beginTransaction() and $database->commit() as explained here.
-
There might be a better approach, but this snippet in site/ready.php works for me: <?php namespace ProcessWire; $wire->addHookBefore('ProcessPageLister::execute', function(HookEvent $event) { if($event->page->process != "ProcessUser") return; # Put all fields that should be searchable by default here: $userSearchFields = ['email']; # We create a comma separated field selector string from the array above for use by ProcessPageLister $searchFieldsString = implode('', array_map(function($f) { return ", $f="; }, $userSearchFields)); $lister = $event->object; # ProcessUser appends the ", roles=" selector before our hook happens, so we inject our fields # before that part: $lister->defaultSelector = preg_replace( '/(?=, roles=)/', $searchFieldsString, $lister->defaultSelector ); }); ProcessPageLister builds its field list from the selector style string its $defaultSelector property. So you just need to put the names of your fields into the $userSearchFields array and ProcessPageLister will take care of the rest. If you want to completely replace the search field list, it's even simpler. <?php namespace ProcessWire; if($event->page->process != "ProcessUser") return; # Put all fields that should be searchable by default here: $userSearchFields = ['lastname', 'firstname']; # We create a comma separated field selector string from the array above for use by ProcessPageLister $searchFieldsString = implode(', ', array_map(function($f) { return "$f="; }, $userSearchFields)); $lister = $event->object; $lister->defaultSelector = $searchFieldsString; }
-
Have you tried with the $raw_output parameter for hash_hmac? And just to make sure: you use your real spektrix username in place of "website" in setHeader, right?
-
Routing feature / custom endpoints without hooking pageNotFound/404
BitPoet replied to bernhard's topic in Wishlist & Roadmap
Did a little tinkering since I'm going to need another API endpoint for a new project soon and came up with this PoC: https://github.com/BitPoet/WireApi-PoC It's very crude and all as its just meant as a playground for ideas for now (wouldn't dare to clutter up the Wire namespace), and it does still use pages behind the scene (though not the implicit 404 redirect). Perhaps hiding the endpoints from the tree for everybody but superusers might be an option, but I haven't fully thought that out. Here are a few very simple examples of an API template (in fact, very close to what I thought up above): <?php namespace ProcessWire; $api->routeGET('/api/hello/{name}/', null, 'helloWorldApi', true); $api->routeGET('/api/pagelist/', 'apitemplates/pagelist') ->roles('superuser') ->setDebug(true) ; $api->route(['GET'], '/api/custom/{value}/', function($url, $values) { wire('api')->jsonResponse([ 'success' => true, 'youare' => wire('user')->name, 'data' => $values ]); })->check(function($url, $route, $check, $values) { return ctype_digit($values['value']); }); $api->handleRequest(['debug' => true]); // ________________Only function declarations below_____________________ function helloWorldApi($url, $values) { wire('api')->jsonResponse(['Hello', $values['name']]); } Oh, and it ships with a near-one-click endpoint installer ? Let me know what you think, and if you have any ideas about improvements or doing things differently, bring it on. -
Routing feature / custom endpoints without hooking pageNotFound/404
BitPoet replied to bernhard's topic in Wishlist & Roadmap
The last 5 projects all needed some kind of API endpoint. Over time, I've settled for a custom "api" page under home. In two projects, this is a very simple piece of code with a switch statement, in others it looks for individual templates under templates/api. I'm not completely happy with any of them, though, and I've often wished for something akin to hooks with enough boilerplate to avoid repetitive code. I do concur that Laravel has some very nice and expressive features. I could picture a WireApi singleton class with which modules could register, and a fixed "api" page that is by default not much more than <?php namespace ProcessWire; wire('api')->handleRequest(); WireApi may or may not route to matching PHP files under templates/api by default. Autload modules could register their routes in their init() method. Just a rough example with one or two Laravel niceties stirred into the usual PW awesomeness to get a nice declarative approach to api route definitions: <?php namespace ProcessWire; class ApiExampleHandler extends ApiRoute { public static function getModuleInfo() { return [...]; } public function init() { parent::init(); # hand GET requests to /api/users/USERID/profiledata off to our getUsrProfileData method # and pass the value in place of USERID as the first parameter $user to the method. # Use $sanitizer->intUnsigned on $user to make sure it is a valid page id. $this->api->get('/users/{user}/profiledata', $this, 'getUserProfileData')->whereSanitized('user', 'intUnsigned'); } public function getUserProfileData($user) { $u = $this->users->get($user) ?: $this->api->notFound(); $p = $u->profilepage ?: $this->api->notFound(); # Set Content-Type header, set json_encode return value and output it $this->api->jsonResponse([ 'nickname' => $p->nick, 'hobbies' => $p->hobbies, 'membersince' => $p->joindate ]); } } Something along those lines could go a long way to making endpoint implementation easier. I don't know how often I've checked for isAjax et al, called json_decode and json_encode, checked the request method, sanitized and compared urlSegement1 and urlSegment2 and urlSegment3 (and sometimes even urlSegement4) in nested conditionals... For quick and dirty solutions, api route registration could go into api.php before handleRequest() is called, or even into site/ready.php. With a bit of creative magic, a nice PWish equivalent to Laravel's model binding should also be in the realm of possible things. -
Solutions for adding custom text blocks/snippets to ckeditor fields?
BitPoet replied to bernhard's topic in General Support
Instead of ProcessMention, you may want to look at its successor CKInlineComplete. It's got a bit of boilerplate so you can easily extend it with your own InlineCompleteAction. Creating an action module that searches the name or title of answer pages and returns the anser's HTML from a field there should be straight forward. I guess it would be possible to extend the functionality to plain textareas as well, though the code in plugin.js makes use of CKE's range api a lot and positioning relies on its iframe, thus it would mean a bit of refactoring work.- 1 reply
-
- 5
-
-
-
Yes, that is because of the order in which ProcessWire executes its steps when it renders a form. While your template is used for every Inputfield contained in an InputfieldWrapper, each Inputfield's render method is called individually inside InputfieldWrapper::render first, then its rendered return value is then inserted into the item_content template in place of {out}. So the Inputfield doesn't even get to see the template. The "strange" behavior comes up because the surrounding InputfieldWrapper, often an InputfieldForm that extends it, also extends Inputfield and is the last (outermost, as we are talking about recursion) time the render() hook is invoked, this one finally with all the rendered item_content parts that still have the literal {meta_id} strings. Not sure how to accomplish what you're trying to without jumping through some complicated hoops.
-
Unfortunately not. There were changes in the core that broke my own changes, and I simply didn't have the time (a recurring topic, lately...) to fix that at that time, so I switched back to regular logging.
-
I'm not sure if this requirement is common enough it that will be considered for the core, but you can easily extend those Fieldtypes by yourself. These are untested, but they should work. It should even be possible to switch an existing FieldtypeText or FieldtypeTextarea to a non-indexed type.: FieldtypeTextNoindex <?php namespace ProcessWire; /** * ProcessWire Text Fieldtype without Index * * Fieldtype equivalent to FieldtypeText but * without creating an index on its text content * to preserve storage. * * Only use this Fieldtype if you will never search * through the contents of the field. * */ class FieldtypeTextNoindex extends FieldtypeText { public static function getModuleInfo() { return array( 'title' => 'Text Noindex', 'version' => "0.0.1", 'summary' => 'Field that stores a single line of text, not indexed' ); } /** * Return the database schema in specified format * * @param Field $field * @return array * */ public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); unset($schema['keys']['data_exact']); unset($schema['keys']['data']); return $schema; } } FieldtypeTextareaNoindex <?php namespace ProcessWire; /** * ProcessWire Textarea Fieldtype without Index * * Stores a large block of multi-line text like * FieldtypeTextarea but without an index on its * text content to save storage space. * * Only use this Fieldtype if you will never search * through the contents of the field. * */ class FieldtypeTextareaNoindex extends FieldtypeTextarea { public static function getModuleInfo() { return array( 'title' => 'Textarea Noindex', 'version' => "0.0.1", 'summary' => 'Filed that stores multiple lines of text, not indexed', 'permanent' => true, ); } /** * Get database schema used by the Field * * @param Field $field * @return array * */ public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); unset($schema['keys']['data_exact']); unset($schema['keys']['data']); return $schema; } }
- 1 reply
-
- 7
-
-
Thanks, this looks good at first glance. I'm going to give it a spin as soon as I find the time. I'll probably want to let the user choose between client and server side tradeoffs depending on the use case, which also means deciding which is the reasonable default.
-
Built into the dev branch at https://github.com/BitPoet/CacheRedis/tree/dev are storeFor() / fetchFor() / deleteFor(). I'm not a thousand percent sure how reliable this is in large environments, since this uses SCAN and UNLINK to iterate with a wildcard over all keys in the namespace and deletes them asynchronously. You can pass TRUE as the third parameter to deleteFor to force synchronous deletion, but SCAN may at (rare) times return incomplete results anyway. Needs at least redis 2.8.0.
-
Unfortunately not. The mysqli wrapper uses the object oriented interface, so there isn't even an easy point to add that part in the core library between instantiating the module and invoking mysqli_real_connect(). Your best bet is to look at the stack trace to see which module causes the dump (enable $config->debug in site/config.php) and either find a replacement or post an issue in the module's git repo to get it converted to PDO.
-
Repeater field doesn't open (incorrect use / bug)
BitPoet replied to michelangelo's topic in General Support
Is the repeater set to Ajax load? If yes, you may have to look into the network tab of the browser's developer console for the ajax request. The server's response to that should give you more of a clue. -
How to get a list of all changes on page save?
BitPoet replied to bernhard's topic in General Support
Since your Page::changed hook is called, trackChanges already appears to be enabled. Have you tried getChanges(true) inside a saveReady hook? Also, the change information should be visible in arguments 1 and 2 in a Pages::saved hook. -
Using SSL should be quite straight forward, assuming that everything is configured correctly on the server side. The enforcing happens on the server the moment you issue an ALTER USER your-processwire-user@your-mysql-server REQUIRE SSL The moment you do that, you'll get a database error when you access your site. To enable PHP to talk over an encrypted MySQL connection, you now need to point it to the MySQL server's CA certificate. Copy that to a location where the web server can read it and add an entry in site/config.php (adapt the path to match your ca cert location): $config->dbOptions = array( \PDO::MYSQL_ATTR_SSL_CA => 'C:/temp/mysql-ca.pem' ); There may be scenarios where the name you use to access the server doesn't match the name in the certificate and you get the error "SQLSTATE[HY000] [2002]". The same error occurs when you use a self-signed certificate in the server (that's the case when you leave things to default after installing MySQL on most distributions). In that case, you need at least one of the following PHP versions: PHP 7.2, 7.3, 7.4 or 8 all versions PHP 7.1 >= 7.1.4 PHP 7.0 >= 7.0.18 The reason is that earlier versions of the MySQL PDO module didn't have the flag to disable certificate verification. You need to expand your entry in site/config.php: $config->dbOptions = array( \PDO::MYSQL_ATTR_SSL_CA => 'C:/temp/mysql-ca.pem', \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false ); Most (hopefully all) PW modules should be using the PDO interface by now, but you may stumble upon one that still makes use of the old mysqli wrapper. Those won't work with an SSL connection.
-
main.css?ts=<?= time() ?> Has me confused.
BitPoet replied to Greg Lumley's topic in Getting Started
That. The main.css under site/templates/assets/css is meant as a convenient place to add custom styling (no sass/less magic involved here). I had meant to change that time() call to an mtime() call as an example of how to bust the cache whenver the file is changed, but I apparently never got around to it. Brushing up my site templates is another point on my growing todo list. The default styling is done in site/templates/assets/css/blog.css, but you won't see that filename in the developer console because the site template uses the AOIM module to combinde and minimize the js and css files. -
Hi HerTha, sorry I couldn't respond sooner. This looks like the "link" field is a page field, am I right there? In that case, it doesn't work right now as you'd need something like "link->url" in the mention columns, which isn't supported yet. Only direct properties of the found pages can be used. I'm going to meditate on it, but I can't promise a quick solution.