Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 06/27/2024 in all areas

  1. Page classes are an outstanding feature of ProcessWire and probably ranks among my favorites overall (and that's really saying something with ProcessWire). I don't remember how I lived without them, well I do, but I don't like to think about it. I wrote a response to a question asking how ProcessWire can help transition from procedural code to OOP and in the process (pardon the pun) of answering, I realized how much I've come to use, if not outright rely on, page classes in every project. I wanted to compile a few more thoughts and examples here because there may be devs who are finding this feature for the first time, and besides, why not go down the rabbit hole? Page classes have been around since ProcessWire 3.0.152 and if you're not familiar this will all make a lot more sense if you do a quick read of the feature announcement by Ryan here because this post assumes familiarity. We're also going to pick up the pace between the examples below compared to my comment in the link above. Here are a few goals that I have that page classes put solutions for within reach. You may have some of your own and I'd love to hear about those as well. Keep templates clean by restricting logic to flow operations- if statements and loops. Work with data in context, accessing data and fields closer to their source. Create "universal" methods available in every template, but also have scoped methods per-template Increase the scalability of a project, growth with limited increases in complexity. Stay DRY Embrace and extend the power of OOP in ProcessWire's DNA This seems like quite a list for one feature to handle, but rest assured, this is an example of how much power comes with using page classes. I'll continue and build on the blog example from my linked response above, and you can "follow along" with the Blog module since the templates mentioned are present out of the box. Some of the examples below are taken from production code, but please excuse any errors that I may have introduced by accident and I'm happy to update this post with corrections. Some of these things can be done other ways but they're just to illustrate, replace with your ideas and think of times that this would be useful. First up, what are some features and behaviors that everyone needs on every project? What are some universal methods that would be great to have everywhere? Let's start out by creating a "base" page class called DefaultPage. Going forward, page classes will extend this class instead of Page and benefit from having access to universal methods and properties. EDIT: I initially wrote this using BasePage rather than DefaultPage for this class name as described by the custom page class writeup by Ryan in the article above. I've changed this to now use the correct DefaultPage class name. <?php namespace ProcessWire; // /site/classes/DefaultPage.php class DefaultPage extends Page { /** * Returns all top level pages for the main navigation. */ public function navigationPages(): PageArray { $pages = wire('pages'); $selector = 'parent=1'; $excludedIds = $pages->get('template=website_settings') ->nav_main_excluded_pages ->explode('id'); // Check for excluded pages from nav defined in the "Website Settings" page count($excludedIds) && $selector .= ',id!=' . implode('|', $excludedIds); $homePage = $pages->get('id=1'); $topLevelPages = $pages->find($selector); $topLevelPages->prepend($homePage); return $topLevelPages; } } All your base page are belong to us. Right off the bat we've managed to pull complex logic usually in templates and kept our markup clean. This isn't for DRY, it's to store logic out of templates. This simple example only illustrates working with a top level page nav. We can start to appreciate the simplicity when considering how much more the navigation may call for in the future. A navigationChildren method that also accounts for excluded pages is a prime example. In our markup: <!-- /site/templates/components/site_nav.php --> <nav> <ul> <?php foreach ($page->navigationPages() as $navPage): ?> <li> <a href="<?= $navPage->url; ?>"><?= $navPage->title; ?></a> </li> <?php endforeach ?> </ul> </nav> Next up, our settings page has newsletter signup fields where users can copy/paste form embed code from Mailchimp. Our website has a "settings" page where global values and fields can be edited and maintained There are multiple textarea fields on the settings page that can each contain a different mailchimp embed code There is an embed select field that can be added to templates. The values are textarea field names so an embed can be chosen by the user. An embed select field has been added to the settings page which allows for choosing a default embed code if one isn't selected when editing a page Embedded forms should be available anywhere on any template <?php namespace ProcessWire; // /site/classes/DefaultPage.php class DefaultPage extends Page { // ...Other DefaultPage methods /** * Renders either a selected form embed, fallback to default selected form */ public function renderEmailSignup(): ?string { $settingsPage = wire('pages')->get('website_settings'); $embedField = $this->signup_embed_select ?: $settingsPage->default_email_embed; return $settingsPage->$embedField; } } Excellent. We've got some solid logic that otherwise wouldn't have a great home to live in without page classes. We can also modify this one method if there are additional options or complexity added to the admin pages in the future. Wherever we use renderEmailSignup, the return value will be either the selected or default form embed code. Let's create a folder called "components" in our templates directory that will hold standalone reusable markup we can use wherever needed, here's our newsletter signup component: <!-- /site/templates/components/newsletter_signup.php --> <div class="newsletter-signup"> <?= $page->renderEmailSignup(); ?> </div> Great! We've kept logic out of our code. We can render the field, and we can account for an empty value with a fallback. Unfortunately this is pretty limited in that it only handles a specific field, and it's implementation isn't as flexible as a component should be. We can fix that, and here are some new requirements to boot: Some templates may have multiple embed select fields which may or may not have a value Each embed select field is now paired up with a text field to add some content that appears with each form embed, think "Sign up for our newsletter today!" We want to render our mailchimp form embeds using the same component file, but handle different fields Sounds like a tall order, but it's a challenge easily overcome with page classes. We're going to introduce a new method called renderComponent that will be global, reusable, and very flexible. We're also going to make use of another very great ProcessWire feature to do it. Back to our DefaultPage class: <?php namespace ProcessWire; // /site/classes/DefaultPage.php class DefaultPage extends Page { // ...Other DefaultPage methods /** * Renders a file located in /site/templates/components/ with optional variables */ final public function renderComponent(string $file, ...$variables): string { $componentsPath = wire('config')->paths->templates . 'components/'; return wire('files')->render($file, $variables, ['defaultPath' => $componentsPath]); } /** * Renders either a selected form embed, fallback to default selected form */ public function renderEmailSignup(?string $embedSelectField = null): ?string { $settingsPage = wire('pages')->get('website_settings'); $embedField = $this->$embedSelectField ?: $settingsPage->default_email_embed; return $settingsPage->$embedField; } } We've done a couple of things here. We've updated renderEmailSignup to accept a field name, so now we're flexible on exactly which field select we'd like to check for a value on before falling back to default. We've also created a renderComponent method that is going to be super useful throughout the rest of our ProcessWire application. Our renderComponent receives a file name located in the components directory and any number of named parameters. You could change the $variables parameter to an array if you'd like, but I'm a big fan of the great features we have now in PHP 8+. Here's our refactored component file: <!-- /site/templates/components/newsletter_signup.php --> <?php if ($embedField): ?> <div class="newsletter-signup"> <?php if ($text): ?> <h2><?= $text; ?></h2> <?php endif ?> <?= $page->renderEmailSignup($embedField); ?> </div> <?php endif ?> And let's hop over to our (abbreviated) home page template: <body> <!-- ...Sections full of great content --> <section class="signup-call-to-action"> <?= $page->renderComponent('newsletter_signup.php', embedField: $page->embed_select, text: $page->text_1); ?> </section> <!-- ...Your awesome template design --> <section class="page-end-call-to-action"> <?= $page->renderComponent('newsletter_signup.php', embedField: $page->embed_select_2, text: $page->text_2); ?> </section> <!-- Your footer here --> </body> I don't know about you, but this is looking really good to me. The number of things we've accomplished while having written so little code is remarkable: Because we used wire('files')->render(), the entire ProcessWire API is available within the component, so now our renderEmailSignup method is too. The variadic function parameters (or array if preferred) let us pass an arbitrary number of variables to any component file, unrestricted future flexibility Variables are scoped to each component! There's no reference to template fields in our component that could break if changes are made No more PHP includes, we don't have to juggle paths or constantly repeat them in our code, nor rely on declaring variables before including a file. ProcessWire will throw an exception if we try to render a file that does not exist which makes locating issues very easy We'll also see an exception if we try to reference a variable in our component that wasn't passed which can also help troubleshooting. Notice that the renderComponent is final. We want that behavior to remain consistent everywhere we use it and not overwritten either intentionally or by accident on our inheriting page classes. We want to eliminate any confusion between templates by knowing it will always do the same thing the same way. We can explore other uses too, perhaps a renderPartial for files in /site/templates/partials where we store files like site_header.php. As mentioned above however, if a variable is expected in the rendered file but not included in our render method, we'll see an exception. Let's use site_header.php as an example because we're sure to run into situations where variables may or may not exist: <?php namespace ProcessWire; // /site/templates/partials/site_header.php ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?= $page->title; ?></title> <meta name="description" content="<?= $metaDescription ?? null; ?>"> <?php if ($includeAnalytics ?? true): ?> <script> // All that Google Analytics jazz </script> <?php endif ?> </head> <body class="<?= $bodyClasses ?? null; ?>"> <header> <?= $page->renderComponent('site_nav.php', includeEmailButton: true); ?> </header> Problem: solved. By using nullsafe ?? operators, we can call $page->renderPartial('site_header', description: 'The ultimate Spice Girls fan page.'); and never get errors for variables that may not be included when calling renderComponent, such as includeAnalytics, which now also has a default value of 'true'. Nice. We haven't even gotten to our actual page classes yet... Our templates are about to receive superpowers. Let's take our blog to the next level. In my comment on the other thread, I created a specific example of adding a readTime method to our blog posts, let's go one level higher to our blog.php template. We'll populate some methods up front and then talk about what we've done: <?php namespace ProcessWire; // /site/classes/BlogPage.php class BlogPage extends DefaultPage { /** * Get latest blog posts, optionally with/without pinned post, optionally in blog category */ public function latestPosts( int $limit = 3, bool $includePinnedPost = true, ?int $categoryId = null ): PageArray { $selector = 'template=blog-post'; if ($categoryId) { $selector .= ",blog_category={$categoryId}"; } $posts = wire('pages')->get($selector); if ($includePinnedPost) { $pinnedPost = $this->getPinnedPost(); if ($pinnedPost) { $posts->remove($pinnedPost); $posts->prepend($pinnedPost); } } return $posts->slice(0, $limit); } /** * Gets an optional pinned post if set/chosen * pin_blog_post A checkbox to indicate whether a post should be pinned * pinned_post A InputfieldPage field to choose a blog post */ public function getPinnedPost(): ?Page { if (!$this->pin_blog_post) { return null; } return wire('pages')->get($this->pinned_post); } } On our main blog page a user has the ability to choose whether a blog post is "pinned". A pinned post will always remain the first post anywhere a list of posts is needed, something like a big company announcement that the client wants to keep visible. These two methods alone have given us awesome abilities. For our main blog page, when someone visits our blog page, the most recent or pinned post is presented at the top, followed by the next two most recent posts, followed by two rows of 3 posts, for a total of 9. Let's assume that we've already created the BlogPostPage.php with the readTime method from my previous example. Here's our blog.php template <?php namespace ProcessWire; echo $page->renderPartial( 'site_header.php', bodyClasses: 'blog-page', metaDescription: $page->blog_description ); $posts = $page->latestPosts(9); $firstPost = $blogPosts->first(); ?> <section class="main-post"> <article> <img src="<?= $firstPost->blog_image->url; ?>" alt="<?= $firstPost->blog_image->description; ?>"> <h1><?= $firstPost->title; ?></h1> <?= $firstPost->summary; ?> <span><?= $firstPost->readTime(); ?></span> <a href="<?= $firstPost->url; ?>">Read More</a> </article> </section> <section class="recent-Posts"> <?= $page->renderComponent('blog_preview_card.php', blogPost: $posts->get(1)); ?> <?= $page->renderComponent('blog_preview_card.php', blogPost: $posts->get(2)); ?> </section> <section class="past-posts"> <?php foreach ($posts->slice(3, 6) as $post): ?> <?= $page->renderComponent('blog-preview_card.php', blogPost: $post); ?> <?php endforeach ?> </section> <?= $page->renderPartial('site_footer.php'); ?> So first off, we've really started to use our renderComponents method and component files. We also implemented a renderPartial as speculated upon above. Each does a similar thing, but having separate methods makes everything clear, handles paths, but still has a similar interface when calling them. A big thing to notice here is that at no point have we added any markup to our page classes, and no business logic to our templates. If we need to find anything, we know where to look just by glancing at the template. Ultimate maintainability. Here's our blog_preview_card.php component: <?php namespace ProcessWire; ?> <!-- /site/templates/components/blog_preview_card.php --> <article class="blog-preview-card card"> <img src="<?= $blogPost->blog_image->url; ?>" alt="<?= $blogPost->blog_image->description; ?>"> <h2><?= $blogPost->title; ?></h2> <div class="blog-summary"> <?= $blogPost->blog_summary; ?> </div> <span class="blog-read-time"><?= $blogPost->readTime(); ?></span> <a href="<?= $blogPost->url; ?>">Read More</a> </article> I am liking how well this is working out! Page classes have done a ton of heavy lifting here: We're using our renderComponent method to it's maximum potential and it's payed off in spades Our template couldn't be cleaner or more easily maintainable BlogPostPage.php has taken care of all of our needs as far as delivering the PageArray of posts and all our template does is output the data as needed Our "card" component will render the same thing everywhere and we can update how that looks globally with changes to one file If you don't think this can get more awesome, or think this post is already too long, I have bad news for you and you should stop reading now. Still here? Let's create a BlogPostPage class and add a method: <?php namespace ProcessWire; // /site/classes/BlogPostPage.php class BlogPostPage extends DefaultPage { // ... Other page methods, like readTime() public function relatedPosts(int $limit = 3): PageArray { return wire('pages')->get('template=blog')->latestPosts( limit: $limit, includePinnedPost: false, categoryId: $this->blog_category?->id ); } } The BlogPage::latestPosts method is really flexing it's muscle here. We've used it in two different places for different purposes but requesting similar data, blog posts. If you noticed, we're also specifying a category for this blog since we have a page select field that references a blog category. That was a parameter that we included back in our BlogPage::latestPosts() method. So, blog posts have a "You might also be interested in..." section with posts in the same category as the that one that a visitor has just read. With page classes this couldn't be easier to add, so let's use the relatedPosts method we just created in BlogPostPage.php in our blog-post.php template: <?php namespace ProcessWire; // /site/templates/blog-post.php echo $page->renderPartial( 'site_header.php', bodyClasses: 'blog-post', metaDescription: $sanitizer->truncate($page->blog_summary, 160) ); ?> <!-- The page hero, blog content, stuff, etc. --> <section class="related-posts"> <?php foreach ($page->relatedPosts() as $post): ?> <?= $page->renderComponent('blog_preview_card.php', blogPost: $post); ?> <?php endforeach ?> </section> <?= $page->renderPartial('site_footer.php'); ?> We've just added a related posts feature in *checks stopwatch* seconds. This project is going so fast that you can already hear the crack of the cold beer after a job well done. One more example, and I'll let you decide if this is too much, or feels just right. We want a blog feed on our home page. So before we go to the home.php template, let's do what we do (surprise, it's a page class): <?php namespace ProcessWire; // /site/classes/HomePage.php class HomePage extends DefaultPage { // Other HomePage methods... public function blogFeed(int $limit = 3): PageArray { return wire('pages')->get('template=blog')->latestPosts(limit: $limit); } } At first glance, this might seem like a bit much. Why create a method in our page class that is so short and simple? We could just call $pages->get('template=blog')->latestPosts(3) inside our template and get away with it just fine. Here's why I think it's worth creating a dedicated page class method: This creates yet another example of predictability between templates It promotes thie philosophy I like of page classes talking to page classes It's likely that we'll have other more complex methods in the HomePage class, keeping them together feels right and helps with uniformity- we always know where to look when seeing custom methods If we need to make a change to the BlogPage::latestPosts() method that affect how other page classes call that method, we don't have to root around in our templates to make any changes. It's pretty nice to see that thus far we have only ever referenced the $page object in our templates! There aren't any calls to $pages because our template is really about doing one thing- rendering the current page without caring about other pages. That's not to say that we can't, or shouldn't, use other ProcessWire objects in our templates, but it's still impressive how much we've been able to scope the data that we're working with in each template. I truly love the page classes feature in ProcessWire and even though this is a deeper dive than my other post, this really is still just scratching the surface because we can imagine having more complex behavior and other benefits. Here are some from my experience: Handling AJAX calls to the same page. Choosing how and what type of content is returned when the page is loaded is really nice. This can be done with hooks, but I really like page classes handling things like this in-context. Working with custom sessions between one or more pages. Creating a trait that shares session behavior between page classes is a great way to extend functionality while keeping it scoped. Adding external API calls and whatever complexity that may entail Provides the ability to add significant complexity to our templates in the admin yet little to no additional complexity in template files. The basic "pinned post" and form embed features are just the start As you can imagine, this makes replicating code you use often between projects trivial Page classes provide a home for logic that otherwise would be stuck in a template, or relegated to a messy functions.php file. No more functions.php ever. We met every single goal I listed here at the beginning Every single template thus far only uses if statements and loops DefaultPage could have a "bootApplication" method if needed that does things for every page and is called in ready.php. This is also a great way to create a "bootable" method in all of your page classes where bootApplication could call a bootPage method in your page classes if it exists. Thanks for coming to my TED talk. Hope you find this useful! If you have any questions, corrections, use cases, or things to add, the comments below are open and I'd love to hear them!
    4 points
  2. SOLVED - but I'll leave this here for anyone else to reference as I struggled to find examples. Long story short, I think I was massively overthinking it - with a bit of help from Chat GPT (!! Chat GPT know ProcessWire now - who knew!) I've got this: ..... } elseif($field->type == 'FieldtypeRepeaterMatrix') { //HANDLE REPEATERS HERE $repeaterItems = $page->$field; foreach ($repeaterItems as $item) { // Iterate over each field in the Repeater Matrix item foreach ($item->fields as $subField) { $subFieldName = $subField->name; $subFieldValue = $item->get($subFieldName); // Perform your modification here if the subfield is a string type if (is_string($subFieldValue)) { $modifiedValue = $this->replace_merge_tags($subFieldValue, $tags, $defaultTags); // Save the modified content back to the item $item->set($subFieldName, $modifiedValue); } } } }
    2 points
  3. Very happy to hear you enjoyed it!. Writing a course on anything hasn't ever crossed my mind haha. I just put this together to share a great ProcessWire feature that has really boosted my workflow and thought maybe sharing some examples might be useful to others. I hadn't thought about this post as a walkthrough on OOP specifically, but I did want to be descriptive in a way that would be useful to as many people as possible- whether you are familiar with OOP or not, and without any assumptions. All said, I'm happy to try and answer questions if it would help anyone ?
    2 points
  4. Hello @Juergen. First of all I want to thank you for releasing this module for free. I can tell from the code, that A LOT of work went into it. I want to make some suggestions for improvement, as I stumbled across some errors or hickups that occured in my setup. I am using FrontendForms 2.2.2 and ProcessWire 3.0.235. Module settings timeout with many pages After the first install of the module and trying to go to the modules settings, the page loaded forever and then timed out. I figured, that the timeout results while trying to get the selectable pages for the data privacy page: https://github.com/juergenweb/FrontendForms/blob/main/FrontendForms.module#L1831-L1840 As I have some tens of thousands of pages it takes too long to process. So I changed the code to use a PageListSelect (treemenu) instead, which I think is much better. // Select the data privacy page $dataPrivacySelect = $this->wire('modules')->get('InputfieldPageListSelect'); $dataPrivacySelect->attr('name', 'input_privacy'); $dataPrivacySelect->label = $this->_('Privacy policy page'); $dataPrivacySelect->description = $this->_('Select the page which contains your privacy policy.'); $dataPrivacySelect->notes = $this->_('The link to this page will be used inside the Privacy class for generating the "Accept the privacy policy" checkbox.'); $dataPrivacySelect->parent_id = 1; // Set this to the ID of the parent page you want to start the tree from What you think of that? Placeholders in email don't work Then I tried to use the placeholder functionality for the emails, but the placeholders don't get replaced. I attached my form to this post, so you can replicate it. Is there anything else I need to setup, to get them replaced? Did I miss some instructions? I took the example from https://github.com/juergenweb/FrontendForms/blob/main/Examples/contactformWithWireMailSMTPModule.php and this is the result: File Upload Size does not work correctly I have a file upload in my form, and want to set a custom max filesize. So I used the rule $fileUpload->setRule('allowedFileSize', 2028); and get the output "Custom Filesize Please do not upload files larger than 2,0 kB" which is correct. Now I want to set this to 10 megabytes which would be 10 * 1024 * 1024. As soon as I try to set this as the rule, it is falling back to an incorrect read upload_max_filesize from the php.ini. The value set in my php.ini is 100M (megabytes) So after setting the rule to $fileUpload->setRule('allowedFileSize', 10 * 1024 * 1024); I get the notice under the file upload field "Please do not upload files larger than 100,0 kB" which is wrong. It should either be the 10MB that I set in my rule, or the 100M that is set in the php.ini file. Thank you in advance for your answer. If you like to discuss personally, we can schedule a call. I would be happy to get to know you. Another thing: I made my own TailwindCSS adapter which was very easy, with the json files for the other frameworks as a base. contact.php
    1 point
  5. This is great stuff! Thank you! @FireWire, As a beginner, I would love to learn how you usually set up a simple institutional website. It would be nice to see how you do.
    1 point
  6. wbmnfktr and Robin s, thank you so much for your suggestions and hints, they will help me do work this out!
    1 point
  7. Because you're referencing the image dynamically, it's hard to say what it should be here. What you can do though is, if you have the Tracy Debugger module installed, do a few db() calls on $content to try to identify what is actually contained in the value you're retrieving within $link. Try $content, $block, $content->$block, etc. I suspect that you are just not referencing the correct object name, but since you're calling things dynamically, you'll need to do some debugging of your own to figure out what's going on.
    1 point
  8. If you are doing something like this... // Get poi page from Page Reference field $poi_page = $page->my_page_reference_field; // Render poi page echo $poi_page->render(); ...then you can pass variables to the template file of the poi page via the $options array argument. See docs: https://processwire.com/api/ref/page-render/render-page/ // Get poi page from Page Reference field $poi_page = $page->my_page_reference_field; // Render poi page echo $poi_page->render(['rendered_from' => $page]); And in the poi template file: if(!empty($options['rendered_from'])) { $rendered_from = $options['rendered_from']; //... } You can also use the $options['pageStack'] value in the template file. It's mentioned in the docs page linked to above, but probably easiest to understand if you just dump it and see what it contains.
    1 point
  9. I'm a bit puzzled about what might have gone wrong here since your project settings appear correct. The module is also compatible with ProCache enabled, so that shouldn't be an issue. Could you please share your Module-Config so I can analyze the problem further?
    1 point
  10. Your tasks include the development of agile web applications and websites - mainly based on the CMF ProcessWire. Your talents are: at least 3 years of experience with the content management framework ProcessWire quick perception advanced knowledge of PHP, JavaScript, MySQL advanced backend knowledge: REST APIs, DBs, servers OOP GIT Further information: https://www.incognito.ms/backend-entwickler-m-w-d/ Send your application to: bewerbung@incognito.ms Arne
    1 point
  11. @ngrmm Please update to 2.1.58 - this version should fix your problems. Please add the new validator to your fileupload field: $fileuploads->setRule('uniqueFilenameInDir', true); The following method $form->getValue('fileuploads') should now show the sanitized and/or overwritten filenames as well. The full explanation of the additions can be read here in the Changelogs. As always, please take care if everything works as expected after the update!! ?
    1 point
  12. I'm not an expert ? but this sounds good. And another issue I had was this: I generate a page with the values of the submitted form. The filenames get sanitized after submitting. An uploaded Image.jpg is saved as image.jpg. So in order to save add the files uploaded to a page, you have to sanitize them. Because the values-array has the original filenames I did it this way: if($form->isValid()){ $p = new Page(); // create new page object $p->template = 'template_name'; // set template $p->parent = $pages->get(1111); // set the parent $p->name = $sanitizer->pageName($form->getValue('surname'), true); // give it a name used in the url for the page $p->title = $form->getValue('textField'); // set page title $p->save(); $p->addStatus('unpublished'); foreach($form->getValue('fileuploads') as $fileItem) { // // sanitizing // $sanitizer->fileName() did not work for me :( // $sFile = $sanitizer->pageName($fileItem, true); $p->images->add("https://domain.com/site/assets/files/{$page->id}/{$sFile}"); } $p->save(); } Maybe it be an option in the future to output the url of the image inside the values fieldarray?
    1 point
  13. Good Morning @ngrmm By default, the file upload field takes care about your max upload file size value that has been set inside your php.ini (server config file). https://www.php.net/manual/en/configuration.file.php 2048 is the max file upload size as set inside your php.ini in this case. You can disable this behavior by adding the following rule to your file input field: $file1->removeRule('phpIniFilesize'); This disables the check of the php.ini max file size and you will get the desired result, but it is not the recommended way. If you have access to your server php.ini file, make your changes there and set a higher value for the max upload file size. If you have a local installation (fe. Xampp), you can change it by yourself, if you have a site on a shared host, maybe you will need to contact the server admin, if he can change the max. value for file uploads or you can login to your account and make your changes there. Hope this helps
    1 point
  14. Hello Claus, thank you for reporting the bug - it is fixed now. There was a missing typehint declaration at the label file. I have updated the module now to 2.1.26 - alternatively please replace the folllowing file: https://github.com/juergenweb/FrontendForms/blob/main/Formelements/Textelements/Label.php On line 20 I have changed the following: protected int $enableAsterisk = 1; // to protected int|string $enableAsterisk = 1; Best regards Jürgen
    1 point
  15. Solution: https://processwire.com/api/ref/page/num-parents/
    1 point
  16. It's hard to say definitely, but most times this is an issue that occurs on shared hosting. MySQL has a limit on how many concurrent connections it serves (151 by default, though usually set higher for shared db servers). If this number is reached, new connection attempts return with the error you quoted. If you're on shared hosting, check with your hoster if other users are eating up connections. If you're on a dedicated server, also look if there are other tasks/programs connecting to MySQL at the same time (for example backup/maintenance scripts or database logging facilities). If you haven't made large tweaks to your PW installation or webserver/PHP configuration, the first two are the most likely suspects.
    1 point
×
×
  • Create New...