Leaderboard
Popular Content
Showing content with the highest reputation on 12/22/2022 in all areas
-
5 points
-
We are working to fix exactly these issues. But it is slower than I imagined) Hope to get all this fixed some time soon, as we do need it. The Address field use here is different comparing to FieldtypeMapMarker. It is for storing the object address returned by geocoder, not for storing address data you enter. That's why I think it is better to have another field to store the actual address, and Leaflet Map to only set and store a point on the map. Maybe we could find a way to connect them via js, but now it is impossible. By the way, storing address in a a separate field is better for ML environment anyway.4 points
-
Interesting read: https://www.nngroup.com/articles/infinite-scrolling-tips/3 points
-
Why do you have this requirement? JS plugins like https://infinite-scroll.com/ are there for a reason. So developers don't have to reinvent the wheel. Said plugin is well thought out and documented and makes it quite easy to achieve what you want. All you need is a container with your posts and a link to the next page. The rest is taken care of by the plugin. Of course, you could spend hours and write the JS by yourself. I'd rather honor desandro's work and give him 25 bucks for a dev license. Already done it because I needed it for a custom template of a WP site some years ago. If you want to code the JS functionality yourself for whatever reason, you can certainly do that and and have fun with it. You should take browser history into account for usability reasons. The plugin does that. If you decided to use that plugin, setting up the markup and JS initialization could look somewhat like this (simplified example) /** @var PaginatedArray $newspages */ $newspages = $pages->find("template=newsitem, limit=12"); /** @var string $nextPageUrl typically something like /newspage/page2 */ $nextPageUrl = $page->url . $input->pageNumStr((int) $input->pageNum() +1) ?> <div class="infinitescroll"> <?php foreach($newspages as $p) { ?> <article class="post">...</article> <?php } ?> </div> <a href="<?= $nextPageUrl ?>" class="pagination__next">Next page</a> <script> let elem = document.querySelector('.infinitescroll'); let infScroll = new InfiniteScroll( elem, { // options path: 'newspage/<?= $config->pageNumUrlPrefix ?>{{#}}', append: '.post', history: false, // or true }); </script> I have not tested this example code but it should get you started.3 points
-
This week I've been working on something a little different: developing a new site profile in ProcessWire. Actually, I should probably call it an application profile rather than a site profile, as it's not a website profile. Instead it's a profile for an invoicing application in ProcessWire. Though you would install and run it on a web server, but it would be an independent application rather than part of a website... perhaps something you run in a subdirectory, subdomain, or even localhost. This is something I've been wanting to build for awhile—ever since the invoice service I use raised their rates beyond my budget. So I thought I'd build a replacement that I could use, as well as share for others that might have a similar need. I think it might also be a pretty decent PW profile example in general, too. I'd originally considered building it as a Process module but decided not to for a few reasons. Though the biggest one is that a site profile enables the greatest potential for customization and expansion according to each person's needs. Since you can expand upon it by adding your own fields and templates, or editing existing ones, most can really tailor it to their own needs a lot more easily than they could if it were a Process module. Likewise, since the actual invoices (and invoice emails) are rendered from front-end pages, you can customize the look and feel of them to match your brand very easily. (This is something I always wished I could do with the invoice service I've been using previously) This invoice profile requires nothing other than the ProcessWire core. It has no 3rd party or Pro module dependencies. I've got it largely functional at this stage, though will be putting a couple more weeks work into it before releasing it. I'd like to build in the option for clients to pay an invoice with a credit card (via Stripe) for instance. Below are a few screenshots of the work in progress. First is the page-list which shows the current invoices in the system and their status. (click image to view larger) As you can see, there are also pages for Clients and Settings. The client pages contain all the information about each client that invoices can be created for. The Settings page is where you can edit your own company information, logo and billing preferences. Next is the invoice editor. Here we have a repeater for each line item in the invoice. We also have a repeater for payments. All of the totals add up automatically as you type (Javascript added via hooks). They are also calculated automatically at the server side, so that everything stays consistent whether working with the API or in the page editor. (click image to view larger) At the bottom of the invoice editor you'll see a collapsed input for "Invoice action". This is where you can select actions to apply to the invoice. The two we currently have are "Email invoice to client" and "Email invoice to another address". Next up is what we see when viewing the invoice on the front-end. This is just the output of a template file but it is optimized for printing, saving to PDF and sending through email. I've kept it intentionally simple but of course the logo would be replaced with your own and all markup/styles are fully under your control. (click image to view larger) What I plan to add next are payment options, enabling a client to pay by credit card right from the invoice URL or email. What do you think, is this type of PW profile useful to you or someone you know? I've initially built it towards my own client invoicing needs, but I'm curious what other features you would like it to have? Or do you think it's better to keep it simple so that people can more easily take it in different directions? Thanks for your feedback. Have a great weekend!2 points
-
I'd start with what we all love PW for: The API. So I'd ask myself: What would I want it to work (without thinking about the consequences and the work that has to be done to make it work like that). And I'd probably come up with something like this: $newspages = $pages->find("template=newsitem, limit=12"); foreach($newspages as $p) ... echo $newspages->renderPager(); echo $infiniteScroll->renderTag(); To make that work you'd need an $infiniteScroll module with a renderTag() method. That method would render a JS <script> tag that finds the previous sibling (https://gomakethings.com/how-to-get-the-next-and-previous-siblings-of-an-element-with-vanilla-js/#an-example) and detects what page we are on and once the script tag comes into view which next page it should request via ajax and inject into the DOM. Then you'd only have to add the InfiniteScroll.js file to your <head> and maybe make the renderTag() configurable (like how many pages should be loaded before showing a "load more" button or to make the "load more" button markup configurable as well: echo $infiniteScroll->renderTag([ 'pages' => 3, // auto-load 3 pages, then show the load-more button 'button' => "<button class='uk-button uk-button-small'>load more</button>" ]); That would be how I'd like to have it work. But I have to admit that I've never built an infinite scroll so I might be missing something obvious or important ? Good luck and have fun ?2 points
-
The TinyMCE 6 rich text editor opens up a lot of new and useful abilities for ProcessWire users. In this post, we'll take a look at a few of them, and how you can start using them now, with a focus on those that are unique to ProcessWire's implementation of TinyMCE— https://processwire.com/blog/posts/using-tinymce-6-in-processwire/1 point
-
Config() is your Problem ? (it’s only available when the functions api is activated) <?php echo $config->urls->httpRoot; ?>auction_longpolling.php1 point
-
This looks great – I'm still using an old copy of Kedisoft Totals for all my invoicing, but it's getting long in the tooth, many things don't work right. Looking forward to trying this out!1 point
-
That has two reasons: SEO --> bots that do not understand JS will see the plain old paginated site (so every content is accessible) efficiency --> ProcessWire already does the pagination out of the box, so I'd build on that rather than implementing my own logic1 point
-
Hi @Stefanowitsch Yes, and the same focus point would be used. I think if you are looking for a different focus point / crop you might be better off trying to output the <picture> html manually. I see from the source code that the 2nd argument (options) is passed to the size() call. Might be worth trying adding 'cropping' to this - https://processwire.com/api/ref/pageimage/size/ - and see if it works! As for updating the image when the viewport shrinks... I've no idea I'm afraid. If the UIkit implementation can do this - that is it doesn't use the largest image loaded but always the one specified by sizes - you'd need to ask their devs if it isn't working. Actually come to think of it, why not render two images and use uk-hidden@s and uk-visible@s classes to toggle between them? Cheers, Chris1 point
-
Hey @Robin S thanks for your advice! I think you are right, it's not really possible to protect against a malicious superuser account (e.g. If it got hacked). Since the code is saved as a string anyway, I don't think it would cause harm, even if there would be php or javascript code in the field right? But I feel better to use the hook you mentioned. Here is the code, wich works nicely: public function __construct() { $this->addHookBefore('InputfieldTextarea::processInput', $this, 'sanitizeValue'); } public function sanitizeValue(HookEvent $event) { $input = $event->arguments(0); $input->customStyles = strip_tags($input->customStyles, ''); $event->arguments(0, $input); }1 point
-
You could apply the purify sanitizer when the field value is saved by hooking InputfieldTextarea::processInput(). But looking at it another way, it isn't really possible to guard against a malicious superuser - they could destroy the site in any number of ways. Therefore you have to accept that superuser is a role for trusted users only.1 point
-
What about this: I (Stefanowitsch) will create a cronjob module that fetches new reviews maybe once a week so hat you don't need to do an API request every single time someone visits the page. ??1 point
-
Pastefilter: For someone currently testing, could you see what the difference in the Pastefilter is for a[href|id], a[target=_blank] and a[href|id|target=_blank], if any? It seems Ryan split any assignments to their own entry, even though they merge, so I was just curious if there was a reason for that, other than potentially simplified logic/understanding. Not that I currently have a reason to do this, but what if there was a need to whitelist a certain range of classes (attributes; perhaps data-* attributes)? Similar to allowing the pasting of fontawesome content? I'm thinking there's no REGEX or glob matching for attributes and associated values (ex: i[class=fa*] which should cover i.far.fa-horse, i.fas.fa-horse, etc.). Tip on Performance of Plugins: Although it makes complete sense, I might not've thought to check that. I very much appreciate the tip about that, and the "advlist" plugin, specifically! Thanks! The amount of work on documenting its behavior is extremely welcomed - thank you so much, Ryan! EDIT: Pastefilter -- I tested... It appears as though there's a specific distinction when applying value matching as opposed to simple attribute allowance. If providing a match to an attribute's value, it must (currently, at least) have its own unique entry in the list. It cannot be merged with the simpler attribute entry that allows any value. In my testing when I merged the entries together (ex: a[href|id|target=_blank]) only the matching attribute entry was allowed and retained during paste (target=_blank).1 point
-
Hey @ryan I've managed to find a way to get a minimal tinymce field using this very simple json: { "menubar": false, "toolbar": "bold", "toolbar_sticky": true } Result: This is very nice and what I want! The problem: I need to set the JSON globally for ALL tinymce fields: Would it be possible to add an option to set the defaults.json file on a field level? This would be extremely helpful (necessary) for module development where one wants to ship fields with a custom set of options. It's also a lot easier to create fields where the settings are defined in code (GIT!) rather than via gui. Another benefit would be that the core (or a module) could ship different versions of defaults (eg minimal.json, simple.json, default.json) that a field could use and extend on them rather than on global defaults. Thx!1 point
-
As a starting point, I guess, as they look outdated to me but I have not tried... Anyway, I think an officially supported "advanced" code editor plugin would be beneficial to all of us.1 point
-
Maybe this could be of any help https://github.com/melquibrito/source-code-editor-tinymce-plugin https://gitlab.com/tinymce-plugins/tinymce-codemirror1 point
-
Thank you for all the hard work you put into migrating PW to TinyMCE 6, which is so much better than CKEditor. One thing I'm still longing for is a "proper" source code editor which is a paid addon, unfortunately. Would it be possible for you to implement a TinyMCE code editor for ProcessWire, based on CodeMirror or similar?1 point
-
@Joss The utf8mb3 listed on your field_article_text table is the same thing as utf8 I think (utf8 is 3 bytes), so that won't support emojis. Check in your /site/config.php file and look for $config->dbCharset and change it to this: $config->dbCharset = 'utf8mb4'; Then try creating a new Textarea field that uses TinyMCE. It should now use that utf8mb4 charset, and emojis should work. If you want to convert your existing field_article_text field, you'll want to export the table and all its data (with phpmyadmin), then edit the resulting SQL file/dump, change the "CHARSET=utf8mb3" to "CHARSET=utf8mb4". Then import to replace the old table. If you get an error, change the KEY length "250" for that data_exact key in the CREATE TABLE statement to "191".1 point
-
It's just a link to the docs, but maybe it's not so obvious for some that you can even use DDEV to share the project on your local network, so you (or hackers) can access the site from a mobile phone or any other device: https://ddev.readthedocs.io/en/latest/users/topics/sharing/#exposing-a-host-port-and-providing-a-direct-url ddev config --host-webserver-port=8080 --bind-all-interfaces Then ddev restart and then you can access your site locally at something like 192.168.1.2:1234 To find your local ip address you can use ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}'1 point
-
Thx Ryan ? Hope that all the updates go smoothly ?1 point
-
This is a double full circle for me. I started using TinyMCE, then migrated my custom stuff to FCKEditor (when that's what it was called). Then when I came to PW Tiny was still the default, then it moved to CKEditor and now we're going back to Tiny :)1 point
-
1 point
-
This week on the core dev branch we’ve got some major refactoring in the Page class. Unless I broke anything in the process, it should be more efficient and use less memory than before. But a few of useful new methods for getting page fields were added in the process. (A couple of these were also to answer feature request #453). Dot syntax You may have heard of dot-syntax for getting page fields, such as $pages->get('parent.title') where “parent” can be any field and “title” can be any field that has subfields. This is something that ProcessWire has supported for a long time, but it doesn’t get used much because it was disabled when output formatting was on. So it wasn’t something you could really count on always being there. Now you can — it is enabled all of the time. But it’s also been rewritten to be more powerful. When using dot syntax with a multi-value field (i.e. any kind of WireArray value) you can also specify field_name.first to get just the first value or field_name.last to get just the last value. i.e. $page->get('categories.first'); will take a value that was going to be a PageArray (‘categories’) and return just the first Page from it. Bracket syntax With bracket syntax you can call $page->get('field_name[]') with the (‘[]’ brackets at the end) and it will always return the appropriate array value for the type, whether a PageArray, WireArray, Pagefiles/Pageimages, or regular PHP array, etc. This is useful in cases where you know you’ll want a value you can foreach/iterate. Maybe you’ve got a Page field that set set to contain just 1 page, or maybe you’ve got a File/Image field set to contain just 1 file. But you want some way to treat all of your page or file/image fields the same, just append “[]” to the field name in your $page->get() call and you’ll always get an array-type value, regardless of the field settings. This bracket syntax can also be used for getting 1 value by index number. Let’s say you’ve got a page field named “categories” that contains multiple pages. If you want to get just the first, you can just call $page->get('categories[0]'); If you want to get the second, you can do $page->get('categories[1]'); This works whether the field is set to contain just one value or many values. Using the first index [0] is a good way to ensure you get 1 item when you may not know whether the field is a single-value or multi-value field. Another thing you can do with the bracket syntax is put a selector in it to filter a multi-value field right in the $page->get() call. Let’s say you want all categories that have the word “design” in the name. You can call $page->get('categories[title%=design]'); If you want just the first, then $page->get('categories[title%=design][0]'); What’s useful about using selectors in brackets is that this does a filter at the database-level rather than loading the entire ‘categories’ field in memory and then filtering it. Meaning it's a lot more memory efficient than doing a $page->get('categories')->find('title%=design'); In this way, it’s similar to the already-supported option to use a field name as a method call, for instance ProcessWire supports $page->field_name('selector'); to achieve a similar result. Dot syntax and bracket syntax together You can use all of these features together. Here’s a few examples from the updated $page->get() phpdocs: // get value guaranteed to be iterable (array, WireArray, or derived) $images = $page->get('image[]'); // Pageimages $categories = $page->get('category[]'); // PageArray // get item by position/index, returns 1 item whether field is single or multi value $file = $page->get('files[0]'); // get first file (or null if files is empty) $file = $page->get('files.first'); // same as above $file = $page->get('files.last'); // get last file $file = $page->get('files[1]'); // get 2nd file (or null if there isn't one) // get titles from Page reference field categories in an array $titles = $page->get('categories.title'); // array of titles $title = $page->get('categories[0].title'); // string of just first title // you can also use a selector in [brackets] for a filtered value // example: get categories with titles matching text 'design' $categories = $page->get('categories[title%=design]'); // PageArray $category = $page->get('categories[title%=design][0]'); // Page or null $titles = $page->get('categories[title%=design].title'); // array of strings $title = $page->get('categories[title%=design].title[0]'); // string or null // remember curly brackets? You can use dot syntax in there too… echo $page->get('Page “{title}” has {categories.count} total categories'); I’m not going to bump the version number this week because a lot of code was updated or added and I’d like to test it for another week before bumping the version number (since it triggers the upgrades module to notify people). But if you decide to upgrade now, please let me know how it works for you or if you run into any issues. Thanks for reading, have a great weekend!1 point
-
First off, I won't stop developing ProcessWire unless I'm dead. But lets say that one of you showed up at my door and shot me, and then I'm gone for good. This is free software and you don't get any guarantees there, no matter what CMS it is or how big the community or adoption of it is. But what you do get is the source code and permission to use it and do with it what you need to. There is far more security in that than any proprietary or commercial system. We should all feel very lucky that this project has attracted such a capable development community around it (more than any project I've ever seen), and there are several guys here that are fully capable of taking over the project if I go down in a hang-glider crash. I'm always reluctant to list off people because there are so many people that contribute to the core and I don't want to forget anyone. Suffice to say, I may hold the keys to the master GitHub account, but this is a project of many developers, at least 5 of which are fully capable of taking over the project if I kick the bucket. I'm certain that some of these guys could do better than me with it. Please don't take that as an invitation to show up at my door with a weapon. But I would suggest this may be better odds than with the bigger projects you'd mentioned. Lets also point out here that ProcessWire is not WordPress–it does not need daily updating in order to keep running. Most sites I build with ProcessWire are running the version they are launched with. With ProcessWire, you do not need to upgrade your site every time a new version comes out. You can generally upload it and forget it, and it'll keep running till the site as long as the server itself is running. What other CMS can you say that for? (I can't think of any) Personally, I think adoption of something like Drupal, Typo3, Joomla, etc. is more of a risk, because you are dealing with a legacy platform – you are adopting technology from 10 years ago. You are also adopting something that is a target for hackers and spammers. WordPress is perhaps the biggest target, and something I've very apprehensive to setup for clients. Ultimately when a company chooses to adopt a legacy platform because "it's what the clients know" or [more likely] what they themselves know, it's a lazy decision. It's not looking out for the clients' best interests, and it's pursuing mediocrity. When you pursue mediocrity, you pay for it in the long run. There is no better testament to that than the legacy platforms that agency seems attached to. 1-3 years after installing [Drupal/Joomla/Typo3/WordPress/etc.] for the client, they are going to be looking for "something different" in terms of the CMS (we all know this) and they won't be coming back to the same agency. The agency that thinks it's playing it safe is really just hurting themselves when they give their clients something tired and mediocre that anyone can give them. Instead, give them ProcessWire, and they'll know you are different and better (a secret their competition does not have), and they'll be a lifetime client.1 point
-
As it turns out, I don’t get a notification when you change your reaction to one of my posts, only when you add a new reaction, but I noticed anyway ? You have to get an understanding of how to debug what’s happening. Are you familiar with your browser’s web development tools? There will be a place that shows all requests it sends to your site and their results. You can use this to figure out what is going wrong. This line will initiate a POST request to auction-longpolling.php. Check your browser console to see the response status and its contents. It might be 404 or 403 or something, in which case you’re probably hitting the wrong URL or the server doesn’t allow the PHP file to be accessed that way. If it’s in the 500s there’s probably an issue with the PHP code.0 points
-
This is roughly what your directory tree looks like: Your file auction-longpolling.php should be in the same directory as index.php and favicon.ico. Then it should be accessible as example.com/auction-longpolling.php. You can test if the file is accessible by just typing the URL into your browser. This isn’t a very processwirey way of doing things per se. You might want to make a special Template and Page for this polling or use a Path Hook or a UrlSegment.0 points