-
Posts
17,307 -
Joined
-
Days Won
1,725
Everything posted by ryan
-
If we're using the skyscrapers example literally, like the demo site, then there is no structural connection between cities and architects since they are in different branches, not part of the same family: /architects/ /cities/ So the only connection of an architect to a city is via a page reference field on a building. That means that somewhere, the process you described has to take place, like this: $architects = new PageArray(); foreach($page->children as $building) { $architects->add($building->architects); } $architects->sort('title'); echo $architects->render(); On the individual architect pages, if you wanted to list the cities they are active in, you could do this: $buildings = $pages->find("architects=$page"); $cities = new PageArray(); foreach($buildings as $building) { $cities->add($building->parent); } $cities->sort('title'); echo $cities->render(); In either case, you don't need to worry about duplicate removal is ProcessWire is already taking care of that for you.
-
ProcessWire doesn't get involved in markup generation by intention. But Soma is right that it does keep $config->styles and $config->scripts for you, if you happen to want to utilize them in generating your <head> markup. As for things to minify and combine scripts, I think they are worthwhile if the selection of files is reasonably static and the browser can cache and use it. But again, ProcessWire doesn't want to step on the developers toes by crossing the line into your output. We don't want to assume that we know what is best for every implementation and developer. ProcessWire may power many web sites but it's also powering mobile phone applications, web services, shell scripts and more. This forum has always been a great place to find out about new tools and resources we can use with ProcessWire too (I need to check out that Minify at Google Code).
-
Cutting long content into URL segments with HTML tag
ryan replied to nikola's topic in General Support
As far as I know, the page breaks in HTML attributes are specific to printers? Not sure though, I've never had to use them. Could just break on an <hr> tag too. -
I haven't configured anything on my computer (OS X Snow Leopard) but did notice that mail() never works, unless I email to an address powered by GMail. I don't need mail() to work locally, but thought it was curious that GMail apparently accepts mail from my computer where nobody else does.
-
Think of ProcessWire's tree like a fractal. There is no difference between multiple trees, and branches within a tree. Both can extend forever in the same way. ProcessWire knows nothing about menus. So what is or isn't a menu is based entirely on what you make it, there are no limits. You can have as many menus, whether structured, or flat (via page references, toggles, etc) as you want. Here are common ways of drawing upon ProcessWire pages for menus, but these are just examples in unlimited possibilities: It's common to relate your first level of pages to your top navigation: $topnav = $pages->get('/')->children; It's also common to relate the children of your first level as secondary navigation (which you might also carry further into the structure for tertiary navigation and more): $subnav = $page->rootParent->children; If you wanted dynamic footer pages, you might create a structure where they will be (whether actual viewable pages or redirects): $footerNav = $pages->get('/tools/footer-nav/')->children; Or you might assign footer links via a multi-page reference field on your homepage template, where you edit your homepage, pluck out the pages you want to appear in your footer (using a PageListSelectMultiple input) and drag to order them. (this would be my preferred option) $footerNav = $pages->get('/')->footer_nav; Or you might just put a checkbox field on every page that says "show in footer", and check the box for the pages you want in the footer. $footerNav = $pages->find("footer_nav=1"); Or you might use a repeater to create a more literal menu with title (anchor text) and the URL it should go to. This is just for starters. Pull away from the rigid thinking of other CMSs and you'll find anything is possible.
-
Cutting long content into URL segments with HTML tag
ryan replied to nikola's topic in General Support
Here's another route you could take (written in browser, just an idea, not tested): $n = $input->pageNum-1; $data = explode('<h4>', $page->body); if(!isset($data[$n])) $n = 0; $body = $data[$n]; if($n) $body = "<h4>$body<p><a href='./page$n'>Previous Page</a></p>"; if(isset($data[++$n])) $body .= "<p><a href='./page$n'>Next Page</a></p>"; echo $body; I think it's probably better to split on something you define, like "---break---" rather than an <h4> tag, but you should be able to split on just about anything using explode() or preg_split(). -
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.
-
It was a broken redirect. I've fixed the link-- thanks.
-
That is strange, sounds like the MySQL version might play into it somehow. Well, I hope to have this one figured out soon.
-
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.
-
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
-
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.
- 61 replies
-
- 14
-
-
wire("config")->urls->admin in autoload module init() not available?
ryan replied to Soma's topic in General Support
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. -
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.
-
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?
-
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.
-
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.
-
Good point, thanks for finding that. You are right that they should use $video->filename rather than $video->setFilename()
-
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.
-
wire("config")->urls->admin in autoload module init() not available?
ryan replied to Soma's topic in General Support
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. -
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.
-
Testing for the first element in a wirearray
ryan replied to thetuningspoon's topic in General Support
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. -
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.
-
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.