Jump to content

bernhard

Members
  • Posts

    4,306
  • Joined

  • Last visited

  • Days Won

    157

Everything posted by bernhard

  1. thx @szabesz Hi @Andy thx for your kind words! well... I like to do thinks in code rather than clicking around a GUI, because then I have all in GIT and can automatically deploy it to production. In addition to that I love how you can write form code once and get frontend and backend validation of your forms automatically. The next point is that I don't like the embed methods via Iframe and I never got used to the other output method - how is it called? Direct output? Another point is that I try to avoid hook hell as much as possible. Hooks are great, but I started to adopt concepts where things that belong together are in the same file or folder. That's why every form that I create for RockForms is one single PHP file, that defines all the necessary pieces (fields, after submit action like sending an email, markup for the frontend, error messages...). <?php namespace ProcessWire; /** @var RockForms $rockforms */ $form = $rockforms->form('newsletter', [ 'token' => false, // disable csrf for use with procache! ]); $form->setMarkup("field(email)", "<div class='sr-only'>{label}</div>{control}{errors}"); $form->getElementPrototype()->addClass('mb-12'); $form->addText("email", "E-Mail Adresse") ->setHtmlAttribute("class", "text-gray-dark w-full focus:border-violet focus:ring-violet") ->setHtmlAttribute("placeholder", "Ihre E-Mail Adresse") ->isRequired(); $form->addMarkup("<button type='submit' class='btn btn-sm btn-pink !px-12 mt-6'>Newsletter abonnieren</button>"); if($form->isSuccess()) { $values = $form->getValues(); if($form->once()) { /** @var RockMails $mails */ $mails = $this->wire('modules')->get('RockMails'); $mails->mail('newsletter') ->to('office@example.com') ->subject("New Newsletter Subscription") ->bodyHTML($form->dataTable()) ->send(); $this->log('sent mail'); } $form->success('<span style="color:black;">Thank you for your subscription</span>'); } return $form; This is an example for an easy newsletter subscription form. For me it is also better to code my own module because then I have a lot more freedom and can add extensions and new features while working on any project that uses the module. For example the $form->dataTable() is something I need very often (send form data via mail or show form data in the backend). I guess I'll release this as commercial module soon - if anybody reads this and is interested in a closed alpha write me a PM 🙂
  2. These errors indicate that you have an entry for these modules in your database, but the files on the disk for these modules do not exist. That can happen if you install a module and then just delete the module files without uninstalling the module first. In the backend's module section you should have a "missing" tab where you can delete the entries in the db which should prevent the warnings from showing up in the future.
  3. That's the correct syntax, because you do not set a ProcessWire $config property but a server locale setting.
  4. Sure 🙂 <html> <head> <?php echo $rockfrontend->scripts('head') ->add('/wire/modules/AdminTheme/AdminThemeUikit/uikit/dist/js/uikit.min.js') ->add('//unpkg.com/alpinejs', 'defer') ->addIf('/path/to/debug.js', $config->debug) ->render(); echo $rockfrontend->styles('head') ->add('/wire/modules/AdminTheme/AdminThemeUikit/uikit/src/less/uikit.theme.less') // add all styles (CSS/LESS) inside /site/template/sections ->addAll('sections') ->render(); ?> </head> <body> <?= $rockfrontend->render("sections/header.latte") ?> <?= $rockfrontend->render("sections/main.latte") ?> <?= $rockfrontend->render("sections/footer.latte") ?> </body> </html> I've used that module (RockFrontend) for several projects now and I'll release it soon. If you or anybody else wants to try it out and give me some feedback write me a PM!
  5. Hey @Jan Romero thx for that post 🙂 I've never really understood how such things work and when to use what syntax. When to send a body, when json etc.; Some 3rd party API docs use php://input others use $_GET... Do you have some pointers to make me understand better what's going on and where to use what? Up until now I've managed to get everything working, but sometimes it was just try and error. For example how did you know to use application/x-www... and why are you using URLSearchParams as body? Thank you!
  6. Hi @Erica welcome to the forum! you should definitely care about those two. Where are you hosting your site? Is it a very cheap/limited shared host? Maybe one of these posts are helpful? I guess your host does not support all necessary functions and echo's out some error/warning before it comes to session_name and ini_set. That output could then produce the headers already sent warning... That one should not be a big problem, but it should also be easy to fix:
  7. can you try to create the field manually and then copy and paste the field code from the first tab?
  8. As I've never used redis I had to do some research what it is and what it does... Here's a good video that gives you a quick overview: Would be interesting to hear why/where/when one could use this module and what benefit one would get vs. using WireCache 🙂
  9. What do you mean? Is this how it should be? Or is this what you think has to be? If you want it to have a dummy title, @Studio Lambelet has shown the solution. If you think it must be like this: No, you can leave the title empty and the user has to fill the field on the first edit. You can even do this with my technique. Simply add those fields to the redirect url: $event->session->redirect($p->editUrl()."?fields=title,your_upload_field"); <?php $wire->addHookAfter("/createpage/{parentID}", function($event) use($parent) { // access control $parent = $event->pages->get((int)$event->arguments('parentID')); if(!$parent->editable()) return; // create new page $p = new Page(); $p->parent = $parent; $p->template = 'your-child-template'; if($parent->template == 'your-parent-a') $p->title = 'Dummy text for parent A'; elseif($parent->template == 'your-parent-b') $p->title = 'Dummy text for parent B'; $p->save(); // redirect to page editor $event->session->redirect($p->editUrl()."?fields=title,your_upload_field"); });
  10. You could also add a simple URL hook that creates the page and then redirects to its page editor: <?php // site/ready.php // get the parent page $parent = $page->get("/your/parent/page"); // add hook if the parent is editable // adjust access control to your needs if($parent->editable()) { // add hook to create page $wire->addHookAfter("/createpage", function($event) use($parent) { $p = new Page(); $p->parent = $parent; $p->template = 'your-child-template'; $p->save(); $event->session->redirect($p->editUrl()); }); } And on the frontend: <a href="/createpage">Create new Page</a>
  11. Hey @BlindPenguin I've added support for translatable options fields in RockMigrations. It was quite tricky but should work now. I'd be happy if you could do some testing and tell me if everything works as expected. <?php $rm->createField("tmp_opt", "options", [ 'label' => 'testing options field migration', 'options' => [ 1 => 'one|Label for Option one', 2 => 'two|Label for Option two', 3 => 'three|Label for Option three', ], 'optionLabels' => [ 'german' => [ 'Beschriftung für Option eins', 'Beschriftung für Option zwei', 'Beschriftung für Option drei', ], ], ]);
  12. You could create a PR then 😉
  13. Hi @MoritzLost that's interesting! I've recently fallen in love with latte and there I have the same issue. I didn't think of that until know as I've not been using it on a multilang site yet. Your approach with the table field is interesting, especially the fact that it creates rows automatically from code and populates a default value. Though I'm not sure if I really like that approach or not. I think if the default value is changed by the client and a developer looks into the code and tries to change the wording there this might lead to unexpected results as that change would not have any effect since the new value will not be written to the translations? On the other hand having the msgid has the benefit that if you change the wording in the initial language you don't lose the relation to the translation which would be the case in PW's internal system... There's always a pro and con 🙂 Anyway.. it's not really a big issue for me since I'm always using custom page classes and I can easily add a method there that returns a translatable label, eg $page->buttonLabel()
  14. Glad it helped. Happy hooking - it's a lot more fun if you understand what's going on 😄
  15. The part in the braces tells PW to apply a selector. The position where you add this selector tells PW where to apply the selector. And that "WHERE" depends on the hook and it's structure. In your example you hook Pages::saveReady, which looks like this: That means the object is the "Pages" class and the method is the "saveReady" function. As you can see above on line 2287 that method takes one argument: The "Page" object that is being saved. That means: In the HookEvent $event, the object will be the "Pages" class. Try that with tracy: bd($event->object) In the HookEvent, you'll have one argument and that will be the "Page" object being saved. Try bd($event->arguments(0)) You could also use $event->arguments('page') because the first argument of saveReady() is $page. If it were $foo you could do $event->arguments('foo') If you apply a condition like Pages::saveReady(template=foo) that means that you apply the selector "template=foo" to the first argument of the saveReady method which is the "Page" object (on line 2287) that is referenced as $page. What you are doing when using Pages(template=foo)::saveReady is that you apply the selector "template=foo" to the "Pages" class, but the Pages class is not a single Page object and therefore has no template! Hope that makes sense 😄
  16. That are good and important questions if you want to understand hooks 🙂 Pages::trash() is a hook that you attach to the method "trash" of the "Pages" class (that's the file /wire/core/Pages.php). In that file you'll find the method ___trash() which indicates that it is hookable. You'll also find helpful comments on top of that method in the code. The same concept applies to Page::trash() where the hook is attached to the method trash() of the "Page" class (which is the file /wire/core/Page.php) and represents a single page object. See my link in the signature "afraid of hooks" - does that answer your question?
  17. Just read this by coincidence. Could you please remove this statement as this is not longer true. We can now hook almost any part of the admin theme and the module does so: https://github.com/baumrock/AdminStyleRock/blob/f0687ed6220adbf13d8c2c818b59c3de4f57b102/AdminStyleRock.module.php#L69-L78
  18. There is an issue with an open request for this: https://github.com/processwire/processwire-requests/issues/154 I have shown some possible workarounds - maybe there's a solution for you as well: https://github.com/processwire/processwire-requests/issues/154#issuecomment-1071650652
  19. True, and that need was satisfied 10 years ago 🙂 You can set (almost) every field's label and description in template context:
  20. Hey @Pete thx for sharing. Have you seen my new language module? For me it sounds like it could help you so I'd be happy to hear your thoughts...
  21. You don't even need any external libraries to realize that, because ProcessWire's default admin theme uses UIkit and UIkit has the sortable component that does exactly what you need: https://getuikit.com/docs/sortable#group I thought of building such a module the other day... I'll send you a PM...
  22. In general I'd say no, that's not a bad practice. Pages are built for storing data and they are built to scale. And they are built to work with all the nice things we have in PW world that are a pain to build with plain SQL database structures (like children/siblings/parent references and page reference fields in general).
  23. Data is fetched via cron every night and then PW pages are created so that the client can enrich those pages with data that is not part of the API but is helpful on the website (eg a PDF sheet for the car or the point of contact): API-Fields are read-only. If a car is created by the cron and the point of contact is empty, the client gets a link to directly edit this page and select the POC 🙂 If cars are sold, the cron automatically trashes those pages on the website. Ah... I forgot a nice detail! They have a custom branded PDF viewer:
  24. No, if you just do a createField it will just update the field's label and it will not touch its values. You could do something like this: <?php // site/migrate.php /** @var RockMigrations $rm */ $rm = $this->wire->modules->get('RockMigrations'); $rm->createField("yoursettingsfield", "options", [ 'label' => 'Your field label', 'tags' => 'MyWebsite', // values are populated manually on the live system ]); Push that to the live system, populate values, pull DB to dev. RockMigrations works in a way that it only sets properties that are listed in the migration config. That means if you REMOVE properties, it will most likely not remove those values from the DB. That's why the template migrations have a special property "fields-" additional to "fields": <?php ... $rm->setTemplateData('yourtpl', [ 'fields' => ['foo', 'bar', 'baz'], ]); // this will NOT remove field baz from template yourtpl $rm->setTemplateData('yourtpl', [ 'fields' => ['foo', 'bar'], ]); // this WILL remove field baz from template yourtpl $rm->setTemplateData('yourtpl', [ 'fields-' => ['foo', 'bar'], ]); // but I prefer doing such things like this as it's more verbose and clearer $rm->setTemplateData('yourtpl', [ 'fields' => ['foo', 'bar'], ]); $rm->removeFieldFromTemplate('baz', 'yourtpl'); Of course you can also do conditional migrations like this one where I had to migrate from this structure /rockmails /rockmails/mail1 /rockmails/mail2 ... to this one: /rockmails /rockmails/templates /rockmails/templates/tpl1 /rockmails/templates/tpl2 ... /rockmails/mails /rockmails/mails/mail1 /rockmails/mails/mail2 ... The migration creates the new root data page (/rockmails - tpl=rockmails_root), then it creates the mails and mail template and fields, then, if the old mailspage exists (/rockails - tpl=rockmails), it moves the old mailspage from PW root to the new datapage (/rockmails/mails) and it renames it from rockmails to mails (as it lives in /rockmails/mails now and /rockmails/rockmails would be weird) and when done we call the root->migrate() again to revert the setTemplateData from line 200 --- If you find something out regarding to the options field please let me know. I'm happy to merge PRs 🙂
×
×
  • Create New...