Leaderboard
Popular Content
Showing content with the highest reputation on 06/06/2013 in all areas
-
Since you guys asked for it, I'll take a stab at a case study on the development process. Most of the development was done in about a week and a half. I started with the basic profile, but it ended up being something somewhat similar to the Blog profile in terms of how it's structured. Below I'll cover some details on the biggest parts of the project, which included data conversion, the template structure, the front-end development and anything else I can think of. Data Conversion from WordPress to ProcessWire One of the larger parts of the project was converting all of the data over from WordPress to ProcessWire. I wrote a conversion script so that we could re-import as many times as needed since new stories get added to cmscritic.com almost daily. In order to get the data out of WordPress, I queried the WordPress database directly (my local copy of it anyway) to extract what we needed from the tables wp_posts for the blog posts and pages, and then wp_terms, wp_term_relationships, and wp_term_taxonomy for the topics and tags. WordPress stores its TinyMCE text in a state that is something in between text and HTML, with the most obvious thing being that there are no <p> tags present in the wp_posts database. Rather than trying to figure out the full methodology behind that, I just included WP's wp-formatting.php file and ran the wpautop() function on the body text before inserting into ProcessWire. I know a lot of people have bad things to say about WordPress's architecture, but I must admit that the fact that I can just include a single file from WordPress's core without worrying about any other dependencies was a nice situation, at least in this case. In order to keep track of the WordPress pages imported into ProcessWire through repeat imports, I kept a "wpid" field in ProcessWire. That just held the WordPress post ID from the wp_posts table. That way, when importing, I could very easily tell if we needed to create a new page or modify an existing one. Another factor that had to be considered during import was that the site used a lot of "Hana code", which looked like [hana-code-insert name="something" /]. I solved this by making our own version of the Hanna code module, which was posted earlier this week. Here's an abbreviated look at how to import posts from WordPress to ProcessWire: $wpdb = new PDO("mysql:dbname=wp_cmscritic;host=localhost", "root", "root", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'")); $posts = wire('pages')->get('/posts/'); $sql = " SELECT * FROM wp_posts WHERE post_type='post' AND post_status='publish' ORDER BY post_date "; $query = $wpdb->prepare($sql); $query->execute(); while($row = $query->fetch(PDO::FETCH_ASSOC)) { $post = $posts->child("wpid=$row[ID]"); // do we already have this post? if(!$post->id) { // create a new post $post = new Page(); $post->template = 'post'; $post->parent = $posts; echo "Creating new post...\n"; } $post->of(false); $post->name = wire('sanitizer')->pageName($row['post_name']); $post->title = $row['post_title']; $post->date = $row['post_date']; $post->summary = $row['post_excerpt']; $post->wpid = $row['ID']; // assign the bodycopy after adding <p> tags // the wpautop() function is from WordPress /wp-includes/wp-formatting.php $post->body = wpautop($row['post_content']); $post->save(); echo "Saved post: $post->path\n"; } What I've left out here is the importing of images, topics, tags, and setting the correct authors for each post. If anyone is interested, I'll be happy to go more in depth on that, but didn't want to overwhelm this message with code. Template File Structure This site makes use of the $config->prependTemplateFile to automatically include the file _init.php before rendering a template file, and $config->appendTemplateFile to automatically include the file _main.php after. So the /site/config.php has this: $config->prependTemplateFile = '_init.php'; $config->appendTemplateFile = '_main.php'; You may recognize this as being the same setup from the Skyscrapers profile. The _init.php includes files containing functions we want to be available to all of our templates, and set default values for the regions we populate: /site/templates/_init.php /** * Include function and hook definition files * */ require_once("./includes/render.php"); require_once("./includes/hooks.php"); /** * Initialize variables populated by templates that get output in _main.php * */ $browserTitle = $page->get('browser_title|title'); $body = "<h1>" . $page->get('headline|title') . "</h1>" . $page->body; $side = ''; $renderMain = true; // whether to include the _main.php file The includes/render.php file that is included above includes several functions for generating markup of navigation and post summaries, or any other shared markup generation functions. Examples are renderPost(), renderNav(), renderTags(). This is similar to the blog.inc file from the Blog profile except that I'm letting these functions generate and return their own markup rather than splitting them into separate view files. I personally find this easier to maintain even if it's not as MVC. The includes/hooks.php sets up any hooks I want to be present for all of my templates. I could have also done this with an autoload module, but found this to just be a little simpler since my hooks were only needed on the front-end. The main hook of interest is one that makes all posts look like they live off the root "/" level rather than "/posts/" (where they actually live). This was in order to keep consistency with the URLs as they were in WordPress, so that the new site would have all the same URL as the old site, without the need for 301 redirects. /site/templates/includes/hooks.php /** * This hook modifies the default behavior of the Page::path function (and thereby Page::url) * * The primary purpose is to redefine blog posts to be accessed at a URL off the root level * rather than under /posts/ (where they actually live). * */ wire()->addHookBefore('Page::path', function($event) { $page = $event->object; if($page->template == 'post') { // ensure that pages with template 'post' live off the root rather than '/posts/' $event->replace = true; $event->return = "/$page->name/"; } }); Our /site/templates/_main.php contains the entire markup for the overall template used site wide, from <html> to </html>. It outputs those variables we defined in _init.php in the right places. For example, $body gets output in the <div id='bodycopy'>, $side gets output in the right <aside>, and $browserTitle gets output in the <title> tag. /site/templates/_main.php <?php if($renderMain): ?> <html> <head> <title><?=$browserTitle?></title> </head> <body> <div id='masthead'> // ... </div> <div id='content'> <div id='bodycopy'><?=$body?></div> <aside id='sidebar'><?=$side?></aside> </div> <footer> // ... </footer> </body> </html> <?php endif; ?> We use the rest of the site's template files to simply populate those $body, $side and $browserTitle variables with the contents of the page. As an example, this is an abbreviated version of the /site/templates/post.php template: /site/templates/post.php // functions from /site/templates/includes/render.php $meta = renderMeta($page); $tags = renderTags($page); $authorBox = renderAuthor($page->createdUser); $comments = renderComments($page); $body = " <article class='post post-full'> <header> <h1>$page->title</h1> $meta </header> $page->body $tags $authorBox $comments </article> "; if(count($page->related)) { $side = "<h4>Related Stories</h4>" . renderNav($page->related); } What might also be of interest is the homepage template, as it handles the other part of routing of post URLs since they are living off the root rather than in /posts/. That means the homepage is what is triggering the render of each post: /site/templates/home.php if(strlen($input->urlSegment2)) { // we only accept 1 URL segment here, so 404 if there are any more throw new Wire404Exception(); } else if(strlen($input->urlSegment1)) { // render the blog post named in urlSegment1 $name = $sanitizer->pageName($input->urlSegment1); $post = $pages->get("/posts/")->child("name=$name"); if($post->id) echo $post->render(); else throw new Wire404Exception(); // tell _main.php not to include itself after this $renderMain = false; } else { // regular homepage output $limit = 7; // number of posts to render per page $posts = $pages->find("parent=/posts/, limit=$limit, sort=-date"); $body = renderPosts($posts); } The rest of the site's template files were handled in the same way. Though most were a little simpler than this. Several were simply blank, since the default values populated in _init.php were all that some needed. Front-end development using Foundation 4 The front-end was developed with the Foundation 4 CSS framework. I started with the Foundation blog template and then tweaked the markup and css till I had something that I thought was workable. Then Mike and I sent the _main.php template file back and forth a few times, tweaking and changing it further. There was no formal design process here. It was kind of a photoshop tennis (but in markup and CSS) where we collaborated on it equally, but all under Mike's direction. After a day or two of collaboration, I think we both felt like we had something that was very good for the reader, even if it didn't originate from a design in Photoshop or some other tool like that. I think it helps a lot that Foundation provides a great starting point and lends itself well to fine tuning it the way you want it. I also felt that the mobile-first methodology worked particularly well here. Comments System using Disqus We converted the comments system over to Disqus while the site was still running WordPress. This was done for a few reasons: Disqus comments provide one of the best experiences for the user, in my opinion. They also are platform agnostic, in that we could convert the whole site from WP to PW and not have to change a thing about the comments… no data conversion or importing necessary. Lastly, ProcessWire's built-in comments system is not quite as powerful as WordPress's yet, so I wanted cmscritic.com to get an upgrade in that area rather than anything else, and Disqus is definitely an upgrade from WP's comments. In order to ensure that Disqus could recognize the relations of comment threads to posts, we again made use of that $page->wpid variable that keeps the original WordPress ID, and also relates to the ID used by the Disqus comments. This is only for posts that originated in WordPress, as new posts use a ProcessWire-specific ID.18 points
-
What kills me about this module is that Ryan literally decided to make it because I had something similar in WordPress and needed a substitute solution in ProcessWire (as part of the move of CMS Critic to PW) otherwise I'd have a ton of weird shortcodes in my posts for no reason. He made this module, imported my code from the other module (called Hana Code in WP) and named his Hanna Code (after his daughter) and Poof! a module is born. I am insanely jealous of his mad php skills (but glad they can be bought!)4 points
-
Other uploaders/topics that may be of interest http://www.plupload.com/ (by TinyMCE guys) - this is a beast! http://www.fyneworks.com/jquery/multiple-file-upload/# http://www.phpletter.com/Demo/AjaxFileUpload-Demo/ http://pixelcone.com/jquery/ajax-file-upload-script/ http://www.jscripts.info/mfupload.php http://www.jquery4u.com/plugins/10-jquery-ajax-file-uploader-plugins/ http://www.9lessons.info/2012/09/multiple-file-drag-and-drop-upload.html http://www.nacodes.com/2013/04/14/DROPAREA-jQuery-Html5-drag-drop-file-uploader-plugin3 points
-
Hey, The Form API has CSRF protection build in, but if you for some reason don't want to use the API you can however use the CSRF protection. Its very simple but it took some time for me to find out, so i figured i share my findings with the rest. What is CSRF? First you need to create a token and a token name you do that as following: $tokenName = $this->session->CSRF->getTokenName(); $tokenValue = $this->session->CSRF->getTokenValue(); Very simple. Now what you want to do is create a hidden input field like this: $html .= '<input type="hidden" id="_post_token" name="' . $tokenName . '" value="' . $tokenValue . '"/>'; Now this will generate something that will look like this: You are done on the form side. You can now go to the part where you are receiving the post. Then use: $session->CSRF->validate(); This will return true (1) on a valid request and an exception on a bad request. You can test this out to open up your Firebug/Chrome debug console and change the value of the textbox to something else. Basicly what this does is set a session variable with a name (getTokenName) and gives it a hashed value. If a request has a token in it it has to have the same value or it is not send from the correct form. Well I hope I helped someone.2 points
-
I just pushed ProcessWire v2.3.1 to the dev branch. This is a fairly major change in that it switches the DB driver from mysqli to PDO. It meant changes to a large percentage of core files. ProcessWire still supports mysqli, but doesn't attempt to use it unless a module or a template asks for it via the $db API variable. The new API variable (for the PDO driver) is $database. More about PDO at php.net If you are using the dev branch, be careful and test thoroughly with this latest commit to it. Before upgrading, you may want to double check that your PHP supports PDO by looking at your phpinfo (CMD-F or CTRL-F for "PDO"), especially if you are running PHP 5.2.x (where PDO wasn't compiled in by default). Though if you are running PHP 5.2 (vs 5.3.8+) then you may want to just stick with ProcessWire 2.3.0 and upgrade your PHP version when possible. If you are using any modules that use the procedural version of mysqli functions (vs. the $this->db object oriented versions), or type-hint mysqli in methods, then those modules will no longer work. If you come across any modules that don't work with 2.3.1, please let me know here so that I can assist the author in updating them. Note that FormBuilder is one of the modules that does not work with 2.3.1, but I have posted an update in the FormBuilder board that corrects it–so be sure to download that version if you are tracking the dev branch of ProcessWire and using FormBuilder. What this new version adds: 1. New API variable $database that refers to the PDO database. The old $db API variable is still there and refers to mysqli for any modules that continue to use it. 2. New API variable $log that lets you easily log messages or errors to the system logs. Usage: $log->message("This saves this line to messages.txt"); $log->error("This saves this line to to errors.txt"); $log->save("my-log", "This saves this line to my-log.txt"); // Get an array of the last few entries saved to the messages log $entries = $log->get('messages'); // Get an array of the last 50 entries saved to my-log $entries = $log->get('my-log', 50); Note that as always, log files are located in /site/assets/logs/. 3. Conditional autoload modules. In PHP 5.3+, modules may now specify an anonymous function OR a selector string, rather than a boolean for the 'autoload' property returned by getModuleInfo(). PW runs the anonymous function after determining the current $page, so your module can make autoload decisions based on the $page (or any other factor you'd like), if desired. Lets say that we have a module that we only want to autoload when the template is 'admin': public static function getModuleInfo() { return array( 'title' => 'Module Title', 'summary' => 'Summary text...', 'version' => 1, 'autoload' => function() { if(wire('page')->template == 'admin') return true; else return false; }); } And the same example but using a selector for autoload: public static function getModuleInfo() { return array( 'title' => 'Module Title', 'summary' => 'Summary text...', 'version' => 1, 'autoload' => 'template=admin' ); } 4. Addition of $pages->add() method. Actually $pages->add($template, $parent, [string $name], [array $values]); This function adds a new page to the database and returns it. This is for syntax convenience, but using the old method is still perfectly fine too. Here's a few examples of usage: // add a new page using template basic-page under /about/ $newpage = $pages->add('basic-page', '/about/'); // same as above, but named 'contact' $newpage = $pages->add('basic-page', '/about/', 'contact'); // same, but populate the title field too $newpage = $pages->add('basic-page', '/about/', 'contact', array('title' => 'Contact Us')); // you can also do this, specifying the values array as 3rd argument: $newpage = $pages->add('basic-page', '/about/', array('title' => 'Contact Us')); $template and $parent are required, but may be objects, IDs, or string identifiers (like name for template, or path for page). When you add a new page and don't specify a 'name', then PW will make one up, guaranteed to be unique. 5. Module files that end in '.module.php' are now supported. So rather than ClassName.module, you may use ClassName.module.php if you prefer it. The purpose here is to support text editors that determine their syntax highlighting based on the file extension. More updates being made almost daily. Please report any issues you experience. Thanks, Ryan2 points
-
Like Wanze I really like the way that articles paginate in the admin, but you can always create a page in the Admin that lists all the articles any way you prefer. A very simple way o do this is to link to a pre-defined search in the admin. So, imagining that you want to list all the articles, just go to advanced search, and with the search field empty, limit the search to the articles template; or if you want all articles from one category, "limit to the template": "article", "search in fields": "category" and "search for": "category name or ID". You can for instance create a new admin page using my Admin custom pages module with some pre-built links. If you want a really clever system with this, study the urls that result from these searches, and create them dynamically by request. domain.com/processwire/page/search/?field=category&operator=0&q=general_news&show_options=1&submit=Search&template=article&sort=created&display=title,path2 points
-
Yes exactly. The pseudo user page can be anywhere and you could add children pages for all kind of things. And only a couple field like prename lastname to the real user template. You dont have to it's up to you. I also think you're creative enough to already see what is possible it's more the code you worry. Let me say in PW it'S REALLY no rocket sience compared to other systems and just keep trying to go with the simple API. Once you learned that you'll do anything with ease. Maybe I'm too modest but I'm also not a hardcore programmer but a design guy, and learned a whole lot with PW. Of course I got some background and did a lot with php. You're in good hands with the guys here.2 points
-
A posibility I also use is have a page field reference added to the user template and reference the userpage there. Makes it easy to setup and you dont have to care about names as it's already checking for unique username.2 points
-
I had forgotten about that module. If you want to go the route of adding new fields to the user template, that is easy. Just go to templates and click on filters and show system templates. Then you can add any new fields you want to the user template. @slkwrm - your edit stole my thunder2 points
-
You can have your user's page name the same as the user name. Not unique names can cause problems only when we talk about the pages belonging to the same parent. You can always check if the page name is unique using something like: if (!$pages->find("parent=$myParent, name=$myPageName, include=all")->count()) { //the page with this name doesn't yet exist within specified parent so it's safe to add myPage under myParent... } Yes, you can extend user template with additional fields. Just go to templates and choose to show system templates in the filter option.2 points
-
I feel like a padawan learning from Yoda.. ;P #lamestarwarsreferenceforthewin2 points
-
A truly epic write up Ryan. I'm sure all those who visit this forum will benefit from your knowledge. Thanks for sharing it.2 points
-
Hello Igor, you have lots of questions there. All that is doable with ProcessWire, and answered already to the most simple question: PW is not to heavy for shared hosting, it's even much lighter than most known CMSs. Because you have so many questions, I will answer with links. After reading those, you will be able to to narrow down your questions a bit so you can get some really useful answers: categories: http://processwire.com/talk/topic/3579-tutorial-approaches-to-categorising-site-content/ comments: There is a built in comment system (same as in PW site pages), you just have to activate t in modules: http://processwire.com/api/fieldtypes/comments/ eCommerce http://processwire.com/talk/topic/1732-shop-for-processwire-apeisa/ (lots of suggestions besides Apeisa's module) http://processwire.com/talk/topic/3756-shopify-integration/ (most recent discussion) Contact form: http://modules.processwire.com/modules/form-template-processor/ http://processwire.com/talk/topic/59-module-want-form-builder/#entry343 or http://store.di.net/products/processwire-form-builder group/permission http://processwire.com/api/user-access/roles/ multilanguage URLs http://processwire.com/talk/topic/2979-multi-language-page-names-urls/2 points
-
wire("page") in this context will give you the admin edit page and not the page editing. In this case you can get the page edited through ProcessPageEdit. if($this->process = "ProcessPageEdit") { $page = $this->process->getPage(); }2 points
-
Hey Everyone, I had to stop laughing so I could post ths: http://techcrunch.com/2013/06/05/microsoft-internet-explorer-10-is-the-most-energy-efficient-browser-uses-up-to-18-less-power-than-chrome-and-firefox/ Thanks, Matthew2 points
-
Hello all, I just spent 3 days without internet and I couldn't do much work on my ProcessWire project because I couldn't use the cheatsheet. I realized that there's a need to have PW's documentation in an ebook format including cheatsheet. So I've copy-pasted cheatsheet in a doc file & created a cheatsheet ebook for offline use. I'm posting it here so that others can also use it. Please let me know if doing this is wrong in anyway, I'll delete the files. Also note that there was no intention to make any kind of profit by using cheatsheet to make an ebook out of it. I hope this will help people like me when they're unable to access cheatsheet for any reason. Enjoy. EDIT: Attached file has been updated, as it wasn't readable on Mac and also wasn't very good looking. So I've updated it for better readability but I'm not sure if it still works on Mac, so someone needs to check it for me. Cheatsheet_1.1_v0.2.pdf1 point
-
Scale isn't a problem. Pw does paginate the pages after an amount of pages (50 by default). You can change this in the Page List module. No nightmare at all...1 point
-
Hi Galaxy, Have you read the docs? http://processwire.com/api/fieldtypes/repeaters/ It's pretty straightforward: $total = 0; foreach ($page->grants as $grant) { echo "Date granted: {$grant->cheque_date}"; echo "Amount ${$grant->amount_approved}"; $total += $grant->amount_approved; } To get the last date, there are several solutions. One of them: $page->grants->sort('-cheque_date')->first()->cheque_date; Edit: kongondo is online all the time and faster than me1 point
-
Galaxy, These should get you started...nothing really special about accessing repeaters http://processwire.com/api/fieldtypes/repeaters/ http://processwire.com/talk/topic/958-repeatable-fields/1 point
-
And we have you to thank for hiring him in the first place which led us to Hanna Code! Thank you!1 point
-
Such kind & sweet words! I can't thank enough to you guys, not only do you help people like me to do something that we can only dream but also support & understand us. This very thing, the flexibility that PW provides is what allows me to make websites which I could never even think of before. It's easy to learn & work with PW provided you know what to look for & where. I've just basic knowledge of php which also I learnt just to work with ProcessWire, cause I had understood the potential of PW a while ago but I really started using just a few months back. So the topic is solved, I'll continue with FrontendUserProfiles module & add pages for users to store additional information. Thank you all once again. PW & this forum rocks!!!1 point
-
Ok, I saw some other posts from you about this. Did you end up using some of the code from this post: http://processwire.com/talk/topic/126-anybody-did-user-registrationlogin/?p=4812 Depending on your needs, you could add additional fields to the user template, rather than needing a separate page. Regardless, when the form is submitted you need to check for the existence of the username. If it already exists, return the error and get them to enter a different name. If it doesn't exist, save the user and if you need, add the new page using their new username for the page name. Probably stating the obvious here though. I assume you already have the front-end user registration form working? It sounds to me like you might be better served by extending that to add the page, rather than using the FormTemplateProcessor module. Maybe if you post the code you currently have it might make it easier to add the required functionality.1 point
-
Hi Nik, I can not thank you enough. My first trip to Finland will be to buy you some beers or food or whatever. I will heed your advise and be sure I describe my problem thoroughly before I post it to be sure it is understood completely from others perspectives other then mine. I thought I had a good grasp on what I was trying to learn/achieve as this is just a mock site for me to learn from, but I was a tad bit over my head. I still have one other part of the form that I need to tackle, but I will attempt to achieve those results a few more days before asking for help. Thanks again and cheers!1 point
-
Thanks, glad I could help you RJay! I don't know why has_parent isn't in the cheatsheet (Soma/Ryan?) as it is mentioned in the API documentation (http://processwire.com/api/selectors/). There are one or two other selectors that are not mentioned in either of those I'm sure, but I think it's intentional as they're only for internal use for a reason or another. So everything you really need to know is documented . Happy studying with ProcessWire and don't give up if you ever hit a wall. Ask away and someone will help you for sure. But do try and describe the context and what you're trying to achieve detailed enough and there will be a solution in way less than four days next time.1 point
-
A couple of things to consider. Take a look at the sanitizer options for page names: http://processwire.com/api/variables/sanitizer/ One concern with using the title for the page name is name uniqueness. Take a look at this thread for more details/options: http://processwire.com/talk/topic/3262-create-non-duplicate-page-name-via-api/1 point
-
I haven't used that module, but it looks like it is focused on processing a contact form. So the name of the page is being autogenerated as a datetime stamp ($this->contact->name = date('y-m-d-H-i-s-u');) so it is unique to each user's contact submission - the title is not really relevant for this purpose as I see it. If you want to save user generated content as a page, I would recommend this as a starting point: http://processwire.com/talk/topic/59-module-want-form-builder/?p=11639 You can use the title that the user enters and use it for the page's name. Hope that helps.1 point
-
Hi, Vineonardo. You should remove 'title' item from the array on the line 80 of the module: $this->set('skipFields', array('title'));1 point
-
<form action="/actions/post-comment" method="post"> You need a trailing slash, I think Pw does a redirect and you loose the POST request. Please try this once again and make sure it does still not work. Edit: To be bullet proof: <form action="<?php echo $pages->get(ID_OF_PAGE)->url; ?>" method="post">1 point
-
Massive! Thanks for the write-up Ryan! I'm especially intrigued by the import script and the URL hook.1 point
-
OK, now we're back on track I think. So, you've lined up those three things to use page id's? Then your search form would be like this: <select name="location"> <option value="">any</option> <?php foreach($pages->get("/")->children("id!=10") as $location) { $selected = $location->id == $input->whitelist->location ? " selected='selected' " : ''; echo "<option$selected value='{$location->id}'>{$location->title}</option>"; } ?> </select> And now you could use something like this in search.php: # has a location been selected? if($input->get->location) { # sanitize (page id is always an integer) # no need to fetch the actual page as the search selector restricts results to employees $location = (int)$input->get->location; # employees have template 'employee' and their parent page is the chosen location $selector .= "parent={$location}, template=employee"; # whitelist the sanitized value (and use from whitelist later on) $input->whitelist('location', $location); } Hope I didn't introduce any new errors there...1 point
-
Just clicking 'Like' doesn't seem enough. Ryan, you make it all sound so easy.1 point
-
if (wire('input')->post->submit)) { ... } Inside functions, the API variables are out of scope. That's a PHP thing. So for every API variable, use the global function wire('api_var_here').1 point
-
I think I need version control to keep up with the changes in your post. It seems I'm even more lost now than I was before. Which template file are those snippets from exactly, which "child page" are you referring to? And what are the templates assigned to the pages in your structure? Which template has field 'location' you're using in $selector? What else goes into $selector? Can't see the whole picture here... At least you've got a nice mess there as you're using id, name and title like they were the same. What I mean is there's page id as the value for an option in the dropdown you're whitelisting page name as location check for previously chosen item uses page title Those three should be done using the same property, preferably id or name.1 point
-
I think Antti is right here that the lightbox plugin would need some feature enabling it to load it's own images (ajax or otherwise) to go beyond what's actually present on the page. What you have here are two different systems of pagination. I am wondering if you could trick the lightbox into it–you could render those first 15 images as you are now, but then render links for the remaining (perhaps hidden to the user) but have your lightbox plugin somehow recognize them as part of the gallery. For example: <a class='gallery' href='photo1.jpg'><img src='photo1thumb.jpg'></a> <a class='gallery' href='photo2.jpg'><img src='photo2thumb.jpg'></a> ... <div style='display: none;'> <a class='gallery' href='photo16.jpg'><img src='1pixel.gif'></a> <a class='gallery' href='photo17.jpg'><img src='1pixel.gif'></a> ... </div>1 point
-
Here's a proof of concept PW Admin Theme Switcher (video and write-up). It shows how versatile PW is. Since everything in the PW tree is a page, including Admin this was easy to do. I think on the roadmap PW will eventually allow switching between Admin Themes? Anyway, here's how I did it (verbosity below is intentional ). This is not a module but can easily be converted into one. Currently, I don't plan to do so since I suspect it will be overtaken by events (roadmap). Instead, I used the excellent Admin Custom Pages (ACP) module. On load, PW will first check if there is an admin template at /site/templates-admin. If there is none, it will check in /wire/templates-admin/. The file it looks for first is /site/templates-admin/default.php 1. I copied the contents of /wire/templates-admin/ to /site-templates-admin/ 2. I modified the contents of default.php as shown below in order to use it as a controller file $theme = $pages->get(1424)->theme_selector;//id of admin theme switcher page (the child page) include($config->paths->adminTemplates . "{$theme->name}/default.php"); 3. In order to use ACP in PW 2.3, you have to create a parent and child page. The child page will be rendered in the Admin. 4. I created pages under Admin as follows: Admin Theme Switcher Admin Themes (id#1424) Default Teflon Moderna, etc. 5. Admin Theme Switcher has nothing of interest. It just enables me to render its child page in PW Admin. Admin Themes has a single Page Reference Field from which only its children can be selected. This field is called theme_selector. Its reference value is the ID of whatever child page of Admin Themes is selected. 6. Default, Teflon, etc., have various fields to hold data about the themes - author, version, description, screenshot, etc. I populated these as needed. 7. In the above code (#3), 2nd line, I am telling PW where the default.php is. I am appending the name of the selected child page of Admin Themes. Hence, if the current selected page is Minimalist Blue, its PW name, minimalist-blue is included as part of the path. This means I need to create a corresponding folder at that path. 8. I grabbed all my themes in their respective folders including all their files and copied them to /site/templates-admin/. This ensured that there would be no conflict having multiple themes in /site/templates-admin/ folder. I also copied the default PW theme with all its files into a folder called default. I ended up with a structure like this: /site/templates-admin/default/ /site/templates-admin/teflon/ /site/templates-admin/moderna/ /site/templates-admin/appy/ etc. 9. Of course since the themes were now in sub-folders, paths in their respective default.php would be wrong. I needed to change this, e.g. in /sites/templates-admin/teflon/default.php, I changed the paths as follows: $config->styles->append($config->urls->adminTemplates . "teflon/styles/ui.css?v=2"); $config->scripts->append($config->urls->adminTemplates . "teflon/scripts/inputfields.js"); $config->scripts->append($config->urls->adminTemplates . "teflon/scripts/main.js?v=2"); $config->styles->append($config->urls->adminTemplates . "teflon/styles/droppy.css") <?php include($config->paths->adminTemplates . "teflon/topnav.inc"); ?> 10. Now that all is set up, there's two ways to change the current Admin theme: a. The template file of Admin Themes has a foreach that loops through its child pages (the theme pages) and outputs the meta data in a I column Grid. Each item has a radio input field to select the theme. On save, jQuery Ajax sends the single value (ID of selected theme) to a simple processor file. The processor file updates the value of the single Page Reference Field (theme_selector) using PW API. Via JSON, the processor then tells jQuery if update was successful and page is reloaded. b. Alternatively, the theme can be changed by editing the Admin Themes page, selecting a child page (theme) using the single Page Reference Field (theme_selector) and saving. PW will auto-reload the page, applying the new theme. As you can see, there is no rocket science here.....just an amazing CMS1 point
-
I don't think this would work as I'm not aware of any situation in PHP 5+ where PHP will return a TRUE value for empty() on an object. Assuming I understand correctly, PHP will never return anything but FALSE on an object. Though I think that in PHP 4 it would return TRUE for an object with nothing in it, but not so in PHP 5.1 point
-
Well... That was good It looks logical that Microsoft appeals to such a criterion after they've made this promo:1 point
-
1 point
-
One of the many things I love about PW is that whenever new features are added they always make sense. Not just Ryan's excellent descriptions, but the additions themselves. In this case I especially like the $pages->add method.1 point
-
Maybe can you try to change soemthing in RangeSlider? FieldtypeRangeSlider.module in the ___sleepValue method This: $sleepValue = array( 'data' => $range->min, 'data_max' => $range->max ); to this: $sleepValue = array( 'data' => (int) $range->min, 'data_max' => (int) $range->max );1 point
-
1 point
-
I think you're right Ryan but maybe logic shoud be coherent all the times when 404 page is rendered. Consider this kind of scenario (that's used in this particular project). You've translated homepage name to match your language setup so every page has language identifier prefixed in url: domain.com/en/products/ domain.com/de/produkte/ domain.com/se/produkter/ Then if you enable url segments in this product template and handle false matches by throw new Wire404Exception(): domain.com/de/produkte/somefalsepath/ // here 404 page is in german when 404 is thrown manually from template domain.com/de/somefalsepath/ // here 404 page is in default language when system throws it It would be more logical if 404 page has same language in both cases.1 point
-
1 point
-
1 point
-
Thanks ryan! And thanks again to Jason for letting my run with some ideas. I could just as easily completed this by making the admin enter comma separated values in a textarea field (the easy way to go) but I wanted to make input foolproof and the idea was already in my head. And now some techy stuff: I had some fun working out the ajax behind adding new rows and settling on saving the field data as a JSON string in the end (which is fine because nobody is going to set up dozens of email accounts!) but if there are any suggestions for improvement there then please let me know as it seems to me now that there are endless possibilities for "add another row" type setups in module configuration. Before that I was trying to overcomplicate things and have the new rows save in the background as an array, but if I intercepted the page save, ran the ajax request and then use jQuery to finish submitting the form then it wouldn't save the other form data. I abandoned that idea completely (but left the function in the module file for reference). In the end, keeping it simple worked out better as running ajax upon saving the config page slowed the page save process down as it essentially sent two requests - converting the data to a json string and saving to a hidden field is instantaneous by comparison To those not following all that, my eventual solution was to KISS1 point
-
Sorry about that, I went back and forth on the name a couple times and ultimately entered the wrong class name in the modules directory. It should have been TextformatterHannaCode, not ProcessHannaCode. I have corrected that. I was throwing an exception when I shouldn't have. I'm pushing a correction for this. Technically they are part of the same program "Hanna Code", even if that program has two modules. But I know what you mean. I'm adding "(Process)" to the Process one, just so that the two next to each other can be differentiated. Also, one more section I just added to the README: Using Hanna Code from the API If you want to populate Hanna Code from the API, you can do so using the render() method, like this: $hanna = $modules->get('TextformatterHannaCode'); $page->body = $hanna->render($page->body);1 point
-
Status flag 1024 means that a page is hidden. That (<1024) asks for pages not hidden (ie. status smaller than 1024.) See https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/core/Page.php#L58 for details.1 point
-
Hi all, i am new to PS and didn't have enough time to learn it. I think will be great platform for my future projects. For start I use default template and create the /site/templates/contact.php . The form shows well and submit sends the mail, but after that i got page with empty body. No "Thank you ..." message neither option to go back to home page. Please ryan, point me to the right code after sending email which says "thanks" and show button to go to home. Thanks in advance! Edited: changed $page->body with simple echo solved the problem. The working code is: <?php include("./head.inc"); echo $page->body; $sent = false; $error = ''; $emailTo = 'my@email'; // or pull from PW page field // sanitize form values or create empty $form = array( 'fullname' => $sanitizer->text($input->post->fullname), 'email' => $sanitizer->email($input->post->email), 'comments' => $sanitizer->textarea($input->post->comments), ); // check if the form was submitted if($input->post->submit) { // determine if any fields were ommitted or didn't validate foreach($form as $key => $value) { if(empty($value)) $error = "<p class='error'>Please check that you have completed all fields.</p>"; } // if no errors, email the form results if(!$error) { $msg = "Full name: $form[fullname]\n" . "Email: $form[email]\n" . "Comments: $form[comments]"; mail($emailTo, "Contact Form", $msg, "From: $form[email]"); // populate body with success message, or pull it from another PW field echo "<h2>Thank you, your message has been sent.</h2>"; $sent = true; } } if(!$sent) { // sanitize values for placement in markup foreach($form as $key => $value) { $form[$key] = htmlentities($value, ENT_QUOTES, "UTF-8"); } // append form to body copy echo <<< _OUT $error <form action="./" method="post"> <fieldset> <p> <label for="fullname">Your Name</label><br /> <input style="width:300px;" type="text" id="fullname" name="fullname" value="$form[fullname]" /> </p> <p> <label for="email">Your Email</label><br /> <input style="width:300px;" type="email" name="email" id="email" value="$form[email]" /> </p> <p> <label for="comments">Comments</label><br /> <textarea style="width:300px;" id="comments" name="comments" rows="6">$form[comments]</textarea> </p> <p><input type="submit" name="submit" value="Submit" /></p> </fieldset> </form> _OUT; } // include site's footer include("./foot.inc");1 point
-
Greetings Everyone, Sorry I missed this discussion until now. Yes, I am working on a ProcessWire book and companion Web site. As a technical writer/developer/designer, ProcessWire has been an inspiration on multiple fronts: creating Web sites, going deeper with design/development, and writing/documenting the system. I've been working regularly on the book project, and I have about 175 pages completed already. I'll have much more to share in the coming weeks. Stay tuned, Matthew1 point
-
I added this to the dev branch last week, so that you can now pass additional things to $page->render(): https://github.com/ryancramerdesign/ProcessWire/blob/dev/wire/modules/PageRender.module#L212 One of the things I wasn't thinking about before is that $page->render() was already capable of accepting an array of options (though not commonly used, mostly internal). So we weren't starting from a blank slate, and had to make something that was compatible and complimentary to what was already there. In terms of API calls, you can now do any of these: $page->render($filename); // $filename assumed in /site/templates/ $page->render($pathname); // $pathname is full path, but must resolve somewhere in web root $page->render($options); // array of options and/or your own variables $page->render(array('foo' => 'bar')); // same as above $page->render($filename, $options); // specify filename and options/vars, etc. The $options variable was already something that $page->render() accepted before. Only it was used to specify some little-used modifiers to render(). Those are still there (and they are necessary), but now the utility of $options has been extended. When you want to specify options (outlined in the link above) you only need to specify the ones you want to change from the defaults (of course). Typically you don't need to change them, so I'm guessing that most would use $options to specify their own variables. Every rendered template now receives a copy of that $options array locally scoped. That $options array contains any variables you passed to $page->render(). It also contains PW's default options, should you want to examine any of them. If you made this render() call: echo $page->render('myfile.php', array('foo' => 'bar')); myfile.php could access the 'foo' variable like this: echo $options['foo']; // outputs "bar" One other addition that I'm thinking people might like is $options['pageStack']. That is an array containing a stack of pages that called render(). So if you are doing any recursive rendering of pages, any template can access $options['pageStack'] to see what page is rendering it, and any others before it. Previously this was not possible, and the only way a template could tell what other page was rendering it (if any) was for that renderer to tell the renderee, via $mypage->caller = $page; or something like that.1 point