Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 04/19/2024 in all areas

  1. This week we have ProcessWire 3.0.238 on the dev branch. This version contains 17 commits containing new features (some via feature requests) as well as issue fixes. Recent updates have been nicely covered in ProcessWire weekly issues #517 and #518. Also added this week are the following: Improvements to ProcessPageClone, which now supports some new options such as the ability to configure it to always use the full clone form (a requested feature), and an option to specify whether cloned children should be unpublished or not. New $datetime->strtodate($date, $format) method that accepts any PHP recognized date string and reformats it using the given format. It also supports date strings that PHP doesn't recognize if you give it a 3rd argument ($options array) with an `inputFormat`. The existing $datetime->strtotime() method now supports an `inputFormat` option as well. The Inputfield class now has new methods: $inputfield->setLanguageValue($language, $value) and $inputfield->getLanguageValue($language), added by the LanguageSupport module. Previously there were no dedicated methods for this, so you had to manage them with custom keys containing language IDs if you wanted to programmatically use an Inputfield in multi-language mode. The ProcessWire.alert() JS method has been updated with an auto-close option (`expire` argument) that automatically closes the alert box after a specified number of seconds. InputfieldDatetime has been updated with 7 new interactively configurable settings for the jQuery UI date picker. See the screenshot below for an example of a few. Plus, its API has been updated with the ability for you to specify or override any jQuery UI datepicker option, either from PHP or JS, as requested by Toutouwai/Robin S. See the new datepickerOptions() method and phpdoc for details. Next week we've also got some more updates to InputfieldTable that take the newly added actions even further and make them easier to use. Thanks for reading and have a great weekend!
    7 points
  2. Checking all 3 of these will probably do what you want, but I actually never use the second two because I make use of the "Enable Guest Dumps" button from the panel selector. Enable that, do what you need to do as a guest in a private window and then reload the PW admin where you are logged in as a superuser and you'll get the dumps recorded by the guest. Another thing is to make sure you have the "Tracy Exceptions" panel enabled so that if a guest user interaction results in an exception, the bluescreen with full stack trace will be made available for easy viewing. I rely on this for all my sites with Tracy's production mode. Hope that helps.
    3 points
  3. I had to do this just the other week. We had an old CMSMS site that we'd moved to PW. We had a db table with the old user names and passwords in which we stuck on the new site, and then just created our own login page which first checked to see if we had the user in PW already. If the user isn't already in PW then check the password against the old table; If it matches then use that information to add a new user to PW. The users don't really notice that anything has changed. Here's the code I used for that - you'll have to adapt to your circumstances of course but it worked well for our move. if ($input->post('ml_name')) { // when processing form (POST request), check to see if token is present // (we have a CSRF field on our login form) if ($session->CSRF->hasValidToken()) { // form submission is valid // okay to process } else { // form submission is NOT valid throw new WireException('CSRF check failed!'); } $ml_name = $sanitizer->name($input->post('ml_name')); $ml_pass = $input->post('ml_pass'); // dont allow the guest username if ($ml_name === 'guest') { throw new WireException('Invalid username'); }; // do we have this user in PW already? $u_exists = $users->get("$ml_name"); if ($u_exists->id) { // user exists // lets try and log them in try { if ($session->login($ml_name, $ml_pass)) { $session->redirect('/'); } else { $ml_feedback = 'Unknown details.'; } } catch (WireException $e) { // show some error messages: // $e->getMessage(); } } else { // ok - well do we have this user in the old CMS table? $query = $this->database->prepare("SELECT * FROM `cms_module_feusers_users` WHERE username = :login_name LIMIT 1;"); try { $query->execute(['login_name' => $ml_name]); } catch (PDOException $e) { die ('Error querying table : ' . $e->getMessage()); } // we've got a user from the old table if($query && $row=$query->fetch()) { $ml_feedback='Is in old table'; $hash=$row['password']; // handily the old auth just uses password_verify if(password_verify($ml_pass, $hash)){ // Add this user to the PW users $new_user=$users->add($ml_name); if($new_user){ $new_user->pass=$ml_pass; $new_user->addRole('members'); $new_user->save(); $log->save("new_members_site", $ml_name . " added as user"); $u = $session->login($ml_name, $ml_pass); if($u) { $session->redirect('/'); } else { die("Error in logging in. Please contact admin."); } $ml_feedback='new user added to PW'; }else{ $ml_feedback='Unable to add new user. Please let admin know'; } }else{ $ml_feedback='No matching records found.'; } }else{ $ml_feedback='No record found.'; } } } and this is the login form that the we had on our new login page - this and the above was all in a single template. <form id="ml_login_form" class="ml_login_form" method="POST"> <?php echo $session->CSRF->renderInput(); ?> <label for="ml_name">Username</label> <input id="ml_name" name="ml_name" type="text" value="<?= $ml_name ?>" required> <label for="ml_pass">Password</label> <input id="ml_pass" name="ml_pass" type="password" value="<?= $ml_pass ?>" required> <div style="display: none;"> <label for="ml_pass_bear">Password</label> <input id="ml_pass_bear" name="ml_pass_bear" type="text" value=""> </div> <button type="submit" name="ml_submit" class="butt">Submit</button> <div class="ml_feedback"><?= $ml_feedback ?></div> </form>
    2 points
  4. Hi @DrQuincy I think you can do this by hooking in to PW's login flow. Here's how I'd attempt to do this - untested - just to give you some inspiration... Create a new field on the User template for their hashed password from your old system. Write a script to populate this field where there's an email match between the old system and the new one. If the new system doesn't have users in it yet, then great, you'll be creating user records for everyone from the old system. Make sure you create a long, random, password for the initial processwire password on any new users you create and leave existing user passwords untouched. Store the hash from your system in the new field on the matching user and save any changes. You need a before hook on the session::login method. Use the name param (in $event->argument(0)) to match on emails if that's how you did it in the old system, if you have exactly one match, and this user has a non-empty value for your old password hash, use your existing pwd checking algorithm to authenticate them from the pass ($event->arguments(1)) parameter. If that works, the plaintext password they submitted to you is the correct password. Now set the PW password on that account using the plaintext password you just verified is correct AND delete the old password hash value. Now the correct password has been stored in the User's PW pass field, just exit the hook and PW should process the login as normal, or call the forceLogin method to log them in and do your own redirect. Basically, as people log in, they will auto-convert their accounts over to PWs way of doing things. Once they convert, the login process just reverts to using PWs method. Potential gotcha: more than one account in the PW system with the same email (if there are already accounts in there.) After a reasonable period has elapsed, you could then deal with seemingly dormant accounts by emailing non-converted users and asking them to login (or otherwise handle them as appropriate.) Hope that helps.
    2 points
  5. Loading a website after updating to PHP 8.x failed. The connection to the server was reset while the page was being loaded. NS_ERROR_NET_RESET PHP error was not catched, nothing displayed, nothing logged. After a long search, it turned out that the Gettext extension was responsible for the fatal error. Code example to reproduce: $locale = 'de_DE'; setlocale(LC_ALL, $locale); echo gettext("foo bar"); // NS_ERROR_NET_RESET After adding the environment variable LANG it worked as expected. $locale = 'de_DE'; setlocale(LC_ALL, $locale); putenv("LANG=$locale"); echo gettext("foo bar"); // foo bar Same behaviour with ngettext() and maybe other Gettext functions. Alternatively, the problem could also be solved with putenv("LC_ALL=$locale"); Possibly affected: ProcessWire\Languages::setLocale() I found little information about this behavior on the Internet.
    1 point
  6. That's really good, thanks for the suggestions! I'm going to keep that as a potential way. I like it, thinking outside the box. 🙂 My somewhat liazier solution is simply to add a field to the user template: Legacy, Legacy converted, and New. If A Legacy user attempts a login it simply sends them to the password reset page. And when they do they become Legacy converted. Your way is far slicker so I'll present the client with both options.
    1 point
  7. Issue submitted: https://github.com/processwire/processwire-issues/issues/1911
    1 point
  8. Hello @BrendonKoz, On the code above I didn't copy the namespace and use statements but this is not the issue, instead of using ProcesswireTemplate I can directly use strings but this is not working either. EDIT: I've edited my code to remove this ambiguity, using strings instead of class ProcesswireTemplate.
    1 point
  9. Depending on your requirements, the Stripe payment processor for FormBuilder might be worth investigating. You can set Paypal to be a payment option in Stripe so that users can pay using PayPal but you can keep all your transactions on the same plaform. Out of the box FormBuilder might not do everything you need (if you need a basket etc) but it is possible to hook into the module to update line items / prices etc. We recently built a basket system for a site that we'd wired directly into Stripe but then shifted over to hook into Formbuilder instead because it already did a lot of the heavy lifting in using the Stripe API.
    1 point
  10. Maybe open an issue in the issue tracker if not yet done?
    1 point
  11. Source: https://processwire.com/blog/posts/processwire-3.0.107-core-updates/#trash-for-all
    1 point
  12. My understanding is that there are three hookable methods that you can use if you want detailed control of which pages are viewable, editable and "listable". Viewable = user may view the page on the front-end. Example hook in /site/init.php: $wire->addHookAfter('Page::viewable', function(HookEvent $event) { $page = $event->object; // Return early if PW has determined that the page is not viewable if(!$event->return) return; // Your test here... if($page->title === 'foo') { // User may not view the page $event->return = false; } }); Editable = user may edit the page in ProcessPageEdit. Example hook in /site/init.php: $wire->addHookAfter('Page::editable', function(HookEvent $event) { $page = $event->object; // Return early if PW has determined that the page is not editable if(!$event->return) return; // Your test here... if($page->title === 'foo') { // User may not edit the page $event->return = false; } }); Listable = user can see the page in ProcessPageList. Note: superusers are not affected. Example hook in /site/init.php: $wire->addHookAfter('Page::listable', function(HookEvent $event) { $page = $event->object; // Return early if PW has determined that the page is not listable if(!$event->return) return; // Your test here... if($page->title === 'foo') { // User may not see the page in ProcessPageList // And therefore may not see its descendant pages either $event->return = false; } }); There's a catch to be aware of with this last hook. Page::listable only affects ProcessPageList and not other parts of the admin. If the existence or title of the page must remain private then you'll need to handle the following separately: Admin search (AJAX results) Admin search (results page if user hits enter) Lister (aka "Find") Maybe other places such as Page Reference fields I think @adrian has some experience with hiding pages from these places and might have some suggestions. Edit: In my opinion it would be nice if PW used Page::listable to automatically restrict the visibility of pages throughout the admin. I opened a request here: https://github.com/processwire/processwire-requests/issues/379 Besides Page::viewable, Page::editable and Page::listable there are also the following hookable methods that can be used in a similar way to the examples shown above. All of these methods are in PagePermissions.module. Page::publishable Page::deleteable (aka Page::deletable) Page::trashable Page::restorable Page::addable Page::moveable Page::sortable
    1 point
  13. Ok, you are right @Robin S. I think, now i understand you. ?‍♂️ Your idea to use a field for the parent-child relationship works great: I add a empty repeaterfield to template. In ready.php i add to a edit button (each children page) include pagination. It looks like it´s a repeater field items (old page situation). All happy. $this->addHookAfter('Inputfield::render', function (HookEvent $event) { $field = $event->object; $form = wire('modules')->get("InputfieldForm"); //get edit page $page_id = $this->input->get('id'); $page = $this->pages->get($page_id); //add child pages to empty repeater - items not moveable, deletable or to clone if ($field->name === 'products') { $childPages = $page->children('limit=15'); $pagination = $childPages->renderPager(['getVars' => array("id" => $page_id)]); foreach ($childPages as $childPage) { //build the fake-repeater header $wrap = $this->wire('modules')->get('InputfieldFieldset'); $wrap->addClass('InputfieldRepeaterItem InputfieldStateCollapsed InputfieldRepeaterMinItem InputfieldNoFocus'); $wrap->name = "repeater_item_{$childPage->id}"; $wrap->label = $childPage->index + 1 . ': ' . $childPage->product_title . ' Art.: ' . $childPage->title; //if all fields are filled out mark the header $isFilledOut = ($childPage->product_keyword && $childPage->product_type); ($isFilledOut ? $wrap->addClass("text-black", "headerClass") : ""); //add the edit button for the child page (class pw-modal for modal edit) $submit = wire('modules')->get("InputfieldButton"); $submit->attr("value", "Produkt Details bearbeiten"); $submit->attr('data-href', wire('config')->urls->admin . "page/edit/?id=" . $childPage->id); $submit->addClass('pw-modal uk-margin-bottom uk-margin-left'); $wrap->add($submit); $form->add($wrap); } $field_style = '<style>.text-black{color:black !important;}.InputfieldRepeaterItemControls{display:none !important;}</style>'; $event->return = $form->render(); $event->return .= $field_style; $event->return .= $pagination; } });
    1 point
  14. It's getting even more confusing - now it works even in /site/ready.php ? <?php namespace ProcessWire; class RockHook extends WireData implements Module { public static function getModuleInfo() { return [ 'title' => 'RockHook', 'version' => '0.0.1', 'summary' => 'RockHook', 'autoload' => true, 'singular' => true, 'icon' => 'bolt', 'requires' => [], 'installs' => [], ]; } public function init() { bd('init()'); bd(1); bd(2); bd(3); bd('add handleWebhook'); $this->addHookAfter("ProcessPageView::execute", $this, "handleWebhook"); bd('add hello-hook in init()'); $this->addHookAfter('hello', function(HookEvent $event) { bd('hello-hook fired in init()'); }); } public function ready() { bd('ready()'); bd('add hello-hook in ready()'); $this->addHookAfter('hello', function(HookEvent $event) { bd('hello-hook fired in ready()'); }); } public function handleWebhook($event) { bd('handlewebhook fired'); bd('trigger $this->hello()'); $this->hello("trigger hello() in execute() hook"); } public function ___hello($where) { bd('hello!', $where); } }
    1 point
  15. @MarkE - just be aware that hook doesn't prevent a user getting access to the pages via Pages > Find and also via the Live Search in the menu bar. There are some hooks in the Admin Restrict Branch module that will help you with that though.
    1 point
  16. The view permission controls viewing on the front-end, it doesn't relate to pages being listed in Page List. For more advanced control over page permissions try hooking after the following Page methods, returning true/false as needed. $page->listable() is the one related to which pages appear in Page List. $page->listable() bool Returns true if the page is listable by the current user, false if not. Can also be used as property: $page->listable $page->moveable() bool Returns true if the current user can move this page. Optionally specify the new parent to check if the page is moveable to that parent. Can also be used as property: $page->moveable $page->publishable() bool Returns true if the page is publishable by the current user, false if not. Can also be used as property: $page->publishable $page->restorable() bool Returns true if page is in the trash and is capable of being restored to its original location. @since 3.0.107 $page->sortable() bool Returns true if the current user can change the sort order of the current page (within the same parent). Can also be used as property: $page->sortable $page->trashable() bool Returns true if the page is trashable by the current user, false if not. Can also be used as property: $page->trashable $page->viewable() bool Returns true if the page (and optionally field) is viewable by the current user, false if not. Can also be used as property: $page->viewable An example of a Page::listable hook - note that such hooks do not restrict the superuser role: $wire->addHookAfter('Page::listable', function(HookEvent $event) { $page = $event->object; if($this->wire()->user->hasRole('editor') && $page->template == 'basic_page') $event->return = false; });
    1 point
×
×
  • Create New...