-
Posts
16,784 -
Joined
-
Last visited
-
Days Won
1,537
Everything posted by ryan
-
There isn't at present. Mainly because I couldn't think of a situation where you would be echoing someone's email on a public site. But if that's something you need to do, I would suggest iterating the comments field directly (as in the example above), as this is very simple to do and gives you access to every bit of comment data.
-
I've removed the home link from the admin breadcrumbs in the latest commit. https://github.com/ryancramerdesign/ProcessWire/commit/f50cd4ad76cbfc8693ce25b8fec026278d02af46
-
Snippets / chunks / blocks / template inside templates
ryan replied to piranha's topic in Wishlist & Roadmap
I understand that other CMSs have places where you might edit code in the CMS, and I also understand there is a convenience factor in many cases. But the main reason why I want to support snippets is actually to encourage sharing of code snippets. It's feasible that we can have a library of snippets that people can just paste in, and this is accessible to anyone. This is the area where I think the snippets may be worth the compromise of their downside. But I will do as you suggested and document why people might want to avoid using snippets for custom site-specific code, or plugging things into live sites. Thanks, Ryan -
Sure--let me know how it works for you and if you have any thoughts on expanding it further. Thanks, Ryan
-
$pages->get from a Custom Process module
ryan replied to peterb's topic in Module/Plugin Development
Hi Peter, I'm glad to see you extended a Process module and used it in the Admin. I think you are the first to do this. To iterate the fields in a template, you would do: <?php foreach($template->fields as $field) { echo "<li>{$field->name} is a field of type {$field->type}</li>"; } In the top of your .module file, I recommend adding one or more lines indicating that you are the author and any related contact info (website, etc.). Likewise, in your getModuleInfo() function, I recommend changing it to: public static function getModuleInfo() { return array( 'title' => 'Export XML for Flash', 'version' => 100, 'summary' => 'A module to export XML for flash by PeterB', ); } There is still a lot of stuff left in your module from the one you adapted it from, so you can remove lots of unnecessary code, which I'll cover below. Remove your init() function, you don't need it or anything that's in it. You don't need the code that is in your execute() function. You can change your execute() function to be just: public function ___execute() { return "<p><a href='./export'>Export XML for Flash</a></p>"; } In your executeExport function: It's not safe to assume that the assets dir is "../assets/". Instead, you should set your filename like this: $myFile = $this->config->paths->assets . "website.xml"; In general purpose situations, it's probably not safe to find your homepage with a selector like $pages->get("title=Home"). That's because it's feasible that other pages could have that title. I would instead suggest using the homepage's path, like $pages->get("/"); There are also other instances where you are retrieving a page with it's title field. If that's something that you need to do, I would suggest instead using the page's path/url or ID, as those are the only two things guaranteed to be unique about any given page. For pages with the same parent, you can also assume that it's "name" will be unique among it's siblings. On this line (119): $subpage = $this->pages->get("title=$section->title"); $stories = $subpage->children; Is that necessary? On briefly looking at the code, it looks to me like this would achieve the same thing: (?) $stories = $section->children; In your code, you are referring to a field called "page_type". Since this is essentially what a template is, you may want to see if using a Template for a page type would suite your needs (it's possible it may not). At the end of your executeExport function, you have the following: $out = $this->showListFilters || $this->config->advanced ? $this->renderListFilters() : ''; $table = $this->modules->get("MarkupAdminDataTable"); $table->action(array('Export XML for Flash' => './export')); $table->message("Exported!"); return $out . $table->render(); I believe you can delete all of that and replace it with: $this->message("Exported!"); return $this->execute(); Also, you can delete everything after your executeExport() function, as they are functions specific to the Fields module, and ConfigurableModule interface, and they aren't used here. Lastly, go back to the beginning of the file and change your class definition to be just: class ProcessExportFlashXml extends Process { You want to make sure you remove the "implements ConfigurableModule" since it will throw an error if you don't (since I told you to remove everything below your executeExport function. -
$pages->get from a Custom Process module
ryan replied to peterb's topic in Module/Plugin Development
The reason it's not working is because a page's output formatting is usually only turned on for template use, and off elsewhere. Technically, all image fields can contain multiple images, but ProcessWire automatically reduces them for templates when you want them to be just one (likewise for Page references). It doesn't do this for the rest of the API just so that the same bits of code can always be used regardless of the image settings. Whereas in your templates, it lets you choose. Here's a couple of options to make it work in your instance: $url = $section->imagefield->first()->url; Or $section->setOutputFormatting(true); $url = $section->imagefield->url; $section->setOutputFormatting(false); -
You can definitely do everything that you can in those other CMSs. But another thing to note is that both Drupal and Wordpress are primarily bucket-based CMSs, whereas ProcessWire is a page hierarchy-based CMS (as are MODx and SilverStripe, as far as I know). In a bucket-based CMS, you have no hierarchy other than an individual bucket. So custom taxonomies/categories/tags become the primary means by which you establish hierarchy (beyond a content type). Whereas in a page-hierarchy based CMS, you already have a hierarchy present which directly relates to URL structure. So additional categorization is less often used than it would be on a bucket-based CMS. I'm not suggesting that one approach is necessarily better than the other for most sites, except that I prefer a page hierarchy-based approach that can be used like a bucket when the need calls for it (which is ProcessWire's approach). I also feel that the page hierarchy approach is demonstrate-ably better at the large scale. I have a feeling we have the same preferences in this area, based on the other CMSs I know you like working with.
-
Snippets / chunks / blocks / template inside templates
ryan replied to piranha's topic in Wishlist & Roadmap
Snippets will be supported with a module, and you'll be able to define whether you want it to process PHP or not. I'd rather not have a bunch of confusing terminology like chunks, nuggets, snippets and such, and rather just have one term for them all: snippets. I understand the desire for snippets, though admittedly feel it crosses the line away from best practices, splitting your markup into different places. Whereas include files are clean, simple, are kept in one place, and don't need to be eval'd when they contain PHP. I also think it's a bad practice for people to be editing any sort of code in the CMS itself. The CMS is not the right environment for it (your markup and/or code editor is), nor is a live site the place to be messing with the code (this should be a dev/staging/offline task). But the intention with ProcessWire is to adapt to a developer's needs rather than enforce my opinions on best practices, so a snippets capability is something you can count on in the future. Thanks, Ryan -
Piranha, I'm not suggesting that you should create lots and lots of templates. What I am suggesting is that your templates are PHP files, not static files, so you are not bound to the limits of static files. On my sites, I typically have only one markup template, and use the other templates as flow control in preparing whatever is needed for the main template. But you should use templates in whatever way works best for you. The need you mentioned with using a tag within the content of an existing field is one I can relate to. The need does come up occasionally. Though I don't like my clients having to use tags, so I usually design in a way that maintains separation between their content and that which is generated at render time. But that may not be right for you, so I'm going to take Adam's suggestion and turn it into a module. This took all of 10 minutes to create, but it should give you a good starting point to build from. Though it is fully functional as-is. Place the attached module file in /site/modules/TagscriptSimple.module. Then go to Admin > Modules and click the "check for new modules" button at the bottom. Then locate the TagscriptSimple module, and click "install" on that screen. Now in any of your page fields (or templates) you can type a {{tag}} which can either be a page property or a function name. For example {{title}} would echo the current page's title. Or {{myfunction}} would call a function named tagscript_myfunction, and it would pass it the current page. It would then replace the {{myfunction}} tag with whatever output was returned by the function. I included a couple of examples in the module code if you want to try them, so try {{hello}} as well as {{children}} to see the examples run. Here is the module code. I've also attached it in a ZIP file ready do download, so that you don't need to copy/paste. This is fairly basic. Anyone please feel free to take this further, and please post the update. An example of how you want to take it further would be to translate some tags to config URLs, or to include the ability for tags to pass arguments. Though if nobody takes this up, I'll do it here. <?php /** * Simple tagscript parser to serve as an example or starting point. * * Looks for tags surrounded by double curly brackets, like {{example}}. * When it finds one, it first checks if there is a funcing matching the * name: tagscript_example(). If it finds a matching function, then it * calls it, passing it the current $page object (in case it needs it). * The function should return whatever should replace the tag. * * If the matching tag doesn't resolve to a function, then it next checks * if the tag lines up with a field from the current page. If so, the * tag is replaced with the field's value from the page. * */ class TagscriptSimple extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Tagscript Simple', 'summary' => 'Translates {{tags}} to PHP function calls.', 'version' => 100, 'permanent' => false, 'autoload' => true, 'singular' => true, ); } public function init() { $this->addHookAfter('Page::render', $this, 'render'); } public function render(HookEvent $event) { $page = $event->object; $out = $event->return; // don't process tags in the admin if($page->template == 'admin') return; // quickly determine if there are no parsable tags here if(strpos($out, '{{') === false) return; // match all tags enclosed by double brackets, like {{tag}} if(!preg_match_all('/\{\{([_a-z0-9]+)\}\}/i', $out, $matches)) return; // iterate through all found tags foreach($matches[1] as $key => $value) { // first determine if there is a function with the same name as the tag // except preceded by "tagscript_", i.e. "tagscript_functionName" $functionName = "tagscript_$value"; // if the function exists, then call it, passing it the $page object if(function_exists($functionName)) { $result = call_user_func_array($functionName, array($page)); if($result !== false) $out = str_replace($matches[0][$key], $result, $out); // if function doesn't exist, check if it's a page property } else if(($result = $page->get($value)) !== null) { $out = str_replace($matches[0][$key], $result, $out); // if none of the above, then leave it alone } else { // do nothing, or expand from here... } } $event->return = $out; } } Here are a couple of example functions I also put in to test. These are just regular functions (not class functions). <?php /** * Function to test the tagscript parser * */ function tagscript_hello($page) { return "<p>Hello World. The current page path is {$page->path}</p>"; } /** * Render a list of the page's children * */ function tagscript_children($page) { if(!$page->numChildren) return ''; $out = "<ul class='children'>"; foreach($page->children() as $child) { $out .= "<li><a href='{$child->url}'>{$child->title}</a></li>"; } $out .= "</ul>"; return $out; } TagscriptSimple-module.zip
-
Here's how I setup 301 redirects in my .htaccess files. These redirect directives should go probably above ProcessWire's, but after the "RewriteEngine On" directive. Here's a 301 redirect from one directory to another (/old/ to /new/): RewriteRule ^old/?$ /new/ [R=permanent,L] Here's rewriting /old.html to /new/dir/: RewriteRule ^old.html$ /new/dir/ [R=permanent,L]
-
Adam, Have you set $debug = true in /site/config.php? Though the error message you reported is fairly specific, so I'm not sure it's going to get any better than that. What you may want to do is try putting a test file on the server in the web root and then loading it from your browser. I'm guessing it'll report the same problem, but running this test at least allows us to rule out some things. Let me know what it does. /test.php <pre> <?php error_reporting(E_ALL); ini_set("display_errors", 1); $target = "./site/assets/files/1/test.php"; copy("./test.php", $target); if(is_file($target)) { echo "\nfile was copied"; unlink($target); } else { echo "\nfile was not copied -- what error message did you get?"; }
-
If anyone wants to take the lead on building this functionality, I'll be glad to collaborate with you on making it a ProcessWire module. Based on what Deandre212 posted, I am thinking it might involve a Page::render hook that looks for certain tags and adds attributes to them. Or, [possibly better] it might involve creating a new RDFa runtime TextFormatter module that you can assign to text fields like text, textarea and textarea-tinymce. For an example of an existing runtime text formatter, see any of the modules in here: /wire/modules/Textformatter/. While I know how the ProcessWire side would work, I don't really know how the RDFa side would work...
-
Hi Ryan, Good to see you in the forums! This will make it into the main API documentation. I've been posting stuff here first, and then transitioning it to the main API docs after making sure I've accounted for everyone's questions, and haven't made any typos. Ryan
-
Hi Adam, Didn't we find before that if one of the parent dirs lacked "x" permission for apache, that this could happen? I don't recall exactly, but it seems like there was something like that happening on one other person's hosting account. Do you have shell access? i.e. could you chmod -R og+rwx in /site/assets/ to test? Otherwise, I think you may be right about server quota or possibly something in the PHP settings. Have you looked at all file upload related settings in phpinfo? Ryan
-
Snippets / chunks / blocks / template inside templates
ryan replied to piranha's topic in Wishlist & Roadmap
Includes are an important part of templates, and they are about as simple as it gets. When you do the include, I suggest you put a "./" in front of the filename, i.e. include("./header.inc"); Yes it'll probably work without the "./", but it will force PHP to search all of it's include paths. Whereas if you put a "./" in front of it, it will only check in your /site/templates/ dir (which is more likely what you want). -
Adam is right that you really are better off doing this in templates. But if you still have a specific need to do this, here's how you can. I would suggest setting up a field specifically for PHP code, and call it "phpcode" or something like that. Make it a standard textarea field, and disable any output formatting in the field's settings. In your template code, you will want to eval() the field's value, which will evaluate it as PHP. This is how other CMSs do it. It's not particularly safe or efficient, so be careful where you use it. if($page->phpcode) eval($page->phpcode); The reason I suggest using a field just for PHP code is that in a regular textarea field, you would want to have output formatting like entity encoding. You don't want that in a field that supports PHP, because it would break the PHP. So mixing bodycopy and PHP in the same field would not be a best practice (though you could certainly do it).
-
Adam is right, and I'll expand on what he said. Things like RDFa and microformats are part of your markup. If you are wanting to support these, ProcessWire supports it. ProcessWire doesn't generate markup (except for a few helper plugins). Instead, it manages your content and lets you decide how you want to mark it up and output it. That is to say, it's already RDFa and microformats ready. However, you still have to decide when and where you want to mark up your content this way. While you could use TinyMCE, I think I'd want to do so with the support of a TinyMCE plugin. I'd want something foolproof that doesn't get me involved in the technical details of RDFa every time I add a page to my site. If that is also your need, I would structure your data semantically using fields in ProcessWire for data that you want to markup with RDFa attributes. Then take the semantic data from your page fields, and use RDFa attributes when you output your markup. This will work great for many instances. But it doesn't solve the need for contextual RDFa attributes that appear in content you've input into TinyMCE. I did a quick search for an RDFa plugin for TinyMCE and it sounds like there are people working on it, but nothing concrete (as far as I can tell). So for that type of RDFa support, we may have to wait till a rich text editor has proper support. Though that would put pretty much all CMSs in the same spot on that particular issue. I will keep an eye on this and do more research, as I do think this is an important thing. Thanks, Ryan
-
Usually if I have hundreds of page references (categories) to create I use the API to create them (like from a CSV or text file) rather than doing it manually -- let me know if I can give an example of how to do that. If you are doing something with free tagging, then I would use url segments. Lets say you've got a free tagging field called "tags", and it's just a textarea with one or more tags. You might setup a template called "tags" and on a page called "/tags/". Accessing /tags/something/ in your browser should list all pages that match the tag "something". To do that, first turn on URL segments in your template (Admin > Setup > Templates > Advanced > URL Segments). Then, in your template, your code might look something like this: <?php if($tag = $sanitizer->selectorValue($input->urlSegment1)) { $matches = $pages->find("tags*=$tag"); echo $matches->render(); // or loop through and print on your own } For pre-defined categories using page referencs, I think that asmSelect is a good input field to use for selection. However, if you need to be able to select pages in a hierarchy (like category or subcategory with the same field) then I think the "page list select multiple" is a good solution, because it enables you to select pages in a hierarchy rather than in one level. You could also use any of the other multi select input fields and use the template as the base for selection rather than the parent. ProcessWire is a couple months old, and Drupal has been around since 2001, so we're not going to have the same level of features in some areas. But if you think Drupal is better at this than PW2, describe further, and more specifically, so that I can make sure we get PW2 one step ahead. When it comes to working with these kind of data relationships, I believe PW2 is better than Drupal on the back-end, so I want to make sure I provide enough front-end simplicity to provide and prove that. thanks, Ryan
-
AdminBar is really great! I will be installing it on all of my clients' sites (already started actually). ;D
-
You can get the admin URL from: $config->urls->admin Or if you want to retrieve the admin page: $admin = $pages->get($config->adminRootPageID); Btw, I don't often do it, but it is good security to change your admin page name. You do this by editing it, clicking on the 'settings' tab and changing the name.
-
I think you would want to use the sitemap strategy, like on the API "include & bootstrap" page. But if you only need one level of depth, we could do it even simpler. I'm on an iPhone right now so don't have good examples, but let me know if the sitemap strategy doesn't make sense an I can post some good examples in the morning.
-
Hi Martin, I think this is a good example: http://processwire.com/talk/index.php/topic,56.msg443.html#msg443 What we're doing there is hooking in before a page is rendered, and changing the template filename to one in the AdminBar modules directory. Let me know if this makes sense, or if I can provide another example? Thanks, Ryan
-
Sure--what I would suggest is adding a text or textarea field and calling it "tags". Then people can type whatever tags in there that they want. When you want to find all pages matching a given tag, you'd do this: $matches = $pages->find("tag*=something"); // where 'something' is a tag If you want predefined tags, then I would use a page reference fieldtype to associate a group of pages as tags. Populate the pages with their name or title field as your tag. Then use a multiple selection field like asmSelect to allow selection of the tags/categories. To find pages matching a given tag, you'd do this: $tag = $pages->get("/tags/something/"); $matches = $pages->find("title=$tag");
-
Sevarf: I forgot something on the previous commit (it wasn't allowing periods in the URL name). That is fixed, so if you grab the latest, it'll let you use ".html" with your page names (or any page name with periods in it for that matter). Regarding SEO and .html, of course listen to your expert. I don't claim to have the title of SEO expert, so take what I say with a grain of salt. But SEO is fundamentally intertwined with all the work I do, and has been for more than 10 years. I'm responsible for maintaining dozens of sites at top rankings with very competitive terms over the long term. My experience may be different from others, but what I've learned is that any time you are tweaking details with the intention of tricking (or optimizing) Google into thinking your page is something it's not, you are asking for trouble. It's arguable whether using an .html extension falls into that category since it can be purely stylistic. But your SEO expert is suggesting you do it in order to exploit some apparent weakness in Google's algorithm (which I don't believe exists). An SEO strategy that dips into these considerations it not a good long term strategy, and is focused on the wrong things, in my opinion. However, I don't claim to have all the answers, nor do I know anyone at Google, so I'm not suggesting you change your strategy. Just be wary of anything founded in quick fixes or algorithm exploits. Unless the need calls for black hat, in which case we'd probably be talking about how to use ProcessWire to automate creating thousands of Youtube and Facebook accounts. That will be a fun guide to write. Regarding this forum's use of .html -- SMF is not a good example. I love this forum software, but have a look at the SMF code sometime, and you'll see what I mean. In addition, view the source on this page and you'll see a populated meta keywords tag... they make a great forum, but I wouldn't trust them with my client's SEO. Though I do have to give them credit for adding a required, difficult-to-modify (hashed) backlink to their site in the footer. I had to use PHP's buffer functions just to change it to a rel=nofollow. Adam: I don't want a site-wide slash setting because template API code has to consider the slash setting. I want to have a site-wide expected default (of having a slash) so that there is a known playing field for code examples, prepared templates, etc. If someone wants to change it, then they can do so with the knowledge that they may have to consider it in their code on a template-by-template basis.
-
I do want to make sure people have the flexibility to do it any way they want, so I think that's what we've got now (they can set it according to their preference). I've been meaning to add this slashes setting, so figured now was the time with this most recent request. My preference for the slashes is because a page can be both a container for data (fields) and a container for pages (children). As a matter of consistency, I want to treat all pages the same (at least on my own sites) so that my site's API code doesn't always have to be looking for the presence of slashes when working with selectors, relative paths, url segments and such. I don't want to have to always consider these things when developing a site. As for adding extensions like ".html", that would kill the ability to use page URLs/paths in selectors, unless you actually named your page with the ".html" extension. So if we start adding automatic extensions, I think we start creating a lot more work for the site developers and general confusion... at least I would find it confusing. Sure there might be solutions around the issues, but if something is going to be used on less than 30% of sites then it doesn't belong in the core (which would make extensions a possible good module idea).