• Content count

  • Joined

  • Last visited

Community Reputation

63 Excellent

About ethanbeyer

  • Rank
    Full Member

Recent Profile Visitors

631 profile views
  1. ethanbeyer

    @Jon this sounds a little strange. Reading most of your comment, I would think that this might have something to do with your Office365 having sending limits that you're going over. The TLS issue is what makes it weird, though - because if it was a limits problem, that would not be happening. Is there anything in the logs that gives what error is encountered, rather than saying it just stops sending?
  2. ethanbeyer

    At the risk of sounding very pedantic, this condition would result in the timestamp for any page with template cache enabled, which is not what I'm looking for. I only want the timestamp to appear on cached versions of the page - so if I am logged in, and am served a page that doesn't give cached pages to logged-in users, I don't want the timestamp (or maybe I would have a different comment that said this page was generated now and is not cached, etc). On the other hand, if I was not logged in, and looked at the source for the same page and was served the Cached version, I'd expect to see the timestamp. Essentially, I am looking to inject the comment ONLY at the time of the creation of the cached page, not on normal render - so this makes me wonder if there's a hook I could grab before page render to determine whether or not the page render was being performed in order to create a CacheFile. Does this make sense?
  3. ethanbeyer

    Well, I feel a bit silly. I didn't think that needed to be done because of the call to $sanitizer->selectorValue($input->get('s')); . Anyhow, you solved my problem - and I think you're right: String Selectors are probably an easier method to go with than Array Selectors until they've been documented better.
  4. ethanbeyer

    In theory, yes - but the largest problem with that approach is that the timestamp is always there. This is mostly for my OCD brain, but I essentially wanted a sanity check in the source that would quickly show that a page was or was not cached, and when.
  5. ethanbeyer

    As a follow-up...has anyone ever built a site using delayed output with template caching? This is a new frontier for me and I don't know how to set it up.
  6. Not sure if this is possible - but I'd like to add something like this to cached pages via Template Cache: <!-- Page Cache Generated 2018-05-22 @ 12:45:44 UTC / Expires 2018-05-29 @ 12:45:44 --> I thought there might be a way to see if a cache file existed for the page in question, and if it did not, to include that comment in the render->cacheFile->save(). Does anyone know?
  7. ethanbeyer

    I don't think anything has changed with Repeaters in this realm yet.
  8. I'm trying to build a Search Template. The way I had been doing it was by constructing a Selector one string at a time in an array, and then imploding that array with ", " in order to create a Selector that was passed to $pages->find($selectorStr). This is a bare-bones example of what I was doing: $search = $sanitizer->selectorValue($input->get('s')); $selector[] = 'template=member'; $selector[] = "field1|field2|field3%={$search}"; $selector[] = "(repeaterField.fieldX|repeaterField.fieldZ%={$search})"; $selectorStr = implode($selector, ", "); $results = $pages->find($selectorStr); This worked as expected, and I was able to utilize OR-groups within the Selector. There is a problem, though: for whatever reason, despite sanitizing the incoming value, if the user input a string like "test,foo", ProcessWire threw this error: Exception: Unknown Selector operator: '' -- was your selector value properly escaped? field='foo', value='', selector: 'include=all, children.count>0, title%=test,foo' (in /var/www/public/wire/core/Selectors.php line 378) So I moved on to Selector Arrays, and now I'm having trouble with OR-groups. I found out about Selector Arrays (not documented at all as far as I can see in the API Reference) through a ProcessWire blog post about 3.0.13. Here's the code (using the actual field names, unlike above): $selectorArray = []; // template $selectorArray[] = ['template', 'directory-member']; // basic fields $selectorArray[] = [ 'field' => [ 'dm_firstName', 'dm_middleName', 'dm_lastName', 'email', 'dm_ficmTitle', 'dm_addressStreet', 'dm_addressCity', 'dm_addressState', 'dm_addressPostalCode', 'dm_birthday', 'dm_phone1', 'dm_phone2' ], 'operator' => '%=', 'value' => $searchStr ]; //repeaters $selectorArray[] = [ 'field' => [ 'dm_contactREP.dm_contactMethod', 'dm_contactREP.dm_contactValue' ], 'operator' => '%=', 'value' => $searchStr, 'group' => 'repeaters' ]; // search the parent's name $selectorArray[] = [ 'group' => 'parentTitle', 'field' => 'parent.title', 'operator' => '%=', 'value' => $searchStr, ]; // sort $selectorArray[] = ['sort', 'dm_lastName']; $selectorArray[] = ['sort', 'dm_firstName']; The problem is that the Selector is rendered like this (clauses broken into Newlines): template=directory-member, dm_firstName|dm_middleName|dm_lastName|email|dm_ficmTitle|dm_addressStreet|dm_addressCity|dm_addressState|dm_addressPostalCode|dm_birthday|dm_phone1|dm_phone2%=test, repeaters@dm_contactREP.dm_contactMethod|dm_contactREP.dm_contactValue%=(test), parentTitle@parent.title%=(test), sort=dm_lastName, sort=dm_firstName, status<1024 For OR-groups, the whole clause should be wrapped in parentheses - not just the value I'm searching for! So, two questions: Why would a comma in the search value break things that are supposed to be sanitized, and two, how do I get the OR-groups to work as intended?
  9. ethanbeyer

    @alexmercenary Could you post your code? Chances are the variable you're trying to use the attachment() method on isn't an instance of WireMailSmtp.
  10. @gebeer I've looked at your solution and put it in place - and it s a much better start than I would've thought of. I'm not sure it does everything I need it to right now, but I can keep tinkering and I will report back. This is the first time when something ProcessWire does hasn't made a lick of sense to me.
  11. @gebeer It looks like you got further into the solution than I did. Tabs and repeaters were a problem for the Profile Edit screen for me, too. So I eventually gave up and gave users of that template type a role that gave them page-edit permissions, but I hadn't worked out how to make sure that people were only editing their own profiles - so I think your two hooks are helpful there! I know there's a difference in form and function for Profile Edit and Page Edit, but our scenario (where Users = Content) seems like a time when keeping the two completely separate breaks down. I think your method is clean and I will give implementation a shot. Thank you!! Edit: Just saw you'd posted twice in this thread, and just now read the first: That. Exactly that. In full! If it's on a profile page, and I've given the user the permission to edit their page...they should be able to do whatever is necessary on it.
  12. ethanbeyer

    That's strange!
  13. ethanbeyer

    In my _init.php file, the first thing I include is _login.php: <?php // Handle logouts if(isset($input->get->logout)) { $session->logout(); $session->redirect($page->path); } // If they aren't logged in, then show the login form if(!$user->isLoggedin()) { // check for login before outputting markup if($input->post->user && $input->post->pass) { $user = $sanitizer->username($input->post->user); $pass = $input->post->pass; if($session->login($user, $pass)) { // login successful $session->redirect($page->path); } else { $session->login_error = 'Login Failed. Please try again, or use the forgot password link below.'; } } ?> <!DOCTYPE HTML> <html lang="en"> <head> <title>Login</title> <link rel="stylesheet" href="/build/css/app.css"> </head> <body> <div class="container py-5"> <form action='./' method='post'> <div class="row"> <div class="col-12 col-sm-6 offset-sm-3"> <h1>You must be logged in.</h1> <div class="login"> <? if($input->post->user && $input->post->pass) { echo "<p class='error'>" . $session->login_error . "</p>"; }?> <p><input class="form-control" type='text' id="user" name='user' placeholder='Username'/></p> <p><input class="form-control" type='password' id="pass" name='pass' placeholder="Password" /></p> <p><input type='submit' class="btn btn-primary" name='submit' value='Login' /></p> </div> </div> </div> </form> </div> </body> </html> <? die(); // don't go any further if not logged in } If I go to, I am met with a login. After logging in, it takes me straight to the URL I intended to go to before needing to log in. You could change the $page->path to $pages->get('/events/')->httpUrl; - would that work?
  14. ethanbeyer

    It's definitely possible that you haven't set up a valid from-email address. Check out the WireMailSmtp module, it may help!
  15. Hello, I recently posted in this topic, but I decided to start my own thread because while I believe my issue is related to the one in that thread, they are not exactly the same: I have created a custom User Template in the method outlined in the docs. I am creating a directory, so it made sense that every page in the directory was a Directory Member, so they could log in and edit their own information while also keeping the entire directory protected behind a login wall. So the new user type is created: "directory-member". I then created two new roles: "member" and "directory-admin": The "member" only has the ability to View directory-member pages, and "profile-edit", which allows them to manage their own information. The "directory-admin" has the ability to edit any directory-member pages, and administer users. Some Directory Members are both, but all have at least the "member" role. The first hint that something was wrong was when I was testing a "member" user and I could not add a new item to a repeater on that profile. The url for the profile edit (this will be important shortly) is The repeater is set up to load new items through AJAX. If this option is turned off, the rest of this issue is no longer completely valid. But as I have found what I believe to be a pretty large issue in the Processwire codebase, I thought it worth bringing up. See, every page (even a user) has a $page->editUrl() method, and it returns a URL like this: That's all good and fine for users that have page-edit permissions, but if they don't, that link will resolve to the admin's equivalent of a 404. So the way that Processwire currently gets around this is by creating a specific editing area for a user to interact with only their profile: /admin/profile. And that works pretty nicely, except for the fact that nowhere is editUrl() ever made aware of the difference. editUrl() is not hookable, and whether or not a page is editable is based on the PagePermissions module. On top of that, there are several core modules that hardcode a search-and-replace (see InputfieldRepeater.module:627) where the editing screen is for Users. This doesn't allow for a huge degree of flexibility that is offered in other places throughout Processwire. If line 627 of InputfieldRepeater is changed from this: $editorUrl = str_replace('/access/users/edit/', '/page/edit/', $editorUrl); to this: $editorUrl = str_replace('/access/users/edit/', '/profile/', $editorUrl); ...the AJAX repeaters work. It's maddening! As is brought up in the thread I attached above, a lot of the features of page editing are missing within /admin/profile/, and it just makes for an altogether strange editing experience. A user who has "page-edit" permissions for templates other than directory-member, but does have "profile-edit" permissions, will see their user in the Page List, but cannot edit their Page unless they hover over the wrench and click the "Profile" link. It just seems - off. I think what this hinges on for me is that the editUrl() of the user should be "/admin/profile/" if that user is logged in (and their page should be editable from the Page List), or the "/admin/access/users/edit/" url; regardless of the URL, both links should resolve to the Page Edit screen, as the Profile Edit screen seems to be a unnecessarily neutered version of Page Edit.