-
Posts
16,793 -
Joined
-
Last visited
-
Days Won
1,540
Everything posted by ryan
-
PageField: Switching from Checkboxes to Page Autocomplete does not work
ryan replied to doolak's topic in Modules/Plugins
I never was able to reproduce this before. Can you paste in the characters that aren't working for you? I will try to paste them in some pages and then test it out. Also check that you are using the %= operator in your autocomplete settings with the field (not positive that's necessary, but it does bypass the index in case there are any indexing issues with greek characters). -
I agree, it does look very nice! I look forward to integrating this into ProcessWire.
-
I didn't realize before that PageLinkAbstractor doesn't work in this scenario, so will have to put some time into correcting that. But for now I think the best bet is to just search/replace your DB dump when migrating it from dev to live (and this is what I do regardless).
- 6 replies
-
- 2
-
- textareaLanguage
- TinyMCE
-
(and 1 more)
Tagged with:
-
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.
- 37 replies
-
- 61
-
404 when using pagination on multi language (via url-presegment) site
ryan replied to neildaemond's topic in General Support
How is the pagination segment "page2" ending up in the path? The page number isn't considered one of the URL segments returned by $input->urlSegments, so it shouldn't be showing up in there unless the "allow page numbers" option is turned off for that template. -
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>
-
We have 3 cats, and two of them are into singing. Your cat sounds like our cat Sasha. I'm thinking the gateway if() might be here. That $numFields variable must be greater than 1 since your JSON response says "multiple fields". Yet I think you want it to be 1. And it looks that could happen if HTTP_X_FIELDNAME wasn't set for one reason or another, here. I don't understand the technical details of how Fredi works yet (though hoping to learn soon), but I do wonder if this could be solved by just sending the ajax post request to ProcessPageEdit only, rather than Fredi? The reason is that it seems like if something is hooking into ProcessPageEdit, that Fredi doesn't call, wouldn't get executed. An example is this line in FieldtypeRepeater, which I think is attaching a hook that doesn't get executed via Fredi. One other thing, unrelated, is that it looks like these lines should be moved above the ajax section in your execute() function? // Check if there is not such a page found if ( ! $this->pageContext->id) throw new WireException("Page not found"); // Check that this page is editable by current user if ( ! $this->pageContext->editable()) throw new WireException("You don't have right to edit this page")
-
I think that the "page::path" needs to be "Page::path" (uppercase first character). Also, where you place this will matter. You want hooks to be attached before any other code executes. I like to use the $config->prependTemplateFile = 'init.php'; in my /site/config.php, and then setup hooks or any common functions I want in /site/templates/init.php.
-
I can't seem to see anything from the cheatsheet PDF. It just shows up as gradient lines where text would usually be. Anyone else get this? See the screenshot.
- 24 replies
-
- processwire book
- ebook
-
(and 1 more)
Tagged with:
-
TextWrangler is great. While I use VIM and PhpStorm for code, I use TextWrangler for everything else. It's great for performing big manipulations fast (sorting, finding, replacing via regex's, duplicate line processing, multi-file manipulations, etc.). This is one tool I could not do without.
-
Where you can do some nice integration with Shopify is via their post order hooks. You can very easily set Shopify to ping a URL on your ProcessWire site with some JSON data about an order, immediately after it takes place. From there you can easily automate creation of member accounts, for example.
-
I'm guessing that's client side, coming from your browser?
-
Thanks for pointing out the discrepancy–now I understand what you are saying, it makes sense. Technically, when you throw your own 404 in a template file, then the system has already determined the page and language, and the URL actually did resolve to something. But the template file decided it wasn't valid for one reason or another. I could have our 404 handler just set the language back to default. But the problem there is that this screws up the folks that are using subdomains to set the language (where a language-specific 404 could be legitimate). So I'm a little hesitant to change anything here. I think it might be best for the developer to decide what behavior they want from their 404 template file. But will have to think on this a little more to see if we can facilitiate anything better.
-
Proof of Concept: ProcessWire Admin Theme Switcher
ryan replied to kongondo's topic in Themes and Profiles
Nice job Kongondo! Some very cool things going on in that video too. -
I'm not seeing that here. What browser/version are you using? Just wondering if we have some browser issue that is causing them to all be checked when you try to edit? Has anyone else run into this issue?
-
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.
- 12 replies
-
- 1
-
- navigation
- redirect
-
(and 3 more)
Tagged with:
-
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, Ryan
- 367 replies
-
- 33
-
It's working for me at least, though I did change some things about the regex's in 0.0.4, so I might need to get more info about the context. What's an example of a tag that isn't working for you? What is the markup that surrounds it in your text? Also can you double check that "Hanna Code" is set as the textformatter for your body field (or whatever field you are using it in) in Setup > Fields > body > details? It should be fine to set your open/close tags to be whatever you want. Though [this] is a fairly common text construct, where as [[this]] is not. So if your [this] ever conflicts with the name of a Hanna Code, that could cause some confusion. As a result, I think it may be preferable to use open/close tags that don't duplicate things you would usually see in written copy.
-
I've never been able to reproduce this, so am wondering if there was some issue in a previous version of PW that was leaving orphaned repeaters (actually, I seem to recall there was for a short period, but my memory isn't great). What version of PW did you start with, and what version are you running now?
-
It sounds like it can't find the $page->editable(); function, which is a function added by an autoload module called PagePermissions.module. This is an unusual error message and not one posted before. It makes me think there may be something up with the PHP version, MySQL version or PHP opcode cache. Can you post what versions you are running of ProcessWire, PHP, MySQL and anything else about the server environment? (maybe a phpinfo() link if you want to PM to me)
-
Ability to sort and search through users in admin
ryan replied to adrian's topic in Wishlist & Roadmap
Why not in the modules directory? -
It looks like you have some unnecessary comparison in there (including a == full object comparison), I recommend this instead: if('en' == $user->language->name) setlocale(LC_ALL, 'en_GB'); Generally when comparing one page to another in ProcessWire, you want to compare the IDs: $page1->id == $page2->id; or if you want to compare the objects then use 3 equals so that it's comparing the instance rather than all the properties (per the way PHP works): $page1 === $page2; But in the case above, it wasn't even necessary to compare any objects since all you needed to compare was the name to the string 'en'.
-
The "missing required value" string is translatable in the file /wire/core/InputfieldWrapper.php. For your language pack, you would add this file as a translation target.
-
Thanks for the screencast Antti. Your cat sounds exactly like mine. This definitely helps clarify for me what the issue is. On the other hand, I'm still at a loss to know what is causing it. It's interesting to see the responses all appear successful and no indication of errors. I hope to experiment more with this soon. Correct me if I'm wrong, but doesn't it seem like the bit of code starting here is where it's getting stuck?
-
Just posted version 0.0.2 that addresses the items above. It also adds an option that lets you specify whether the immediate surrounding HTML tag should be replaced with your Hanna Code output or not. For instance, if your [[hello-world]] Hanna Code output is "Hello World" and is applied to the body copy containing "<p>[[hello-world]]</p>" then you probably don't want it to replace the surrounding <p> tags. Whereas if your Hanna Code outputs block level elements like "<ul><li>Hello World</li></ul>" then you probably do want it to replace the surrounding <p> tags, otherwise you'd end up with some invalid markup like: <p><ul><li>Hello World</li></ul></p>.