Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 10/19/2013 in all areas

  1. If you are having trouble with a module you've created or are using which is no longer functioning with the new admin themes, this is due to some changes in the admin page markup in the new admin templates. The class .ui-widget-content is no longer used in the new themes aside from specific modules such as FormBuilder, and some modules used it as a point of reference in jQuery selector statements which will no longer work. The fix, which maintains backwards compatibility with old themes, is to change the module's .js file to replace this: .ui-widget-content with this: .ui-widget-content, .InputfieldContent Please use this topic to post any other fixes or modules that you encounter issues with relating to JavaScript whilst using the new admin templates.
    2 points
  2. 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.
    1 point
  3. By default, the "Forgot Password" module is not turned on in v2.1. My thought was that lack of such a function is technically more secure (on any site or CMS). Why? Because having such a function active means your password is only as secure as your email (*though see note at end of this message). So I thought we'd start things out as secure as possible and let people adjust it according to their own need. But I'm rethinking that decision, and may change it to be 'on' by default. If you don't already have that "Forgot Password" module installed, it is relatively easy to reset your password with the API. Lets say that you lost the password for your account named 'admin' and you wanted to reset it. Paste this code into any one of your templates (like /site/templates/home.php in the default profile, for example): <?php $admin = $users->get('admin'); $admin->setOutputFormatting(false); $admin->pass = 'yo12345'; // put in your new password $admin->save(); …or if it's easier for you to copy/paste everything on one line, here's the same thing as above on one line: <?php $users->get("admin")->setOutputFormatting(false)->set('pass', 'yo12345')->save(); Replace "yo12345" with the new password you want and save the template. Then view a page using that template (like the homepage, in our example). The password for that account has now been reset, and now you are ready to login. Don't forgot to now remove that snippet of code from the template! Otherwise your password will get reset every time the page is viewed. Once logged in, here's how to install the Forgot Password capability: 1. Click to the "Modules" tab. 2. Scroll down to the "Process" modules. 3. Click "Install" for the "Forgot Password" module. That's all there is to it. You will now see a "Forgot Password" link on your login page. *ProcessWire's "Forgot Password" function is actually a little more secure than what you see in most other CMSs. Not only do you have to have the confidential link in the email, but the link expires in a matter of minutes, and PW will only accept password changes from the browser session that initiated the request. So an attacker would have to initiate the password change request and have access to your email at the same time, making it a lot harder for a man-in-the-middle snooping on your email.
    1 point
  4. ProcessWire 1 had built-in live drafts and versioning. I was particularly proud of these features, but I was always surprised at how little they were used. It turns out that most end-users don't actually write content in the CMS. They tend to go to the CMS when we are ready to publish something. This doesn't define everybody, but I think it defines most CMS end-users. At least that was my experience, repeatedly. As people building sites and applications, I think we tend to assign more importance to these things than they deserve because we ourselves are much more likely to use the CMS as a composition tool than our clients are. This is why live drafts (drafts of already published pages) have not been high priorities as yet in PW2. But there is that small group of clients that these features are actually quite important. That's why this is something that I think belongs on the roadmap. the strategy I'd planned to take was to clone the live page to a new but unpublished page that would become the live draft. It would have an extra field (behind the scenes) indicating that it's a variation of another page. When the editor wants to publish it to be the live page, it would push the unpublished page's data to the live page's data, and then delete the unpublished page. This is important because the original page ID needs to remain in tact. That pushing of data gets a little tricky when it comes to some fieldtypes (like repeaters and files) but that's where the fun will be. Side note, but protected methods are still hookable. The only methods that aren't hookable are those that aren't preceded by 3 underscores.
    1 point
  5. This probably doesn't cover all that you are talking about, but you might find this post interesting, as it gets into how to build simple custom workflows with ProcessWire.
    1 point
  6. ProcessWire originally used ui-widget-content (and ui-widget-header) classes so that it could be themeable with the jQuery UI themeroller. But that was never as popular as I thought it might be, so I'd rather just get away from using those classes for PW's Inputfields and instead leave them just for jQuery UI widgets--this simplifies the CSS quite a lot. I also want to use jQuery UI more consistently with what it was meant for (actual input widgets). So what you see in the new admin theme is replacing ui-widget-header with InputfieldHeader and ui-widget-content with InputfieldContent. This is done by the /site/templates-admin/init.php file, which tells PW's core to use a different markup than what's in the core. The core will switch to these classes as well, but the current default admin theme will continue to use them, as will FormBuilder (where jQuery UI theming is more relevant there). If you've got some jQuery selector that is referring to a ".ui-widget-content" you might want to replace it with ".ui-widget-content, .InputfieldContent" so that you are covering both old and new admin theme. This is better than just replacing ui-widget-content with InputfieldContent. Same goes with ui-widget-header and InputfieldHeader. This is what I've done with any core modules that needed to refer to these wrapper classes in the JS or CSS. Unrelated question: for folks that are complaining about jQuery UI--what alternative do you propose? I'm not aware of any other jQuery based library that can do the same things better or faster than jQuery UI. jQuery UI also brings us a lot of great input types (current and future) that we would not have otherwise. Now that we are moving towards using jQuery UI more consistently with how it's intended to be used, I think it's quite a good long term solution. But if you guys have found something better (that's not extJS or bootstrap) then please share it.
    1 point
  7. @mackski, thanks for your module, please add to the modules directory when you get the chance. To be honest, I can't tell the difference here on my desktop, but then just tried on my notebook and it did seem faster (maybe) but cutting out the animation. I went ahead and added it as a module configuration option for ProcessPageList (Modules > Process > PageList), now available on the dev branch. Try changing it from 200 to 50 ms (or even 0 ms) and see if it makes a difference? It might. I think we just need more people to test it.
    1 point
  8. RequreJS looks good, no doubt. But does PW really need it at the moment? Will modular loading open new possibilities for backend development? I haven't encounter complains here about issues with using scripts when loading them traditionally. Please, correct me if I'm wrong. Yes, potentially there can be a lot of developers but even then even then, I'm sure, BEM is overkill for PW's backend. Yandex use set of tools to automate BEM project's deploying (can be found here http://bem.info/tools/bem/). Or do you mean using this methodology as guidelines and just hand-crafting css the BEM way? If anyone's interested there's a good article about BEM to wrap one's head around this concept (and don't forget the comments section as there's an interesting discussion going on there): http://coding.smashingmagazine.com/2012/04/16/a-new-front-end-methodology-bem/ this one is also good: http://bem.info/articles/yandex-frontend-dev/ and this can help to understand the evolution of the method: http://bem.info/method/history/ Please note, Yandex has more than 150 frontend-developers and they create super-complex projects that are sometimes interconnected and so the context where the "block" can potentially be used is often unknown so this methodology suits their projects well. I think that more concise "backend's frontend" development guidelines would be more suitable. Or maybe limit BEM's usage to certain rules of its simplified version. IMO the real reason of the backend "outdated" feel at the moment is primarily jQueryUI, which I guess was chosen by Ryan as it provides quick and dirty way to have a very decent UI though not extremely sexy. Sex appeal is a good thing if there's a substance behind it or you are risking to end up with one night stand) Also wanted to mention that to me it's not totally fair to compare PW to Craft and Anchor as they are blogging platforms that have more rigid structure and definite purpose so they can afford to have beautiful interface without thinking about flexibility and adaptability. Craft looks good and can be a role model for PW but what's interesting it doesn't use any progressive frontend paradigms. It seems they have their own homegrown "Garnish UI toolkit" and parts of jQuery UI. Don't know much about this CMS so can't really say anything about its flexibility compared to PW. The best is the enemy of the good. Just a Russian saying) I have a parallel with PHP here: it seems like everybody hate it, but at the same time it's the most used web-development language out there. It works just everywhere and it's possible to start making sh*tty websites in minutes! But then there's a depth and space to grow. So what changes would be required to PW core to make it work the proper way? Can it be injected as an option via PW module? As far as I understand, jQuery is AMD compatible but not all its plug-ins so then one have to shim them. Is it correct? Oh, sorry. I see what you mean now. Does it have to be a js-testing framework of some kind? Yeah, agree. Alternative totlal rewrite of backend is too much. It can be reasonable only if there's a plan to migrate to such new backend in future. I think your idea about a new theme is great. It would definitely help understand what is needed for a better backend. Thank you for your thorough answers and interesting ideas!
    1 point
  9. Ryan these admin themes look great! I am trying them out right now ;-) Excited to see what opportunities this opens up for even more admin theme development. I have always wanted to try my hand at one, but found the jQuery UI classes / inputfields etc. very complex and a bit daunting to create a clean and simple admin theme. Excited to see how the new structure will simply things - especially now that you're using SASS!
    1 point
  10. Hi Nik, thanks to clarify. You may have a look into some existing modules that deal with images and textareas: http://modules.processwire.com/categories/photo-video/ for example the ImageInterceptor. Maybe you find code that preg_matches all imagelinks of textareafields. somas Images Manager also deals with textarea fields in some parts: https://github.com/somatonic/ImagesManager/blob/master/ImagesManagerParser.module And if you don't get it to work, please come back here to ask for help. To enhance the script to also support images in textareas is usefull for many other users I think. I think the new code can be go here: // ... // determine which files are valid for the page $valid = array(); foreach ($page->template->fieldgroup as $field) { if ($field->type instanceof FieldtypeTextarea) { // here comes the code to parse the content of textarea for images links that resides in assets/files/idOfCurrentPage/ // all found images must be added to the array $valid! // ... } elseif ($field->type instanceof FieldtypeFile) { // here follows the existent code for files and images fields
    1 point
  11. Thank you for your help, ryan! I hadn´t realized that I would have to rename the module tor run in /site/modules/, that´s why it didn´t work from there. Great work the whole CMS, I like it a lot!
    1 point
  12. Another small update. Images now have "video-" prefixed to their filenames. This allows the images field to also store user uploaded images, in addition to the video images. When the module checks for videos that are no longer present on the page, it will no longer delete images that the user has uploaded, unless of course they happen to have "video-" in the filename as well. Hopefully this is a reasonable approach to this. Let me know if it causes anyone any problems and I'll revisit.
    1 point
  13. Glad you like it. Ryan's a code genius so it's better than I was hoping for.
    1 point
  14. CMSCritic just launched another part of the site which is a directory of CMS products (powered by ProcessWire of course). Hope you guys enjoy: http://www.cmscritic.com/dir/ While you are there, maybe click the "like" button for ProcessWire if you are so inclined.
    1 point
  15. Sounds awesome. Let's make it happen!
    1 point
  16. This hasn't been asked, but wanted to cover how the permissions and publish workflow work on the site. It has a very simple, though nice setup, where authors can submit new posts but can't edit already published posts, nor can they edit unpublished posts by other authors. It enables Mike to have full control over any content that gets published on the site, while still allowing easy submission and edits for the authors. Post workflow All of the authors have a role called "author" with page-edit permission. On the "post" template, the boxes for "edit" and "create" are checked for this "author" role. This site also makes use of the page-publish permission, which is an optional one in ProcessWire that you can add just by creating a new permission and naming it "page-publish". Once present, it modifies the behavior of the usual page-edit permission, so that one must also have page-publish in order to publish pages or edit already published pages. The "author" role does not have page-publish permission. As a result, authors on the site can submit posts but can't publish them. Nor can they edit already published posts. In this manner, Mike has final say on anything that gets posted to the site. Post ownership The default behavior in ProcessWire is that the Role settings control all access... meaning all users with role "author" would be able to do the same things, on the same pages. In this case, we don't want one author to be able to edit an unpublished/pending post created by another author. This was easily accomplished by adding a hook to /site/templates/admin.php: /** * Prevent users from being able to edit pages created by other users of the same role * * This basically enforces an 'owner' for pages * */ wire()->addHookAfter('Page::editable', function($event) { if(!$event->return) return; // already determined user has no access if(wire('user')->isSuperuser()) return; // superuser always allowed $page = $event->object; // if user that created the page is not the current user, don't give them access if($page->createdUser->id != wire('user')->id) $event->return = false; }); Planned workflow improvements Currently an author has to let Mike know "hey my article is ready to be published, can you take a look?". This is done by email, I'm assuming. An addition I'd like to make is to add a Page reference field called "publish_status" where the author can select from: DRAFT: This is a work in progress (default) PUBLISH: Ready for review and publishing CHANGE: Changes requested - see editor notes DELETE: Request deletion Beyond that, there is also an "editor_notes" text field that only appears in the admin. It's a place where Mike and the author can communicate, if necessary, about the publish status. This editor_notes field doesn't appear on the front-end of the site. All this can be done in ProcessWire just by creating a new field and adding these as selectable page references. That's easy enough, but I want to make it so that it notifies both Mike (the reviewer) and the author by email, every time there is a change in publish status or to the editor_notes. This will be done via another hook in the /site/templates/admin.php: wire()->addHookAfter('Page::saveReady', function($event) { // get the page about to be saved $page = $event->arguments(0); // if this isn't a post, don't continue if($page->template != 'post' || !$page->id) return; // if this post wasn't made by an "author" don't continue if(!$page->createdUser->hasRole('author')) return; $subject = ''; $message = ''; if($page->isChanged('publish_status') || $page->isChanged('editor_notes')) { // the publish status or editor notes have changed $subject = "CMSCritic post publish status"; $notes = $page->isChanged('editor_notes') ? "Notes: $page->editor_notes" : ""; $message = " Title: $page->title\n URL: $page->httpUrl\n Status: {$page->publish_status->title}\n $notes "; } else if($page->isChanged('status') && !$page->is(Page::statusUnpublished)) { // page was just published $subject = "CMSCritic post published"; $message = "The post $page->httpUrl has been published!"; } if($message) { $reviewer = wire('users')->get('mike'); $author = $page->createdUser; mail("$reviewer->email, $author->email", $subject, $message); $this->message("Email sent: $subject"); } }); Mike, if you are reading this, does this sound useful to you?
    1 point
  17. Let that sink in. Ryan, I can't absorb the knowledge you share fast enough — thank you!
    1 point
  18. This is great stuff! I'd love to see some additional detail about how you managed images, topics, tags and assigning authors. I have a few big sites to migrate. I'd also love to see your take on managing page references for related content, if applicable. Thanks for sharing! -Brent
    1 point
  19. I feel like a padawan learning from Yoda.. ;P #lamestarwarsreferenceforthewin
    1 point
  20. 1 point
  21. You can always reset your password just by pasting this temporarily into any one of your templates, and then viewing a page that uses the template: $u = $users->get('admin'); // or whatever your username is $u->of(false); $u->pass = 'your-new-password'; $u->save();
    1 point
×
×
  • Create New...