Jump to content

ryan

Administrators
  • Posts

    17,231
  • Joined

  • Days Won

    1,697

Everything posted by ryan

  1. I think these sound like good ideas. I was initially thinking the fields permission would work very similar to how it does in the Template editor (for consistency). Something like this: Setup>Fields>[field] editor would have an 'Access' tab or fieldset. It would say "Do you want to manage edit access for this field?". If No, it doesn't have anything else. If Yes, then you get a list of roles where you can check the box for the roles you want it to be editable for. When you want to do grouped permissions, you'd use a role, as their purpose is to group permissions for users. (Roles are a many-to-one relationship with users). To reuse your example, you'd name the role 'edit-private-fields' if you wanted to isolate permissions for a group of fields. I was also thinking the access could be a component of the field-template context modal dialog, so that you could grant a role access to edit the 'body' field in one template, and not in another. Though that may be going farther than we need to.
  2. ryan

    Forum Bug

    Thanks for pointing that out. What's really strange is that it only seems to munge the last one: Hopefully the next IP.Board update will fix this. I'll check with Pete on if we can submit a bug report to them.
  3. It was a broken redirect. I've fixed the link-- thanks.
  4. That is strange, sounds like the MySQL version might play into it somehow. Well, I hope to have this one figured out soon.
  5. If you've got a lot of URLs to deal with, you don't have to create separate redirects for each of them in your .htaccess if you don't want to. You could automate it. Setup a rewrite rule for "index.php?/" and redirect all of them to another directory where the old blog is, your own PHP script, or into ProcessWire (where you could let the Redirects module handle them). Let me know if I can be of assistance with the RewriteRule.
  6. Thanks, this is what I was looking for. This answers my concern. @internal sounds like what we need! I will put this on my to-do list. Thanks, Ryan
  7. One of the ways you can show support for ProcessWire is to help get the word out by including a small "Powered by ProcessWire CMS" tagline (ideally linking to processwire.com) in the footer of sites that you develop. This is a big help to the ProcessWire project. But I know there are many cases where it just doesn't work to do that because the client thinks of it as gratuitous. I think it's important to communicate to your client that it's not gratuitous at all. It is doing the right thing by showing appreciation and support for a software that is running their site at no cost. Even so, it's not always as simple as that, and I completely understand. We have no requirement or expectation that sites developed in ProcessWire do this. We just encourage and appreciate it when you can. Let your client decide One thing I've been doing lately is to put the control into my clients hands. They really appreciate that I've given them control over it… more so than if I'd left out mention of ProcessWire completely. It also makes them feel good as they are the one showing support, not just their site developer. Here's how to do it in 1 minute: 1. Create a new "checkbox" field in Setup > Fields called "toggle_powered" (or whatever you want to call it), and enter the following for label and description: 2. Add the "toggle_powered" field to your homepage template. 3. Edit the homepage and check the box (if possible in your situation). 4. Edit the template file or include file that contains the site footer and paste in the following: <?php if($pages->get('/')->toggle_powered): ?> <p> <a id='processwire' target='_blank' href='http://processwire.com'>Powered by ProcessWire Open Source CMS/CMF</a> </p> <?php endif; ?> The code above is an example, so adjust the markup, size, wording and placement to suit the site.
  8. I've made some updates to have $config->urls->admin ready before init() is called. I can't think of any reason not to have it available, especially since (as you mentioned) the $pages API var is accessible (even if no pages have been loaded yet). I'll commit to the source here within the next day or so.
  9. Great! Thanks for testing it out and reporting back. I'm going to give it a couple more days here to test before I move it into the public source, but so far so good. Likewise, please let me know if you run into any concerns. Thanks, we are lucky to have you here! Your do.php repeater-populate is actually what prompted me to write that tweet. I ran out of memory before it could create all the pages. Then I realized I had debug mode on and it made sense why it was running out of memory. Now I'm debating putting a limit on the number of queries it'll save in memory for debugging purposes, to prevent this problem.
  10. Where are your sort settings defined? On the parent page(s) or with the template? I think we do have a bug when sort settings are defined with the template. I noticed when I imported a profile the other day, the sort settings in the template were still there, but somehow not having an effect. Somehow the process of exporting/importing causes template-based sort settings to get lost, but I haven't figured out why yet. So this is something I'm working on and hope to have a fix for soon. But I'm not positive if this is the same issue you are running into or not?
  11. Pete is right that if you had fields you didn't want to be editable to your client, then you probably wouldn't keep them as fields at all. Instead, you'd handle such things from your template file. Though I think there is value in having field-level access, and so Soma is also right that it'll be coming to ProcessWire soon. In fact, it's a real simple fit with the existing system. I suppose the only reason we don't have it yet is just that it hasn't really come up as a common need, and we tend to be pretty cautious about adding too much (always balancing simplicity with capability). That's a great module example that Soma put together there too, seems like a good way to solve it at least until the capability is in the core.
  12. Is that a valid URL? I'm not sure a variable name in a URL can have a slash in it, but I could be wrong. However, I don't think that the Redirects module will get the opportunity to ever see that URL. /index.php is a file that already exists (ProcessWire's /index.php). So requesting it directly sidesteps the whole Apache rewrite engine which maps non-existing file requests to ProcessWire. I think that the only way you could setup a redirect for a URL like this is by using Apache Rewrite rules in your .htaccess file.
  13. Good point, thanks for finding that. You are right that they should use $video->filename rather than $video->setFilename()
  14. I've actually got a lot of updates coming for the PageLinkAbstractor module that I was working on in the last couple weeks. Beyond what it was doing before, it now also keeps track of links to deleted pages and deleted images or files. When it detects a broken link, it logs it and optionally emails you about it. It also keeps an alert at the top of the admin screen telling you about it with a link where you can go review it. Once you fix the broken links, it detects they've been fixed and clears the log of fixed errors. Here's a couple of scenarios that it covers: 1. Page /about/ links to /products/widget/ in the bodycopy (TinyMCE) field. The /products/widget/ page gets moved to the trash or deleted. Since /products/widget/ is gone (or at least, not accessible to anyone) the link is broken. 2. Page /about/ has a photo embedded in the bodycopy (TinyMCE) from the /events/bbq-fest/ page. That photo gets deleted from the /events/bbq-fest/ page, which causes a broken image to appear on the /about/ page. Both of those errors would be automatically detected, logged, and emailed to you, so that you can fix them. It's keeping track of this stuff so that internal broken links don't ever get to persist. With these additions, the module is also being renamed to LinkMonitor, LinkNotifier or LinkPolice (still deciding), as I think PageLinkAbstractor is just a bit too technical sounding.
  15. The reason for this is that init() is called before any pages are loaded. This is so that you have the opportunity to hook into things like $pages->find and expect that your hook won't miss any pages. The admin URL is dependent upon what name you've given to your admin root page. I think it's actually the only $config->urls property that wouldn't be ready in init(). Given that, I could probably come up with a way for it to be populated before init() (like a direct SQL query)--I think I will do this. However, for now, I'm thinking that you probably want to use ready() rather than init(), since the requested page and admin page have been loaded by that time. When you use ready(), the API is fully ready without exception. But for some autoload modules, the events they wanted to hook into have already occurred, or their hook was attached after $page was already loaded by $pages->find(), etc. I think it's best to start with ready(), and if it's not doing what you need, switch to init(). Also, this may be obvious after the above, but if your module needs to access any kind of data or function from the loaded $page, then ready() is the only way to go.
  16. If you keep the structure above, then you would consider the parent ($page->parent) to be the 'primary sub-category' and the grandparent ($page->parent->parent) to be the 'primary category'. In that case, you'd probably want to consider your categories page reference to be 'secondary categories'. I think this structure works fine if you consider the primary category/subcategory to be mostly static once published and more important than the secondary ones.
  17. I don't think it matters much here. The object===object is probably about the same thing as an id==id integer comparison, when it gets down into the low level code that it's written in. That's because object===object is comparing that two objects are the same instance, which translates to: do they occupy the same place in memory? (1234567==1234567) But there is a good reason to use the id==id over object===object in ProcessWire: $about1 = $pages->get('/about/'); $about2 = $pages->get('/about/'); echo $about1 === $about2 ? "Same Instance " : "Different Instance "; This should output "Same Instance". But then try this: $about1 = $pages->get('/about/'); $about1->set('title', 'About Test')->save(); // can be any page that is changed then saved $about2 = $pages->get('/about/'); echo $about1 === $about2 ? "Same Instance " : "Different Instance "; This should output "Difference Instance". Why? The entire memory cache is cleared when you save a page where one or more fields changed, or if you delete a page. It doesn't matter if it was $about1, or some other page, the memory cache is wiped. Since the code above kept it's own copy of the /about/ page before it was saved, there are now two copies of /about/ in memory: your copy and ProcessWire's copy. For this reason, you may prefer to compare the 'id' property if the code you are working with is manipulating any pages or interacting with modules that do. But for most front-end situations, it doesn't matter.
  18. That's not far off from what I'd been working on with versioning, which was to store page versions in flat JSON encoded strings in a DB table (though could just as easily be stored as text files). Any ProcessWire page can be reduced to a PHP array of basic types (strings, integers, floats, arrays), which means it can be reduced to a JSON string really easily. This lends itself well to version storage, except for assets where only references are encoded (like files and page references). But I have to admit that the more I think about Antti's proposal to store this at the field-level, the more Iike it. I would create another interface that could optionally be added to a Fieldtype's implements section in the class definition (FieldtypeWithVersions or something like that), and give the individual fieldtypes responsibility for if, how and where they store their versions. That would enable us to get up and running with versions quickly (for text fields at least). It would also seem to provide a nicer level of control to the user, as you don't have to keep track of what is changing in multiple fields at once. And, it would let you to enable versions on fields that you want it, omitting on field where you don't, making things more efficient. Lastly, it would leave the page-structure out of versions completely (where the page lives in the tree), which struck me as a potentially dangerous problem with versions. It could work something roughly like this: You hover over the revisions label and get a summary of recent revisions. You click on one, it could pop up a modal showing you the revision (maybe with a compare tool) and a button to revert to it or cancel. It just seems like a really easy-to-use solution for the user.
  19. Niklas, Thanks this has been hugely helpful. Your test scripts and database really saved me a lot of time and helped me to get a handle on the issue quickly. Actually your do.php was a pretty outstanding example of using the API with a shell script for automation. I think that I've now got it fixed, though need more eyes on it to be sure. But here are the page creation times with the fixes implemented: Setup 1 do.php test BEFORE populate .. /countries/test7807394638/ saved in 0.008997 seconds .. /countries/test1201072181/ saved in 0.010174 seconds .. /countries/test4845661424/ saved in 0.010104 seconds do.php test AFTER populate .. /countries/test13939a4379/ saved in 0.010184 seconds .. /countries/test8963721816/ saved in 0.010156 seconds .. /countries/test8775327378/ saved in 0.009929 seconds Setup 2 do.php test-repeater BEFORE repeater-populate .. /countries-repeater/test1328343619/ saved in 0.027560 seconds .. /countries-repeater/test1178892434/ saved in 0.029362 seconds .. /countries-repeater/test1854160826/ saved in 0.029181 seconds do.php test-repeater AFTER repeater-populate .. /countries-repeater/test2117723102/ saved in 0.031516 seconds .. /countries-repeater/test1829907000/ saved in 0.031705 seconds .. /countries-repeater/test1610663476/ saved in 0.031419 seconds I didn't take comparisons before, as I went in straight to work once I confirmed there was an issue. However, you had mentioned it would take measurable seconds before, so I'm thinking this is a major improvement? I'm also not surprised by the very slight increase in time after repeater-populate, given that repeater-populate added more than 30,000 pages to the system. One thing is for sure, doing 'do.php populate' is exponentially faster than before. Page names have random numbers in them to prevent ProcessWire's from consuming time in a loop trying to make the page name unique (skewing the numbers). So rather than making each page name 'test', I made it 'test'.mt_rand(); The repeater test pages take at least 2x as long to create as the non repeater test pages because the repeater tests are populating a repeater item on every page creation, which the other test isn't doing. Basically it's creating 2x the number of pages on each call with the repeater test. The Fix ProcessWirePagesParentsFix.zip Can you try replacing your /wire/core/Pages.php and /wire/core/PageFinder.php with the files attached? Please let me know your results. These fix it for me, but this is something with broad scope and it definitely needs more eyes on it. I honestly don't know what I was thinking when I wrote the piece of code you originally quoted from Pages.php. It may have worked in terms of producing accurate results, but had a real legibility problem, and of course the bottleneck problem. Part of the issue was that pages_parents didn't originally come with ProcessWire, so it was added in later. We had to deal with situations where people might not yet have a pages_parents index, which led to some code that doesn't make as much sense in today's context. I went ahead and rewrote that part, among some other details. Test Package Because some of this logic had to be rewritten, I'm naturally concerned about breaking stuff. So I put together a quick test package that runs through various tests to confirm that it's returning all the results it should. This test script assumes the setup you've described with the /countries/ and cities below them. It tries moving around a few pages and executing find()s and confirming it's returning the right pages. I'm including here just in case you or anyone else wants to help test or can think of other tests we should perform with it. This should be placed in /site/ like the do.php script. #!/usr/bin/php <?php include("../index.php"); /** * Perform a find test calling $parent->find($selector) * * If the quantity found is different from the provided $expectedCount * then the test fails. * * @param Page|string $parent Parent page to call find() from, either Page or path (string) to it. * @param string $selector Selector string to use in the find() * @param int $expectedCount Quantity required to PASS the test. * */ function findTest($parent, $selector, $expectedCount) { $expectedCount = (int) $expectedCount; if(is_string($parent)) $parent = wire('pages')->get($parent); echo "\n\t+ pages.get({$parent->url}).find($selector); "; $matches = $parent->find($selector); $numMatches = (int) count($matches); if($numMatches == $expectedCount) { echo "PASS ($expectedCount)\n"; } else { echo "*****FAILED***** ($numMatches != $expectedCount)\n"; } } /** * Move $page to $parent * * @param Page $page * @param Page|string $parent May be parent page object or path to it (string) * */ function movePage($page, $parent) { if(!is_object($parent)) $parent = wire('pages')->get($parent); echo "\n\n----------------------------------------------------------"; echo "\nMoving {$page->path} to {$parent->path}{$page->name}/\n"; $page->parent = $parent; $page->save(); } /** * Start the tests * */ $page = wire('pages')->get('name=country-97'); $selector = "name={$page->name}, has_parent!=/countries-repeater/"; movePage($page, '/'); findTest('/', $selector, 1); findTest('/countries/', $selector, 0); movePage($page, '/countries/'); findTest('/', $selector, 1); findTest('/countries/', $selector, 1); movePage($page, '/countries/country-99/'); findTest('/', $selector, 1); findTest('/countries/', $selector, 1); findTest('/countries/country-99/', $selector, 1); movePage($page, '/countries/country-99/city-1/'); findTest('/', $selector, 1); findTest('/countries/', $selector, 1); findTest('/countries/country-99/', $selector, 1); findTest('/countries/country-99/city-1/', $selector, 1); findTest('/countries/country-99/city-2/', $selector, 0); findTest('/countries/country-96/', $selector, 0); findTest('/countries/country-95/city-1/', $selector, 0); movePage($page, '/countries/'); findTest('/', $selector, 1); findTest('/countries/', $selector, 1); findTest('/countries/country-99/', $selector, 0); findTest('/countries/country-99/city-1/', $selector, 0); findTest('/countries/country-99/city-2/', $selector, 0); findTest('/countries/country-96/', $selector, 0); findTest('/countries/country-95/city-1/', $selector, 0); $countries = wire('pages')->get('name=countries'); movePage($countries, '/about/'); findTest('/countries/', $selector, 0); findTest('/about/', $selector, 1); findTest('/about/countries/country-99/', $selector, 0); findTest('/about/countries/', $selector, 1); movePage($countries, '/'); echo "\n"; Thanks again for your help in finding and debugging this issue.
  20. Okay I think I tracked down what the issue was. Looks like ProcessPageSearch just didn't support include=hidden directives in the AJAX query. I've updated it so that it does, if you want to try the latest. Let me know if this works from that end too?
  21. I think it might be a little better to use a counter just because that is native PHP with no function call (i.e. always faster/more efficient). And you should be able to get that counter right out of the foreach(). foreach($newsChildrenArray as $cnt => $newsChildPage) { $newsImg = $newsChildPage->images->first(); if($cnt == 0 && $newsImg) { $newsImg = $newsImg->size(157,151); // big } else if($newsImg) { $newsImg = $newsImg->size(77,71); // small } // the rest… }
  22. I'm not sure that I understand the question? But you did say that you want a categories Page reference on the article template. That makes me think that you probably don't want the articles structured under the subcategories and instead maybe they should all be under /articles/ ? Then you'd select categories/subcategories from the Page reference field using something like PageListSelectMultiple or PageAutocomplete. That would at least solve the issue of articles that need multiple categories/subcategories (though not sure if this is something you needed or not?)
  23. If I understand correctly, you want to change the rendering context at runtime if certain conditions are met. You have a few options. 1. You could set your $video to have a different template file before calling $video->render(); /site/templates/home.php $video->template->filename = 'teaser.php'; echo $video->render(); 2. Or you could throw up a flag that your video.php template file knows to look for: /site/templates/home.php $input->whitelist('teaser', 1); echo $video->render(); …and in your video.php: /site/templates/video.php if($input->whitelist('teaser')) { include('teaser.php'); } else { // render full video page } The advantage here is that all your video rendering code is together in one file. 3. Or you could include your teaser.php from home.php manually: /site/templates/home.php $_page = $page; $page = $video; // substitute $page before including teaser.php include('./teaser.php'); $page = $_page; // return $page to it's original state 4. Or you could isolate the teaser rendering to a function (perhaps included from another file): /site/templates/functions.inc function renderTeaser($page) { return "<p><a href='{$page->url}'>{$page->title</a><br />{$page->summary}</p>"; } /site/templates/home.php include('./functions.inc'); echo renderTeaser($video); This last option is the one I'd be most likely to take as I think it's probably one of the lowest overhead, it's easily reusable across different templates, and requires very little consideration wherever you use it. Here's another way you can do the same thing, by actually adding a renderTeaser() function to the Page class at runtime: /site/templates/functions.inc function renderTeaser(HookEvent $event) { $page = $event->object; $event->return = "<p><a href='{$page->url}'>{$page->title</a><br />{$page->summary}</p>"; } $this->addHook('Page::renderTeaser', null, 'renderTeaser'); And now you can do this: echo $video->renderTeaser(); Btw that line that has $this->addHook(); has null as the 2nd argument because the function is outside of a class. Some people create auto-loading modules to contain rendering functions, in which case that 2nd argument is the module's object instance.
  24. ProcessWire's admin is designed for trusted users. I would avoid a setup that allows any user on the internet to register and then gain access to your admin. I'm not aware of any CMS that I would consider for this type of permission escalation for untrusted users (though possibly Drupal). Instead, I would take a framework approach, whether with ProcessWire or another framework. When users are untrusted, you really want to jail them, limit everything, and lock it down. Kind of the opposite of what you want to do for trusted users. If they are going to be uploading files to the system, you want to limit the size and quantity, so one user can't go fill up the hard drive or launch DDOS attacks by uploading massive files and getting them stuck in GD resizes. Anything uploaded should be stuck in a non-web accessible quarantine area that is only delivered to the authenticated user via a passthru script with a forced mime type. You kind of have to expect the worst when you open up any kind of editing or upload tool to untrusted users. But this is relatively easy to do when you are using a framework (like ProcessWire) and coding for a specific purpose with clear limits.
  25. It all sounds good to me. I'm not against it. Though I don't use PHPDoc or any editor that supports it, so the comments you see in the code have always been for my reference (though I think the style is similar to PHPDoc). I'll be honest, I just can't think in code when I'm surrounded by a IDE that has lots to look at and is trying to do stuff for me. So I stick to VIM, full screen. I haven't figured out how to bring in a pull request without files getting deleted or tabs/linebreaks getting screwed up, so I'd prefer to just make any changes here manually. It sounds like what you are wanting is a @property tag for the class properties in Page.php? I'm happy to do this, but have a concern: most of these properties are not API accessible. You don't want these showing up in your IDE unless you are modifying or extending the Page class (which nobody has done yet as far as I know). Likewise, I think we need to be careful with phpdoc for any PW class properties if the intention is to have them show in your IDE. The reason for this is that the vast majority of API accessible properties are handled through PHP magic methods and not actually properties of the classes themselves. If you use the properties of the classes, you'd be getting an very incorrect view of the API. On the other hand, PHPDoc should be totally fine for public functions. Though there are many public functions that are not part of the public API (meaning, PW classes use them internally, but you shouldn't), so relying on an IDE for this stuff would again give an incorrect view of the API that could get one into trouble. I think it's better to use the cheatsheet and PW API documentation for this stuff. Though maybe there are also some phpdoc tags that can help clarify if a public function is for internal (core use).
×
×
  • Create New...