-
Posts
1,489 -
Joined
-
Last visited
-
Days Won
43
Everything posted by gebeer
-
Look at 3.2 in the first post, I use basic HTTP authentication which, in combination with SSL, was save enough for that application. If you need more sophisticated auth scenarios, have a look at JSON Web Tokens. @thomasaull implements it in his REST-Api site profile.
-
Thanks for that hint. My site is running on 3.0.62. That explains why it is not working for me. Will look at upgrading. @Robin S Interesting approach, thank you! Would this add significant performance overhead with using soundex() and metaphone() on the PHP side rather than in MySQL?
-
@BitPoet Thank you very much for the code! I put it in ready.php and my find logic inside a custom ProcessPageAdd::executeMembersExist hook looks like this $selector = 'template=member'; $firstname = $this->wire('sanitizer')->text($this->wire('input')->get('firstname')); $lastname = $this->wire('sanitizer')->text($this->wire('input')->get('lastname')); if($firstname) $selector .= ", soundex.firstname={$firstname}"; if($lastname) $selector .= ", soundex.lastname={$lastname}"; $selector .= ', include=all'; $users = $this->wire('users')->find($selector); I always get an error: Field does not exist: soundex Full backtrace: ProcessPageAdd: Field does not exist: soundex DEBUG MODE BACKTRACE ($config->debug == true): #0 /var/www/uhtinew.dev/wire/core/Wire.php(386): ProcessWire\PageFinder->___getQuery(Object(ProcessWire\Selectors), Array) #1 /var/www/uhtinew.dev/wire/core/WireHooks.php(698): ProcessWire\Wire->_callMethod('___getQuery', Array) #2 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\PageFinder), 'getQuery', Array) #3 /var/www/uhtinew.dev/wire/core/PageFinder.php(391): ProcessWire\Wire->__call('getQuery', Array) #4 /var/www/uhtinew.dev/wire/core/PageFinder.php(391): ProcessWire\PageFinder->getQuery(Object(ProcessWire\Selectors), Array) #5 /var/www/uhtinew.dev/wire/core/Wire.php(386): ProcessWire\PageFinder->___find(Object(ProcessWire\Selectors), Array) #6 /var/www/uhtinew.dev/wire/core/WireHooks.php(698): ProcessWire\Wire->_callMethod('___find', Array) #7 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\PageFinder), 'find', Array) #8 /var/www/uhtinew.dev/wire/core/PagesLoader.php(248): ProcessWire\Wire->__call('find', Array) #9 /var/www/uhtinew.dev/wire/core/PagesLoader.php(248): ProcessWire\PageFinder->find(Object(ProcessWire\Selectors), Array) #10 /var/www/uhtinew.dev/wire/core/Pages.php(232): ProcessWire\PagesLoader->find('template=member...', Array) #11 /var/www/uhtinew.dev/wire/core/Wire.php(386): ProcessWire\Pages->___find('template=member...', Array) #12 /var/www/uhtinew.dev/wire/core/WireHooks.php(698): ProcessWire\Wire->_callMethod('___find', Array) #13 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\Pages), 'find', Array) #14 /var/www/uhtinew.dev/wire/core/PagesType.php(260): ProcessWire\Wire->__call('find', Array) #15 /var/www/uhtinew.dev/wire/core/PagesType.php(260): ProcessWire\Pages->find('template=member...', Array) #16 /var/www/uhtinew.dev/site/modules/ProcessUhtiPageAddMember/ProcessUhtiPageAddMember.module(231): ProcessWire\PagesType->find('template=member...') #17 /var/www/uhtinew.dev/wire/core/WireHooks.php(782): ProcessWire\ProcessUhtiPageAddMember->membersExist(Object(ProcessWire\HookEvent)) #18 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\ProcessPageAdd), 'executeMemberse...', Array) #19 /var/www/uhtinew.dev/wire/core/ProcessController.php(244): ProcessWire\Wire->__call('executeMemberse...', Array) #20 /var/www/uhtinew.dev/wire/core/ProcessController.php(244): ProcessWire\ProcessPageAdd->executeMembersexist() #21 /var/www/uhtinew.dev/wire/core/Wire.php(380): ProcessWire\ProcessController->___execute() #22 /var/www/uhtinew.dev/wire/core/WireHooks.php(698): ProcessWire\Wire->_callMethod('___execute', Array) #23 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\ProcessController), 'execute', Array) #24 /var/www/uhtinew.dev/wire/core/admin.php(113): ProcessWire\Wire->__call('execute', Array) #25 /var/www/uhtinew.dev/wire/core/admin.php(113): ProcessWire\ProcessController->execute() #26 /var/www/uhtinew.dev/site/modules/AdminThemeCustom/controller.php(13): require('/var/www/uhtine...') #27 /var/www/uhtinew.dev/site/templates/admin.php(520): require('/var/www/uhtine...') #28 /var/www/uhtinew.dev/wire/core/TemplateFile.php(268): require('/var/www/uhtine...') #29 /var/www/uhtinew.dev/wire/core/Wire.php(380): ProcessWire\TemplateFile->___render() #30 /var/www/uhtinew.dev/wire/core/WireHooks.php(698): ProcessWire\Wire->_callMethod('___render', Array) #31 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\TemplateFile), 'render', Array) #32 /var/www/uhtinew.dev/wire/modules/PageRender.module(514): ProcessWire\Wire->__call('render', Array) #33 /var/www/uhtinew.dev/wire/modules/PageRender.module(514): ProcessWire\TemplateFile->render() #34 /var/www/uhtinew.dev/wire/core/Wire.php(383): ProcessWire\PageRender->___renderPage(Object(ProcessWire\HookEvent)) #35 /var/www/uhtinew.dev/wire/core/WireHooks.php(698): ProcessWire\Wire->_callMethod('___renderPage', Array) #36 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\PageRender), 'renderPage', Array) #37 /var/www/uhtinew.dev/wire/core/WireHooks.php(782): ProcessWire\Wire->__call('renderPage', Array) #38 /var/www/uhtinew.dev/wire/core/WireHooks.php(782): ProcessWire\PageRender->renderPage(Object(ProcessWire\HookEvent)) #39 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\Page), 'render', Array) #40 /var/www/uhtinew.dev/wire/modules/Process/ProcessPageView.module(205): ProcessWire\Wire->__call('render', Array) #41 /var/www/uhtinew.dev/wire/modules/Process/ProcessPageView.module(205): ProcessWire\Page->render() #42 /var/www/uhtinew.dev/wire/core/Wire.php(383): ProcessWire\ProcessPageView->___execute(true) #43 /var/www/uhtinew.dev/wire/core/WireHooks.php(698): ProcessWire\Wire->_callMethod('___execute', Array) #44 /var/www/uhtinew.dev/wire/core/Wire.php(442): ProcessWire\WireHooks->runHooks(Object(ProcessWire\ProcessPageView), 'execute', Array) #45 /var/www/uhtinew.dev/index.php(56): ProcessWire\Wire->__call('execute', Array) #46 /var/www/uhtinew.dev/index.php(56): ProcessWire\ProcessPageView->execute(true) #47 {main} It seems like the hook never gets called, although I can see it in Tracy Hooks Triggered I tried to add a low hook priority 10 but it doesn't make a difference. Also tried to add the hook in admin.php, since I only need it there. Same results. Do you have this working in one of your projects and what could be wrong here?
-
Hello all, I'd like to revive this thread as I could use such functionality for a project with more than 2000 users round the world. When new users are added I'd like to perform a fuzzy search on text fields firstname and lastname to avoid accidental duplicate entries. @teppo thanks a lot for the code examples. Everything is basically there to perform soundex search. But how would one implement this today without hacking the core? Would that be possible in a module that extends classes Selector and DatabaseQuerySelectFulltext? Or an additional option to $pages->find(), e.g. ['fuzzy' =>true]? I'm not that experienced at OOP yet, so any pointers on how to approach this would be very much appreciated.
-
@Batyr Happy you like the tutorial. Could you please be more specific about what exactly is not working: Do you get any error messages? Then post them. Do you have correct JSON in the body field of the REST Client Addon? Pls post a screenshot of how the REST Client window looks when you try to send the PUT request. Then I might be able to help.
-
SVG-based image lazyloading as a matter of enjoyable UX
gebeer replied to Andreas Augustin's topic in Showcase
great writeup on the svg lazyloading. Thank you very much for that. Very inspiring. -
In the first post of my REST API tutorial I link to clsource's REST helper class. You could use that one or lend some code from it.
-
Is it possible to use HTML tags in log files and how?
gebeer replied to Juergen's topic in General Support
I had this problem once and solved it with JSON. It is quick and simple. In your case $logmsg = array( 'Email' => $emailvalue, 'Betreff' => $subjectvalue, 'Nachricht' => $commentsvalue ); $log->save('contact-form', json_encode($logmsg)); -
Custom User Types, Page Edit vs. Profile Edit
gebeer replied to ethanbeyer's topic in General Support
@ethanbeyer As I mentioned above, I have a custom dashboard process module with links to the areas that members are allowed to view/edit. One is the edit page for their profile at /access/users/edit/?id=xxxx. I then redirect them to the dashboard upon login through a hook so they never see the page tree. $this->addHookBefore('ProcessHome::execute', $this, 'rootPage'); /** * Redirect users with custom role to another page after login */ public function rootPage(HookEvent $event) { if($this->config->ajax) return; // this is needed so that ajax background tasks still work $user = $this->user; if($user->isSuperuser() || !$user->hasRole('member')) return; if($user->hasRole('member')) { $url = $user->statusPageUrl; // custom property added via hook that returns the dashboard URL } $this->session->redirect($url); } I also have a hook that prevents them from viewing the page tree (and other pages, if required). $this->addHookAfter('Page::viewable', $this, 'viewable'); /** * Give only superusers access to certain pages */ public function viewable(HookEvent $event) { if($this->config->ajax) return; // this is needed so that ajax background tasks still work $page = $event->object; if($this->user->isSuperuser() || $page->template != 'admin') return; $denyPages = array(3); // 3=page list if (in_array($page->id, $denyPages)) { $this->session->redirect($this->user->statusPageUrl); } } I then deactivated Breadcrumbs for the member users so they don't see what else would be available. The whole logic sits on top of a custom admin theme which is based on Reno theme. But I think this is not necessary at all. It just made it easier to remove the main navigation and other stuff that I didn't need. With all that in place the members can now only see what they need to. And I can use the dashboard to render some summary information about their profile etc. This was quite some work to setup but in the end it seemed more feasible to go that route rather than trying to change the core profile edit page functionalities. But I still can't see the security benefits that come through the current restrictions in the profile edit page. If that page allows users to change their password why not allow them to change other information? EDIT: Setting up a custom dashboard process module actually isn't that hard and can be fun @bernhard wrote a great tutorial about it.- 7 replies
-
- 2
-
- custom user
- repeater
-
(and 4 more)
Tagged with:
-
Custom User Types, Page Edit vs. Profile Edit
gebeer replied to ethanbeyer's topic in General Support
@ethanbeyer Did you manage to work around the profile edit screen limitations? I have a directory site where users are the directory pages. Their profiles have lots of fields, including tabs andf repeaters. Since it was not possible to use the native profile edit process, I needed to find a workaround. I have a custom Dashboard page in the admin where users have a link to their user page edit screen URL like /access/users/edit/?id=1377 To have this profileEditUrl easily available for every user, I added it as a custom property via a hook: $this->addHookProperty('User(template=member)::profileEditUrl', $this, 'addPropertyProfileEditUrl'); /** * adds profile edit URL property to every member user * * @param HookEvent $event * */ public function addPropertyProfileEditUrl($event) { $user = $event->object; $event->return = $this->pages->get('template=admin, name=users')->url . "edit/?id={$user->id}"; } I had to give the member role page edit permissions for the member template. So I needed a way to make sure that they could not access other members' edit urls by just changing the id parameter in the URL. My hook for that looks like /** * prevent member user from editing other member pages */ $this->addHookAfter('ProcessPageEdit::loadPage', function(HookEvent $event) { $id = $event->arguments[0]; $user = $this->user; if($user->isSuperuser()) return; $editPage = $this->wire('pages')->get((int) $id); if($editPage->template != 'member') return; // normal members can only edit their own profile if($editPage->id != $user->id) wire('session')->redirect($user->profileEditUrl); }); Could also have hooked into Page::editable and apply logic there, I guess...- 7 replies
-
- 3
-
- custom user
- repeater
-
(and 4 more)
Tagged with:
-
Custom User Types, Page Edit vs. Profile Edit
gebeer replied to ethanbeyer's topic in General Support
@Zekathanks for pointing me there. I wasn't even aware that this issue exists. Otherwise I wouldn't have created my issue regarding tabs not being displayed correctly.- 7 replies
-
- 1
-
- custom user
- repeater
-
(and 4 more)
Tagged with:
-
I submitted an issue report: https://github.com/processwire/processwire-issues/issues/552 If you think, this should be fixed, please leave a comment on github. Thank you. I did not file this as a feature request because I consider it a bug. One would expect to display tabs as tabs and not as fieldsets. No matter whether in page edit or profile edit screen.
-
Custom User Types, Page Edit vs. Profile Edit
gebeer replied to ethanbeyer's topic in General Support
@ethanbeyer Thank you for bringing this up. I also have the feeling that the profile edit page functionality can be improved. I recreated your example setup on the latest dev branch. Same behaviour here for repeater fields on the profile edit page of the member user when they only have profile-edit permissions. If you add page edit permissions to the member role and then go to the user template Access settings and add view and edit permissions for the member role, then the repeater field is working. But this is not a good solution. Because once you enable page-edit permissions for the member role, they get access to the page tree which might not be desired. And you'd have to go through all your templates' access settings to make sure that the member user can only do what you want to allow them to do which is a bit tedious. Also it is not obvious that you need to give page edit permissions for the user template to a role that already has profile edit permissions. Permission to edit the profile should imply the permission to edit the user template. All in all this is very confusing behaviour. I'd consider this a bug. Don't know if others would agree and would be happy to hear their opinion on this.- 7 replies
-
- custom user
- repeater
-
(and 4 more)
Tagged with:
-
Love this thread. Once you make a PR for the findArray to the PW repo, please let us know, so we can upvote it. I would definitely be willing to pay if RockDatatables was released as a pro module.
-
Module: AIOM+ (All In One Minify) for CSS, LESS, JS and HTML
gebeer replied to David Karich's topic in Modules/Plugins
Hello all, I'm experiencing serious issues when using PW built in template cache together wit AIOM. When a page is loaded from cache and you then change any of your CSS or JS assets, the rebuilding of the combined/minified versions is not being triggered at all. This explains @anttila's problem mentioned above. Also when you use the button in the module config screen to clear the asset cache, the template caches are not being cleared. So next time a page is loaded from cache, it will point to old asset files that do not exist anymore. I also did extensive testing with @matjazp's fork. But same problem there. After looking at the module code, I think that the problem lies in the module hooking into Page::render. But this never gets called once pages are cached. Please correct me if I'm wrong here. One possible solution would be to hook into PageRender::renderPage instead. This always gets called for every page request and returns a cached version if available or a fresh Page::render. I forked @matjazp's version as it seems to be the most up to date and will try to fix these issues... -
Interesting discussion about extra address fields. I created a fieldtype 'AddressGeoNames' that consists of separate text inputs for country, city, postcode, lat, lng etc. where field values are verified and partially auto populated through geonames.org API. This is a standalone Inputfield that geocodes through the google maps api. But it can also be connected to an InputfieldLeafletMapMarker field so that the given address will be rendered as pin on the map. The pin can also be manually repositioned and writes back lat/lng to the AddressGeonames field. I am currently using this in one live project with about 1500 users and they seem to get along fine with data input and geocoding. This is how it looks like when used next to a InputfieldLeafletMapMarker field: Planning on adding support for GoogleMapsMarker, too and eventually releasing it on github. Not quite ready yet, though. But if anyone is interested in the concept, I could share what I have so far.
-
I can confirm that the module is still sending mails ok on my site. No errors.
-
@patman I'd suggest making the PR to the original repo as this feature will be a good improvement to the original module.
-
_js_geocode_address is used to hold the address that gets returned by the JS geocoding call, thats why the value is not set when rendering the field. This address is then stored to the data field in the DB on save. If you want to store the raw data in that data field, you also would need to change the schema, because it currently allows a max of 255 characters which will not be enough to store all the raw data. When you then retrieve the address in your template with $page->mylocationfieldname->address, it will return the raw data that you stored in that field. So you would need to extend the get method in LeafletMapMarker.php to return only the address and not the whole string. I think it would make more sense to extend the DB scheme and have a field raw that stores the raw data. You'd need a hidden field in the form, e.g. _js_geocode_raw that gets populated from the JS (just like the address field gets populated now) and then on save sends that value and stores it to the DB. You'd need to add that to the set method in LeafletMapMarker.php.
-
@BrendonKoz Have you checked with some debug tool (I highly recommend Tracy Debugger Module) that $json['results'][0] has a value?
-
@BrendonKoz The JS side of this module is using the OSM geocoder while the geocode() method in LeafletMapMarker.php is using the Google geocoder. If you wanted to save the raw data returned by Google geocoder, you would need to extend the database schema in FieldtypeLeafletMapMarker.module to add the necessary columns (e.g. streetAddress) to the table where data for this fieldtype ist stored. And extend the geocode method so it can save the raw data to the newly added columns. And add the respective keys to the construct method in LeafletMapMarker.php. Then you should be able to access your values in your template php like: $map = $modules->get('MarkupLeafletMap'); echo $map->streetAddress; and use those values to render the JSON+LD JS. You would either need to fork the original module and then add your code or write your own module that extends the original one and adds your methods.
-
Hello, having the same problem as described by @Ivan Gretsky. Inside a namespaced module I have this method with a try catch block: public function addJson($pages, $filename) { $tmpPath = $this->files->tempDir('json'); try { $start = microtime(true); $jsondata = $this->getGeoJsonCollection($pages); $time = microtime(true) - $start; va_dump($time); // Note the intentional typo $filePath = $tmpPath . $filename; if($tmpPath && $jsondata) { //write json data into geojson.json file if(file_put_contents($filePath, $jsondata)) { $jsonPage = $this->jsonPage; $jsonPage->of(false); $file = $jsonPage->jsonfiles->getFile($filename); if($file) $jsonPage->jsonfiles->remove($file); $jsonPage->save(); $jsonPage->jsonfiles->add($filePath); $jsonPage->save(); if(!unlink($filePath)) $this->message("Error deleting temporary $filePath"); $this->message("$filename created and saved"); $this->log->save('json', "$filename created in $time seconds and saved"); return true; } else{ return false; } } } catch (\Exception $e) { $this->log->save('json', $e->getMessage()); } } I'm calling the module via command line from a worker php script that bootstraps PW. I get this ouput from my script and nothing logged to my custom json.text log file: PHP Fatal error: Call to undefined function ProcessWire\va_dump() in /var/www/uhtinew.dev/site/modules/SaveJson/SaveJson.module on line 208 Fatal error: Call to undefined function ProcessWire\va_dump() in /var/www/uhtinew.dev/site/modules/SaveJson/SaveJson.module on line 208 Error: Call to undefined function ProcessWire\va_dump() (line 208 of /var/www/uhtinew.dev/site/modules/SaveJson/SaveJson.module) This error message was shown because: site is in debug mode. ($config->debug = true; => /site/config.php). Error has been logged. So it seems that WireException is catching this before my try catch.
-
@BitPoet Thank you very much for the heads-up. Also to @Robin S. I finally ended up with this hook function in a module context which does exactly what I need, also in ajax mode: $this->addHookAfter('Field::getInputfield', $this, "lockConfirmedItems"); public function lockConfirmedItems(HookEvent $event) { if($this->user->isSuperuser() || $this->user->hasRole('certificationadmin')) return; $thisPage = wire('page'); // // Only run our locking logic if we are editing a user in the backend if($thisPage->process != "ProcessUser") return; $repeaterPage = $event->arguments(0); $context = $event->arguments(1); if($context != "_repeater" . $repeaterPage->id) return; if(!$repeaterPage->confirmed) return; $event->return->collapsed = 7; } Setting collapsed with the Inputfield constants like in BitPoet's code didn't work for me.
-
Hello, I would like to prevent future editing of a repeater item once a checkbox in the repeater item has been checked. Either the form inputs of the repeater item should be read only or only the field values should be rendered. Looking at the module code I can't seem to figure out where to hook for that functionality. Any ideas would be much appreciated.
-
@Pierre-Luc is there still no way to get feedback on success/errors when sending through the Mailgun API? I would like to build a Lister(Pro) Action around your module and for that I would like to show results after sending. Something like $mail->getResult(); in WireMailSMTP. EDIT: Guess I would need to use Mailgun Events for tracking emails. Right?