Jump to content


  • Posts

  • Joined

  • Last visited

Posts posted by DrQuincy

  1. I have a select field that allows the user to choose from a drop down of repeater pages for that current page.

    I have set Selector string to id=page.options

    This works perfectly for me as the superuser but when the client logs in (custom role content-manager) they can't see anything but an empty drop down even though they have edit access for the template.

    So template typeface has a field called initialStyle (Page select as above). That chooses from a repeater call options.

    How do I grant permission for content-managers to see the pages from options in initialStyle when editing a typeface template? content-manager already has (I think by default) view access to repeater_options.


  2. I've noticed that if I have a CKEditor field and add a link that begins with tel: E.g.

    tel:+44 (0)1234 567 0000

    When I view source immediately in CKEditor I see:

    <p><a href="tel:+44 (0)1234 567 000">TEST</a></p>

    So it's not being removed by ACF (that seems to run once you close the source dialog). But when I save it I get this:

    <p><a href="tel:+4420012342056720000">TEST</a></p>

    It looks like it's encoded it correctly (%20 for each space) and then stripped out the %, making the number invalid.

    If you omit tel: from the link everything is encoded correctly.

    It seems to affect all my installs regardless of version (3.0.165+). I have an old site on 3.0.98 that doesn't seem to have this issue. I'm worried now quite a few sites will have invalid numbers.

    What is happening and is this intended behaviour? Does it run through HTML purifier? It seems to be the same even if you set Content Type to Unknown/Text rather than Markup/HTML.

    EDIT: It is HTML purifier. If in the Input tab of the field you set Use HTML Purifier? to false, it doesn't do anything with the HTML. It seems fine with a 3.0.98 site though with the same settings.

    Is there anywhere to switch this feature off while still having Use HTML Purifier? set to true?

    If not, I guess my options are to either switch it off (I think that's okay within a CMS context since ACF would stripe out anything untoward) or tell clients they can only use +, - and 0-9 in phone numbers.

  3. I've realised what was causing it:

    It is down to the map-view template being set to not have a slash at the end. If I change this it works.

    So when it was /maps/submit/test-map and you log in it goes to /maps/submit but when I set it to /maps/submit/test-map/ and you login it, it stays on /maps/submit/test-map/.

    Is this a bug and, if not, how can I use URLs with no slash to redirect in this way? In this instance, it doesn't matter as it makes no difference to have a slash at the end. I just wondered really whether it was a limitation, bug or feature as I do like to display homogeneous pages without slashes.

    I know you can pass the page ID to /processwire/ and then, I guess, use hooks to redirect to the page but wondered if it really required hooks as it seems like this should be part of the standard access functionality.


  4. I have two templates: map-submit, map-view. map-views are children templates of a single map-submit template/page and this is enforced in the template Family settings. map-view is set to option B in Access:

    Show the Login page

    And guest has View pages permission unchecked.

    What I want to happen is if you access a map-view page and aren't logged in it shows the login page, you login and then go straight to the specific page you were trying to view. However, this is what happens:

    • Login page is presented
    • On successful login you it takes you to the parent page, the single map-submit page, e.g. login on maps/submit/test-map (map-view) and go go to maps/submit (map-submit)
    • If you visit the page it should've redirected to (maps/submit/test-map) you are presented with the login page again and so on
    • The only thing that seems to work is to login via the usual CMS URL: /processwire/ and then visit the page

    I can't see anything in the logs. Any ideas how I can fix this as it's currently a bit clunky for the client?

    I don't think I have any unusual modules installed and I have no hooks set up that manipulate the login or the session. I'm on 3.0.184, PHP 7.4.


  5. I'm sure this will be something really simple but I am adding repeaters items to a Pro Fields Matrix and each item gets added twice (they are identical). The code definitely only runs once and there are no duplicate items in $basketTable. Am I doing anything wrong?


    (Irrelevant code has been removed)

    $p = new \ProcessWire\Page();
    $p->template = 'account-order';
    $p->name 	 = $orderReference;
    foreach ($basketTable as $tableItem) {
    	if (isset($tableItem['type']) === true) {
    		if ($tableItem['type'] == 'typeface-trial') {
    			$orderDownload  						= $p->orderDownloads->getNew();
    			$orderDownload->repeater_matrix_type 	= 1;
    			$orderDownload->orderTypeface 			= $tableItem['id'];
    		if ($tableItem['type'] == 'typeface-weight') {
    			$orderDownload  						= $p->orderDownloads->getNew();
    			$orderDownload->repeater_matrix_type 	= 2;
    			$orderDownload->orderDownloadsWeight	= $tableItem['weight-id'];
    			$orderDownload->orderDownloadsLicense	= $tableItem['license-id'];
    		if ($tableItem['type'] == 'typeface-bundle') {
    			$orderDownload  						= $p->orderDownloads->getNew();
    			$orderDownload->repeater_matrix_type 	= 3;
    			$orderDownload->orderDownloadsBundle	= $tableItem['bundle-id'];
    			$orderDownload->orderDownloadsLicense	= $tableItem['license-id'];

    I thought the process is to called getNew(), set the properties, save it and then add it to the page and save that — which I'm doing.

  6. I want to make it so that both user name and email are unique in template users. I've got this which issues a warning, which is better than nothing:

    wire()->addHookAfter('Pages::saveReady', function(\ProcessWire\HookEvent $event) {
    	$page = $event->arguments(0);
    	// Don't run on newly created pages
    	if ($page !== null && $page->created !== 0) {
    		if ($page->template == 'user') {
    			$pages = wire('pages')->find('template=user');
    			foreach ($pages as $p) {
    				// Don't compare to current page
    				if ($p->id != $page->id) {
    					if ($p->email == $page->email) {
    						wire('session')->warning('**' . $p->email . '** is already being used by [' . $p->name . '](/processwire/access/users/edit/?id=' . $p->id  . ') — it is highly recommended each account has a unique email address', \ProcessWire\Notice::allowMarkdown);

    I am also using the EmailNewUser module. The reason I am issuing a warning as opposed to preventing the page being saved is regardless of what I do if you have the Send welcome email checkbox checked, EmailNewUser seems to always send the email out — and always to the duplicate email. I did try overriding the email to a random non-existent one so the email send would go nowhere — but EmailNewUser always emails the duplicate address, which is going to be confusing for the client.

    Is there any way to adapt the above code to prevent EmailNewUser from running if the email is a duplicate? Or is there a way to make EmailNewUser used an updated random email instead? I'm not sure how it works but however EmailNewUser is set up it seems to always use the old value.

    I hope that makes sense! Thanks.

  7. Thank you.

    I never knew about the relationship between the hook and the class and function name so that is a huge help. Studying the Page and Pages class shows me how different they are — so that makes sense! ?

    I'm still not 100% on the second point. From reading about hooks I thought the first condition in parenthesis in a hook was a selector and the second was a filter for fields. So to me, this should work since I want to filter out saves where the page has a specific template:

    \ProcessWire\wire()->addHookBefore('Pages(template=typeface)::saveReady', function(\ProcessWire\HookEvent $event) {
    	\ProcessWire\wire('session')->error('Hook called');

    But instead this is what works:

    \ProcessWire\wire()->addHookBefore('Pages::saveReady(template=typeface)', function(\ProcessWire\HookEvent $event) {
    	\ProcessWire\wire('session')->error('Hook called');

    Why is that? I'm sure I'm missing something really obvious!

  8. I am preventing pages with a specific template from being deleted. I have it working with Pages::trash(template=foo), Pages::delete(template=foo).

    I'm after a better understanding of why this works please.

    1. Why does it work with Pages and not Page, what's the difference?
    2. Why is it Pages::trash(template=foo) and not Pages(template=foo)::trash?

    I have read the docs page on hooks but still don't quite get it. ?

  9. Thanks to you both, some very good suggestions.

    From the template file @Robin S is right, you need to use return. Actually though, just this will work:

    return '<p>An error occurred</p>';

    What would be the advantage of:

    <p>An error occurred</p>
    <?php return $this->halt(); ?>

    What does halt() actually do?

    I am reluctant to use exit() as I have some code in finished.php to run so the above works well for me, thanks.

  10. I've searched for this but couldn't find anything but am maybe using the wrong terms.

    Is there a way to do something like exit('<p>an error occurred!</p>'); but still have ProcessWire “shutdown” normally? Specifically, I want to be able to not render the template but still run some logging code in finished.php.

    Is there a function for this?


  11. In my recent question about utf8mb4, I was asking about columns being lowered from 255 to 250. I misunderstood since I've just realised for some time VARCHAR can go way beyond 250 anyway so none of them in the database dump would be affected. The 250 in that quote is referring to column_name values for indexes.

    Many of PW's tables for fields have indexes:

    data_exact, BTREE data(250)
    data, FULLTEXT, data

    I presume it just uses data or data_exact based on type of selector used (natural language or exact).

    I've never specified an index length when using BTREE though and didn't realise there was a limit (though databases aren't my strong point). Does this just mean that if you do an exact search it will only apply to the first 250 characters of what is in data? If so, why explicitly set a limit? Wouldn't MySQL just default it to 250 anyway — or if you're in strict mode does it cause errors?

    I guess when setting up a database myself I would almost always use BTREE on relations where it's just an int, so would never run into this limitation.



  12. I have changed a database over to utfmb4 and it seems to work. I:

    • Exported the database
    • Replaced utf8 with utf8mb4
    • Imported SQL back in
    • Added $config->dbCharset = 'utf8mb4'; to config.php

    And now I can add Emojis via the CMS. Do I need to do anything else? I know that types like VARCHAR will no longer hold the same amount of characters now some characters take up four bytes but here Ryan says:


    Because utf8mb4 uses more bytes per character, it places new limits on the length of indexes used by ProcessWire. ProcessWire has several 255-character index lengths, and the maximum allowed by utf8mb4 are 250 (250x4=1000). For this reason, we've updated all of our core Fieldtypes to use no more than 250 length indexes in order to support utf8mb4. However, it's possible that 3rd party modules might be using index lengths that aren't compatible with utf8mb4, so this is something to keep in mind.

    Modules-wise I am using mainly Pro Fields and core ones. I also notice studying the database schema that most fields would not be affected by this; some use ASCII, most use larger TEXT fields. The few UTF8 VARCHAR fields are system ones set to 250 as above, which from the sounds of it Ryan allocated for years ago. EDIT: I can see the above refers to index lengths and not VARCHAR lengths, the latter can be larger than 250. See my other post.

    Am I likely to run into any problems? Other than smaller indexes or a huge VARCHAR column being too big to convert over (highly unlikely) what is the worst that can happen?

  13. Ah, thanks — id=page.your_repeater_field_name works perfectly!

    Also, thanks for your explanation regarding point 2.

    Regarding point 1, I worked it out, thanks, per my previous post. I am just storing an array of repeaterID => quantity in my session as I wanted to pull the live data on each request.

  14. Ah, well, I've answered point 1.

    $item = $this->wire('pages')->get('template=repeater_options, id=1255');

    I'd love to know the answer to point 3 though. It would be extremely useful if that's an option. If I do a Page Reference field and set the selector string to template=repeater_options it would get every single item — whereas I'd only want the ones for the current page.

  15. I've got a repeater of product options within a single product page template.

    So template=product has a template file and has a field called options, which is a repeater with title and price.

    I want to store the repeater IDs in a session for the shopping basket and then read them. E.g. if ID 1255 is in the array, this gets me the repeater item:

    $item = $this->wire('pages')->get('template=product, options.id=1255')->options->get('id=1255');
    1. Is there a way to access the repeater directly? It just seems a bit clunky and inefficient to have to get the page and then the repeater by expressing the ID twice, when the ID should be able to get me to the repeater item with one call.
    2. I can run $pages->get(123) with a top-level page and it gets the page but when using get() from a repeater item I have to do get('id=123') — any reason for this? It doesn't really matter, I just wondered.
    3. Is it possible to have a Page Reference field that references a repeater for that page only? So, say I have options in a repeater as above: (Small, £10), (Large, £15), could I have a Page Reference field that let's me pick Small, Large, etc? The use case is specifying product bundles.


  16. On 1/27/2020 at 10:23 PM, Arcturus said:

    During a recent maintenance routine we found that our website's database (1,700+ pages) had thousands of instances of unnecessary, garbage code that had come with copied text from Word. Passages with margins expressed in points, cms and inches, and some that were wrapped in upwards of 7 spans were among the most easily identified crimes. Purging all of this dropped our database size by over 4%.

    A few of the code examples above nuke all inline styles, which will impact some important out-of-the-box functionality for PW3 and CkEditor (depending on your use); specifically with many of the options with tables and lists, such as setting a column width or changing the bullet styles within a nested list.

    To work around that, I made some changes to Ryan's code to target specific tags and to eliminate spans (which you can only add via Source view without pasting them in).

    $wire->addHookAfter('InputfieldCKEditor::processInput', function($event) {
      $inputfield = $event->object;
      $value = $inputfield->attr('value');
      if ((strpos($value, 'style=') === false) && (strpos($value, '<span>') === false)) return;
      $count = 0;
      $qty = 0;
      // Optional remove spans
      $value = preg_replace('/<span.*?>/i', '', $value, -1, $qty);
      $value = preg_replace('/<\/span.*?>/i', '', $value, -1);
      $count = $count + $qty;
      // Remove inline styles from specified tags
      $tags = array('p','h2','h3','h4','li');
      foreach ($tags as $tag){
        $value = preg_replace('/(<'.$tag.'[^>]*) style=("[^"]+"|\'[^\']+\')([^>]*>)/i', '$1$3', $value, -1, $qty);
    	$count = $count + $qty;	
      if(!$count) return;
      $inputfield->attr('value', $value);
      $inputfield->warning("Stripped $count style attribute(s) from field $inputfield->name");	


    This is great, thanks!

    In my settings I am allowing spans with classes. Do you know how I might adapt your code to allow this? If I have HTML with <span class="foo">bar</span> but no empty spans or style assets, <span class="foo">bar</span> stays. But if I add in <span>foo</span>, <span>foo</span> changes to just foo (great!) but <span class="foo">bar</span> also change to bar.

    Also, by default, CKEditor seems to allow style="margin: " I assume this is related to indentation. Any idea how you can disable this? Extraneous spans are annoying but harmless. However, the margin styles mess the formatting up. I do not even have the indentation buttons visible in the toolbar.


  17. Thanks for the explanation, Jan. And, yes, that makes sense as to the difference between roles and permissions.

    I have created the permission tasks-list-access and assigned it to the role content-manager. I have then added 'permission' => 'tasks-list-access' to my Process class but it hasn't made a difference. Am I missing something?

    I refreshed the module and it worked. Hoorah! Thanks very much for your help. ?

    • Like 1
  • Create New...