Jump to content

ryan

Administrators
  • Posts

    16,714
  • Joined

  • Last visited

  • Days Won

    1,515

Everything posted by ryan

  1. er314 sorry I think I misunderstood, as I thought you were suggesting we change the behavior of the existing $pages->get() method, or make it have front-end vs. back-end state (which isn't really possible since PW's API has no front-end/back-end knowledge, and the context of the API is neither). But I'm glad you started this topic because, like I said, I think you've identified a reasonable addition to the API here that should help to further clarify the purpose of the methods for any that may have confusion. And if you and Teppo had confusion about it, then certainly others will too. We've had a $pages->findOne() method in our API since the beginning, but it's just been there as an alias to $pages->get() and just for backwards compatibility. I have dropped the purpose of that method in PW3, since we no longer need that backwards compatibility, and I very much doubt anyone uses it other than the core in a few spots (which I've updated to use the get method instead). I have changed $pages->findOne() to be an alternative to $pages->get() in PW3, that by default filters in the same way as $pages->find(). You'll see this in today's commits. I think this adds more clarity because one can now assume that any $pages method that starts with "find" is also "filtered" by default.
  2. I have to admit I would also like to free the emojis. PW should support utf8mb4 now at install time (click the gear icon on the DB config screen). There are some hard-coded utf8 references in the install.sql files (core and site profile) but the installer updates those before executing the queries (at least it should). And any create/alter table queries that occur after that (like when adding a field, etc.) use the $config->dbCharset that is setup at install time. It sounds like we need to update some column lengths though. Going from 255 down to 250 seems fine to me (for new installs). Though maybe a little more concerned about going down to 191 for InnoDB, especially since we'll probably make InnoDB our new default before PW3 becomes the new master branch. I'll look closer to see if there are situations where the indicated columns need more than 191. In PW3, we wouldn't change the character set or DB engine of existing installations, only new installations. @Pierre-Luc I completely missed your patch before. Sorry about that, sometimes I've got information overload here and it's easy for me to miss things (I think this is likely the case for all of us). I will definitely use and apply this. Thank you for creating it. We won't need the changes to the install.sql files though, since the hard coded db charsets in the install.sql files are updated dynamically by the installer. Though I will need to double check that is working, as it's been awhile since I've tested it. @BitPoet can you expand on why you think it might be possibly dangerous? I know it would certainly be dangerous if applied to existing installations (it would delete data). But I don't think we should ever have PW modify the charset or engine of existing installations. But for new installations, do you see any potential dangers?
  3. er314, I think your intentions are in the right place and I appreciate your determination. You are still asking for an established method with a specific purpose to have a different purpose and an access controlled front-end context, when that is not the purpose or full context of the method. So I hope you can understand why it's not realistic to just change the definition and implementation of an established method. But I get where you are coming from and think you've identified a potential helpful addition in the API, so I go ahead and add a "one" method or change the "findOne" method (in PW3) to behave as a "find just one" method with regard to access control, hidden/unpublished visibility settings, etc. While I don't personally think I will ever use this method, I think that you and Teppo have identified a couple of situations where some might find it handy. Even if one doesn't need it, perhaps just the presence of it in the API will further clarify the intentions of the methods as a whole, for folks that may have thought that the existing find() and get() were the same. I still worry a little bit about people forming an impression that PW's API is doing access control for them, when our entire API is based around providing methods for the developer to control access the way they see fit. The viewable() method is the basis of that. But we've already dipped into that territory with the find() method, and by adding the proposed method, we're not removing any control, just adding more options. So I think it's an okay. What I was stating is that I'm not aware of an instance where anyone has introduced security problems into their site as a result of using the get() method. Though you stated above that you did just that, so now I'm aware of an instance. You are correct that if a developer uses a $pages->get() method to retrieve a page that came from user input, and neglects to validate that the page is one you allow, then that could be a problem. But the same problem could surface anytime a developer neglects to properly sanitize or validate something–anything–that originated from user input. And this is not something you can skip regardless of what method you are using to retrieve a page or group of pages. Simply validating that a page is viewable does not mean it's valid for whatever operation you may be performing upon it. You would need to validate that it uses the expected template, comes from the expected parent, etc. So while we are adding a PW3 method to support your request, be careful not to get the impression that you no longer need to validate a page that originated from user input. Also keep in mind that a $pages->find() or proposed $pages->findOne() method that filters results is based on database-filtering, not runtime filtering. Part of PW's access control model supports runtime hooks to Page::viewable. If your situation includes any runtime access control options and pages you are loading as a result of user input, you shouldn't skip a $page->viewable() call regardless of what method you used to retrieve the page.
  4. Tom, there's a setting in MySQL called ft_min_word_len and the default is 4. That means that if you have any word in your query that is less than 4 characters, MySQL won't be able to match it unless that setting has been adjusted (and you've forced it to rebuild the indexes on your db/table). That's why you aren't able to match "Disability Act", because "Act" likely falls under your ft_min_word_len. You should be able to get the result you are looking for by using %= instead. But since you want to match "disability" and "act" anywhere in your search, you'd need to do this: template=document, title%=disability, title%=act The following code should use %= for words less than 4 characters and ~= for the rest. $search = $input->get->text("search"); if($search) { $selector = ''; $parts = explode(' ', $search); foreach($parts as $key => $part) { if(strlen($part) > 3) continue; // use MySQL LIKE for words under 4 characters in length $selector .= ", title%=" . $sanitizer->selectorValue($part); unset($parts[$key]); } if(count($parts)) { // use MySQL fulltext index for words 4 characters and higher $selector .= ", title~=" . $sanitizer->selectorValue(implode(' ', $parts)); } $results = $pages->find("template=document" . $selector); } else { $error = "Please enter a search term in the search box above."; $results = null; } Come to think of it, it might be nice if PW just did this for you automatically when it detected the condition... but we don't know what the ft_min_word_len setting is.
  5. What is front-end and what is back-end? That term is just for our point of view. The code powering the rest of PW doesn't know about this, nor does it know if you are using it for an access controlled website, or some command line API tool, or something else. While your front-end page is rendered, there might be several other core and/or 3rd party modules also doing other things (because they auto loaded, or they were requested by the page), and those modules in turn might be doing several of their own $pages API calls that have absolutely nothing to do with your output, user or access control. There is a much bigger picture than just what you are doing in your template file. The built-in filtering that you have on top of $pages->find() is actually a pretty unique case in the overall API, and the phpdoc for that method outlines it as a unique case. After all, find() is primarily used for lists and pagination. You have access to everything with your API code, but your code is not the user, and the user is not your code, so be careful not to blur those lines. The API is there to assist you in getting any content you need as quickly and easily as possible, but you are the gatekeeper. It's important to keep this in mind anywhere in the API. In the more than 5 years of this open source project, I'm not aware of any instance where someone has misused a get() method and caused a security problem. So we're talking about a hypothetical scenario of someone making a code error, that to my knowledge has never occurred before, though who knows. We could construct all kinds of scenarios where a developer could misuse the power provided and get themselves in trouble. PW is a tool that when used correctly, provides very strong security and its been my primary focus in this project. But I also don't believe in magic_quotes, safe_mode or trying to save developers from themselves by introducing obstacles. These things might seem to provide more security initially, but actually create a perception that one is no longer responsible for their own security and thus become security problems themselves. I have no qualms about an access controlled "one" function per an earlier post, which could be a useful shortcut for some developers and scenarios. But I think it's important that such a method is completely distinct and also outlined as a unique case, because PW's API context is one where the site/app developer using it carries that power and responsibility. We like to access control the user, not the developer. It's good to see this Security board finally getting some discussion.
  6. What you are describing is a coding error. Any time someone uses function/method calls improperly, especially when user input is involved, they are creating a vulnerability. This has nothing to do with PW. This is the world we live in. I fully support trying to anticipate mistakes a developer might make and saving them from themselves. But a $pages->get() method with a stated purpose of retrieving a specific page unconditionally is not the place for it. I am however open to adding a different method with a different purpose. If we did have it, we could expect a whole lot of things to break when it is enabled. The behavior of get() is fundamental to the application logic of PW and sites/apps built upon it. Because a get_is_like_find option would make the get() method perform as something entirely different from what it is, we could expect much of the API to be broken. The intended consumer of a get() result (and most API calls for that matter) is the developer. The get() method has nothing to do with users or access, and everything to do with retrieving the specific page you asked for. You are operating on the assumption that get() is there for a different purpose, like specifically for output. Yes there may be cases where one may want to retrieve a single page and also have it bundle in some access checks, but that would be a different purpose and a different method.
  7. PW provides access control for the request. Once that request is successful, it's between the developer and the API what they want to do. The API is not jailed and not intended to be. If one is dealing with user input, they must sanitize and validate that input. PW works like PHP does in this respect. That means if you are going to start grabbing pages that are influenced by variables from user input (like in Teppo's scenario), then you have to sanitize that user input and validate the result, regardless of what method you've retrieved the page(s). The purpose of get() is to get a specific page that you asked for. The purpose of find() is to find pages that you don't necessarily know what they will be. With find(), since you don't know exactly what pages you'll be retrieving, filtering that result makes a lot of sense as a default behavior. But with get() you are asking for something specific that you know about ahead of time, not some unknown result as with find(). So it makes little sense to apply the same logic to get(), and it would run counter to the purpose of it. I'm not interested in changing the purpose of get(), which works exactly how it's supposed to and intended. I also don't want for there to be an implication that the API is doing everything for you in terms of access control, when that is not the intention or design of it. Where would it end? Blocking $pages->save() API calls and the like? That is not PW. The API does not run in a jailed environment by design, instead it provides dedicated methods for access control. The API is there to give you great power, and yes we expect the developer to use it responsibly. We expect that when a developer makes an API call, they know what the method is for. The get() and find() are entirely different methods with different purposes that are clearly documented. What I'm gathering from er314 and Teppo is that the concern is people using the get() method without a proper understanding of what it's for, or applying what they know about find() to the get() method, even though they are entirely different animals. I don't believe we should intentionally handicap methods to gear them towards people using them improperly. But if it's a concern or enough demand for it, I'm happy to add another method (like the "one" method I used as an example above) for that alternate purpose. Perhaps it'd be useful in a couple of the scenarios Teppo described above if the developer wants to get a page and check for access in one method call, saving a line of code.
  8. Nice work Diogo! True story, I actually had a dream last night that I went into a big fancy department store to do Christmas shopping and there was a metal plaque on the door that said "Design by Ed". When I went in, everything was super minimal and modern like an Apple store, but much more so. It was like all the products for sale, and all the customers, were treated with the highest amount of respect by the designer. But here's the kicker, it was snowing in the store! (this was last night, well before you posted this) No snow for us here either. Our Christmas day forecast is rainy and 75 degrees (24c). That's too hot to even use the fireplace. Bummer.
  9. When using PW's API, you should always consider your code to be operating as a kind of superuser, just like in PHP. In find() operations where you are retrieving multiple pages that you don't know exactly what they will all be, it's a sensible default to exclude "hidden", "unpublished" and no access pages, unless you request otherwise. This is there as a matter of convenience and pagination, not security. Of course you can override that (when you want to) with the "include=hidden" or "include=unpublished" or "include=all" options. Whereas with get() you are being very specific about what you want–a single specific page. Otherwise why would you get() it? Neither find() or get() are intended as a primary means of access control–we have other methods for that (see below). They are methods setup to operate in the most common use cases and keep your API calls as short and simple as possible. Just because find() filters out hidden, unpublished and no-access pages by default does not mean you should count on it for access control. That's because the pages excluded from a find() don't include runtime access control, just that which is stored in the DB. Further, the behavior of whether or not a no-access page shows up in a find() result (that isn't overridden with an include= directive) is configurable with template access settings. Granted, in most cases find()'s defaults are giving you exactly what you want in terms of excluding pages the user can't view (for your convenience), but that's not the entirely of PW's access control, which supports runtime hooks. PW's job is to provide access control for the request, but not for your own code, that's the developers job. PW intends to give you, the developer, access to everything (like you would with jQuery and the DOM), and you decide whether or not it should be available for output. PW provides you with some handy access control methods to help with this: $page->viewable(); // is the page viewable? true or false $page->viewable('field_name'); // is the field on page viewable? true or false $page->editable(); // is the page editable? true or false $page->editable('field_name'); // is the field on page editable? true or false If you want to have you own get() and find() that operate under different defaults, this is easy to do with hooks in your /site/ready.php file. For instance, this would add a $pages->one("selector"); method that automatically does a viewable() check for you and returns a NullPage() if the page is not viewable: $pages->addHook('one', function($event) { $page = $event->object->get($event->arguments(0)); if($page->id && !$page->viewable()) $page = new NullPage(); $event->return = $page; }); Would there be value in adding a method like the above to our $pages API by default? Maybe. It wouldn't benefit my own API use, because the only time I use $pages->get() is because I want to get something without conditions. But if it would be super handy for others I'm happy to add it. Here's another example, lets say you want a method in $pages, like $pages->all("selector"); that always returns all pages with "include=all" implied: $pages->addHook('all', function($event) { $event->return = $event->object->find($event->arguments[0], array('include' => 'all')); });
  10. Makes sense to me, I'll move that <?php endif; ?> down one line, so that the name/version only appears when logged in.
  11. Testing here with PW 2.7.2 in a new URL field I setup called "href", I used the URLs you mentioned. For my field settings, I have "Allow single/double quotes" set to true, and for "Text formatters" I have "HTML Entity Encoder" selected. I tried with and without the "allow IDN" option, but it doesn't matter here since we're not testing with IDNs. Here are the results I got: https://en.wikipedia.org/wiki/Peter_O'Toole Input to URL field in admin: https://en.wikipedia.org/wiki/Peter_O'Toole API code to output: echo "<a href='$page->href'>test</a>"; Output in rendered page: <a href='https://en.wikipedia.org/wiki/Peter_O'Toole'>test</a> Result: Clicking the link works, taking me to the page on Wikipedia https://upload.wikimedia.org/wikipedia/commons/5/53/Peter_O'Toole_-_1968.jpg Input to URL field in admin: Input to URL field in admin: https://en.wikipedia.org/wiki/Peter_O'Toole API code to output: echo "<img src='$page->href' />"; Output in rendered page: <img src='https://upload.wikimedia.org/wikipedia/commons/5/53/Peter_O'Toole_-_1968.jpg' /> Result: Picture of Peter O'Toole Basically, it's working exactly how it should. My best guess is that it wasn't working for you because you've got some other code in there somewhere HTML entity encoding the URLs, to make up for the bug prior to 2.7.2 where text formatters weren't being properly applied to URL fields. Another possibility, check if your $page possibly has output formatting turned off? It would need to be ON for this all to work. That's interesting, so it looks like the URL is getting URL encoded, converting the apostrophe to a %27. I did try entering that too, but PW converted it back to an apostrophe and then entity encoded it, resulting in the same working output as in my second test above. If this is literally how your URLs are stored in the database, then my best guess is that when you are outputting the value, either the $page's output formatting is off for some reason, or the HTML entity encoder wasn't selected in your "text formatters" details tab, which would prevent a URL with an apostrophe from working, whether it was URL encoded or not. I would suggest upgrading again to 2.7.2 and double check all your settings, especially the one about allowing single/double quotes and the Text Formatters. If the URLs are still not working, then check to make sure in your template file that the page's output formatting is enabled, like this: if($page->of()) { echo "<p>Output formatting IS enabled</p>"; } else { echo "<p>Output formatting IS NOT enabled</p>"; } If it's not enabled, you'll want to track down where you are turning it off, typically with a $page->of(false); or $page->setOutputFormatting(false); Finally, check that you don't have something else entity encoding the URLs in your site. If you've got a line like this, you'll want to remove it: $page->bfd_image_from_url = htmlentities($page->bfd_image_from_url); // avoid this
  12. BFD what's an example of the exact URL as you have it input in the admin page editor, and what's an example of the exact output you get in your source on the front end? For your field settings, what textformatter plugins are being applied (details tab), if different from before? You mentioned the URLs were working before, when actually they really shouldn't have, so I'm wondering if they might be getting double encoded somewhere along the line. If some of your URLs are input encoded and some not it might be that you need to output the value a little differently on your front end to account for that. But I need to know what the exact inputs and outputs are first.
  13. I'm waiting for the coffee to kick in still, so this probably is missing something but I'll throw it out there anyway... text_field*=cat|dog, page_field=1234|4321 Btw, for some of the solutions above you can also leave out the "foo=" part if you want too. You only need to start naming the OR expressions if there are multiple OR expressions that should be evaluated as part of separate groups. But if you need a "bar=" too, then you'd need to name them.
  14. Tom, sorry that field was behind a showIf dependency for the inline edit fields checkboxes, like Adrian mentioned. It's actually only supposed to apply to those checked fields, which is why it didn't appear. I was replying from mobile before and was forgetting this. That particular setting really is only meant to apply to those checked fields, so your $page->edit() wasn't working as a result of a bug, which I have pushed a fix to 3.0.2 just now. You should now be able to use $page->edit() with any field regardless of page. There's no concern about performance hit here. The reason it's recommended off is only as a default setting. For instance, let's say you've got a "summary" field in your page and you are rendering search engine results that include that summary. You probably don't want the summary to be editable in all those search engine results, whereas you do want it to be editable when you are actually on the page. So the setting is just there to reduce confusion since chances are, most of the time you'll only want to have a field editable when you are on the page it is owned by. None of this is meant to apply to options B through D since you are already in control of it all from the API side. I don't think there's much we can do about it, if that image is not part of your field there. However, if it's a concern, you may be able to add something like this to your css file to adjust the cursor: #your-container img.your-image { cursor: pointer; }
  15. Tom, check your PageFrontEdit module settings page to make sure you've got the option enabled to allow editing of pages other than the current. This is a new option added in 3.0.2. previous versions let you edit anything anywhere, whereas this version let's you lock it down a little more, and it comes that way by default. but you can turn it off from the module settings page.
  16. Go to your URL field settings (Setup > Fields > your field) and check the box to "allow single/double quote characters in URLs". That should fix it. We started not allowing those characters in URLs by default because such URLs could be a security problem if someone forgets to enable the "HTML entity encoder" text formatter for their URL field, and happens to output the URL in an href attribute.
  17. I'm not entirely sure I know how to answer all your questions here because it sounds like you may trying to do things with InputfieldFile is may not necessarily be designed for. If that's the case, you may be better off extending it as a new PHP class, or copying its code to new module, rather than trying to achieve these all with hooks. Another thing I'm not sure about is whether you are talking about a front-end or back-end context. If dealing with a front-end context, the ajax uploading will not work. In some cases you may have to disable it on back-end use too (see the commented line below for how to do that). InputfieldFile is really meant to be used in combination with FieldtypeFile and within the page editor. But it is possible to use it outside of that context. Though you will still need to supply it a Page object so it has a known storage location, but the given Page doesn't need to have a files field. Meaning, it can be any Page, even an admin one. Here's an example of how you might use InputfieldFile on it's own: // page the file will be stored with (doesn't have to have a files field) $myPage = $pages->get('/path/to/your/page/'); // optionally set the name of existing file(s) that will be present (or leave it blank) $myFiles = array( $myPage->filesManager->path . 'myfile.txt' ); // file extensions you allow $myExts = 'pdf csv txt'; // -------------------------------------------- // create the form $form = wire('modules')->get('InputfieldForm'); // create the files field $f = wire('modules')->get('InputfieldFile'); $f->name = 'my_files'; $f->label = 'My Files'; $f->extensions = $myExts; $f->overwrite = true; // $f->noAjax = true; // uncomment if necessary $pagefiles = new Pagefiles($myPage); foreach($myFiles as $filename) { $pagefile = new Pagefile($pagefiles, $filename); $pagefiles->add($pagefile); } $f->attr('value', $pagefiles); $form->add($f); // add a submit button $f = wire('modules')->get('InputfieldSubmit'); $f->attr('name', 'submit_form'); $form->add($f); // ------------------------------------------- // process the form if(wire('input')->post('submit_form')) { $form->processInput(wire('input')->post); $pagefiles = $form->get('my_files')->value; foreach($pagefiles as $pagefile) { echo "<p>Your file: $pagefile->url</p>"; } }
  18. @ragnarokkr it sounds like you do have page-permission in your system though? (Access > Permissions > page-publish) If that's the case, the user must have that permission in order to edit a published page. See here for more details: https://processwire.com/api/user-access/permissions/#page-publish
  19. You can use "|" to OR fields, or OR values, but not expressions. Your selector is translating to something you didn't intend. I think the selector you might be attempting is instead this: template=event, enddate>=2015-11-06, enddate|startdate<=2015-11-13 Or you could use OR-groups: template=event, enddate>=2015-11-06, (enddate<=2015-11-13), (startdate<=2015-11-13) Either of the above selectors says: The template must be event. AND The enddate must be greater than or equal to 2015-11-05. ​AND (The enddate must be less than or equal to 2015-11-13 OR the start date must less than or equal to 2015-11-13).
  20. Correct, if you've given them edit access those templates. With page-publish installed, that means the user must have that permission to publish any content to the live site. Editing an already published page enables one to publish live content. So that means they can't edit an already published page. That behavior is correct. On the other hand, they would be able to edit unpublished pages (or create them, if you've allowed that). Correct. That's what you'd want the hook for. Alternatively you could add page-publish permission for that role just to the "language" template from the Access tab of the template editor (see "Additional edit permissions and overrides"). But since you aren't otherwise managing access for the language template, I would suggest just using the hook function as the best solution. In that case, you'd want to modify the hook function to consider the language page-edit permissions: wire()->addHookAfter('Page::editable', function($e) { $page = $event->object; $user = $event->user; if($page->template == 'language' && $user->hasPermission('lang-edit')) { if($user->hasPermission("page-edit-lang-$page->name")) $e->return = true; } });
  21. Aaron, PM me your FormBuilder key and I can reply with a snippet of code that will do it for you.
  22. If you upload a random txt or html file to your web server, can you access it at the other domain? My guess is yes. If so, it sounds like an honest mistake. Perhaps you are on a dedicated IP and the owner of the other domain made a typo when setting up their DNS record. Or perhaps the web host made an error when setting up their VirtualHost directives in Apache. You should be able to correct the problem by adding this to your .htaccess file somewhere after the "RewriteEngine On" line: RewriteCond %{HTTP_HOST} !^www\.yourdomain\.com [NC] RewriteRule ^ http://www.yourdomain.com/ [L,R=301]
  23. Good debugging work. The reason this occurs is because MySQL has a limit on the length of a return value from GROUP_CONCAT, though I think that limit is configurable in the MySQL server settings. The GROUP_CONCAT is only used for multi-value autojoins, so not something you need to be concerned about with large text fields or the like. Though with anything large, it's best not to use autojoin at all since it forces PW to load that data in memory every time the page is loaded. It might mean you could only load a few hundred pages in memory at once rather than a few thousand. Autojoin can be a nice optimization for things like "title" or a short "summary" field, or a blog post with category page references. But you would only want to use autojoin for a field that you know will always be accessed from a given $page, every time you load it, regardless of where you load it. For everything else you should leave it off.
  24. Thanks guys, I've added an additional check for this. Regarding the use of a cropping string like "50%,40%" to the size() method, does this work? It doesn't seem to do anything for me. The capability is one that was added from a PR a long time ago, and one I've not ever had the occasion to use in production. But just testing it now, it doesn't seem to do anything at all other than return a resized image without cropping? The $options['cropping'] = '50%,40%' just resolves to a string of '50%,40%', whereas it looks to me like your intention is to provide an array to the size() method. Wouldn't it be better to do one of the following? // just provide a string $page->image->size(200, 120, '50%,40%'); // provide an array $page->image->size(200, 120, array('cropping' => '50%,40%'));
  25. If page-publish permission is installed, it is required in order to edit already-published pages. I suppose we could override that for Language pages if the user has lang-edit permission, but not sure we should, will have to think about that one more. However, if you want to provide that behavior for your site, you could do so with a hook in your /site/ready.php file: wire()->addHookAfter('Page::editable', function($e) { if($e->object->template == 'language' && $e->user->hasPermission('lang-edit')) $e->return = true; }); I agree, I have committed a slight modification that enable this (now on dev). These changes are minor enough and restricted to one permission (lang-edit) that it doesn't affect anything else, so it's fine.
×
×
  • Create New...