Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/21/2020 in all areas

  1. With bells and whistles ?
    3 points
  2. Another possibility: $clean = $sanitizer->purify($dirty, ['HTML.ForbiddenAttributes' => ['style']]);
    3 points
  3. Aren't en and de just the titles names of corresponding language pages? So just change the name of the secondary language page to en.
    2 points
  4. Hey, @fruid! You can try this algorithm of actions: Export all the strings from German language. Import the exported German strings to default language, making it German. Remove all translations from the secondary language, making it untranslated => English. Maybe rename things here and there for the convenience. Change the name of the secondary page to en.
    2 points
  5. Probably a missing </body>.
    2 points
  6. Hey, @strandoo! I think the trick is in the $options argument. Try something like this (or dig into the docs). $sanitizer->purify( $page->summary, array( 'CSS.AllowedProperties' => [] ) );
    2 points
  7. Check if the selector returns any results (echo $posts->count;). Looking here, it seems like you've set your field and selector right, but just maybe you have changed default state for the Toggle Inputfield after it has been created for your posts and it still holds the '' (empty string) value for the most of them if not all. You can look in the database to be sure.
    2 points
  8. Finally gotten a proper Microphone, will drop some multiple videos this week.
    2 points
  9. 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
  10. Just try it. I think it will work. I fixed my advice above a bit to make it more clear.
    1 point
  11. Indeed, that was the reason for the strange behaviour. Thank you, Ivan, for your help.
    1 point
  12. https://www.google.com/search?q=site:processwire.com+change+default+language
    1 point
  13. I've created a PR for the documentation to prevent further confusions: https://github.com/processwire/processwire/pull/177 ?
    1 point
  14. Thanks all. I ended up making a simple function that used used preg_replace and str_replace to strip the styles then nuke the empty spans and paragraphs. The imported html was pretty consistent, so it was pretty straightforward. But I like the idea of using $sanitizer, so I'll try both of those options to see how they work.
    1 point
  15. Yes. If you're doing simple foreach and not sorting items yourself.
    1 point
  16. Greetings from the sunny covid hotspot state of Georgia, where we haven’t left the house since March. And now getting ready for the kids to start a new school year from home with virtual learning. Everyone delivers everything now, so there’s no need to go out to a grocery store anymore (or go anywhere). I live about a mile from the CDC, so our school district has more kids with parents working at the CDC than any other. That gives me some comfort, knowing that I won’t be sending my kids back to school until the experts at the CDC are willing to; when it’s really and truly safe. Though I don’t think it’s going to be safe for a long, long time. The US is a rudderless ship right now, so we just have to ride it out. Thankfully, we’re all staying safe and keeping busy. The kids are building houses in Roblox (an online game addiction they have), we’ve converted our yard to be a summer camp, and converted the basement to be a gym, while we clear more space to start building out a massive N-scale train set—my 3 locomotives still work perfectly, even after 35 years of storage. And I’ve been learning how to manage chlorine and PH in an inflatable kids pool that keeps the family cool in the hot weather. The kids miss school and other activities, my wife misses being at her office and people she works with, and we all miss our friends and family, but it’s the way things are now, and I’m just grateful to have my immediate family home and safe; and in place where we can ride out the storm. I’m also really glad that I can work on the ProcessWire core and modules for pretty much the entire work day, and enjoying coding as much as I ever have; feeling great about where ProcessWire is and where it’s going, thanks to all of you. I’ve been working on the latest ProCache version the entire week, so not many core updates to report today other than some new hooks added to the Pages class (they are hooks that the new ProCache can use as well). I’d hoped to have this version of ProCache finished by now, but I keep finding more stuff to improve, so decided give it another 2 days of work and testing, and if all looks good, it’ll be ready to release, which will be next week. This version is essentially a major refactor, where just about every line of code has been revisited in some form or another. But if you are already a ProCache user, you’ll also find it very familiar. While I don’t have it posted for download today, below is a brief look at what’s new. Completely new .htaccess rules (v2) that take up a lot less space, especially when using multiple hosts, schemes or extensions. Ability to choose .htaccess version (v1 or v2). ProCache now creates an example .htaccess-procache file that you can rename and use or copy/paste from. ProCache now has a built-in URL testing tool where you can compare the non-cached vs. cached render times. New setting to specify how ProCache delivered URLs should respond to trailing vs. non-trailing slashes in URL. Significant refactor that separates all ProCache functions into separate dedicated classes. Improved custom lifespan settings with predefined template lines. Improved behavior settings with predefined template lines and simpler letter (rather than number) based definitions. Ability to specify predefined cache clearing behaviors, specific pages to clear, or page matching selectors, from within the ProCache admin tool. New predefined cache clearing behavior: Reset cache for family of saved page (parents, siblings, children, grandchildren, and all within). New predefined cache clearing behavior: Reset cache for pages that reference saved page (via Page references). New versions of SCSS and LESS compilers. ProCache is completely ProcessWire 3.x native now (previous versions still supported PW 2.x even if 3.x was recommended). Numerous other improvements, fixes and optimizations throughout. I’ve previously mentioned a built-in crawler in ProCache. That part has been moved to a separate module called ProCacheCrawler and will be released a little later in the ProCache board. It was taking a little too much time to develop, so I didn’t want to hold up the rest of ProCache while I developed that. When installed, ProCache communicates with the crawler, identifying and adding URLs to a queue to be crawled and primed for the cache. What it does is pretty cool already, but it needs more time to develop. It’s also something that depends on being run regularly at intervals (like with CRON) so it’s a little bit of a different setup process than the rest of ProCache, which is another reason why I thought I’d develop is as a separate module. I’ll be working more on finishing development of the crawler later in the year, after the next master version of ProcessWire core is released. Next week I'll have the new ProCache version ready for download as well as a new core version on the development branch. It will focus mostly on fixes for issue reports as we continue working towards the next master version. Thanks for reading and have a great weekend!
    1 point
  17. We are using ProCache with nginx on multiple sites. Out of the box it generates rules for .htaccess but those obviously can be translated for nginx. there are also configuration examples in ProCache support (accessible once you bought ProCache)
    1 point
  18. https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ This provides ways of dealing with this limitation, which don't involve replacing one problematic behaviour with another. Maybe one of those fit's your usecase?
    1 point
  19. Thank you @bernhard, @adrian and @kixe for this thread and the very timely thank-you note from Bernard that pointed my in a far more productive direction with regards to my question from yesterday: Had been playing around with hooks to try to achieve my goals but this has made things a whole lot better and a whole lot easier. I kinda figured there had to be a way to hook into the form build process, but without this thread I would have remained lost for much longer as I played around the edges. My very grateful appreciation to all!
    1 point
  20. I have exciting news for you: ? For the release of my module AppApi I have now also finished the section about the Ajax output of Twack. Twack and AppApi work together perfectly. The routing and authentication is done by AppApi. Twack can also render JSON instead of HTML-views. Here you can find out more about routing with AppApi: https://github.com/Sebiworld/AppApi#example-universal-twack-api And here is the description of the Ajax (JSON) output of Twack components https://github.com/Sebiworld/Twack#ajax-output
    1 point
  21. I'm totally agree with your opinion. Even it's beta.. We could start to implement it, use it, find bugs, help to fix it etc. We could start our projects and show something to our clients, get familiar with the system.
    1 point
  22. @kongondo, I am aware that this doesn't help, but I was wondering just how close are we to an announcement? The current COVID situation just brought so many extra ecommerce projects to life, a beta / alpha release could pick up great momentum with the PW community, I'm sure.
    1 point
  23. This is not yet implemented, sorry. But it’s on my list and it will be the next feature to be added. @LostKobrakai @fruid Sorry for my stupidity, this is already implemented in a rudimentary way. You need to add the preinstalled field snipcart_item_custom_fields to your products template. This is a textarea where you can add custom configuration options for your product by using the data-item-custom*-* syntax described here: https://docs.snipcart.com/v2/configuration/custom-fields#product-custom-fields Sample code: data-item-custom1-name="Print label" data-item-custom1-required="true" data-item-custom2-name="Size" data-item-custom2-options="Small|Medium|Large" data-item-custom2-value="Medium" data-item-custom3-name="Gift" data-item-custom3-options="true|false" As I said - this is a rudimentary way to add options to your products. As this method only allows to select/change product options after they are added to the cart, I need to find a reliable way to generate those data-item-custom*-* tags from regular ProcessWire fields so they can be selected directly on the product detail page.
    1 point
  24. Thats great for you. But still nothing in the documentation suggests that it´s obvious just because its a method of $page. It´s says the data is independent of the fields of the page and such lead me to belive i could call $page->meta() from any page and get the data back that i saved. Anyhow, this post is for the people who don´t think its obvious. We are all different. But thank you for your input Markus. have a nice day and happy coding.
    1 point
  25. Hmm, I think it's very clear because it's a method from $page. Everything you do from $page are tied to the page itself.
    1 point
  26. Captain Hook ProcessWire Hooks Cheatsheet http://somatonic.github.com/Captain-Hook/ or on processwire.com http://processwire.com/api/hooks/ I created a simple list with a live search for all hookable methods available in the core. All methods prefixed with a "___" (3 underscores) are hookable. Methods can be made hookable by simply adding those ___ to it. ------------------------- I usually don't really need this, but thought it would be good to have an official list for reference. I used Sublime Text2 for searching and dumb a file with a search for " ___" in wire core. From this list I created a html file with a php script and added some fuzzy live search, similar to the API cheatsheet. If you use Sublime Text 2 like me you can simple use Goto Anything command super+p to show a fuzzy search for files. Enter a class name you want to search for. For example "fields" or "process page edit" you will recieve a list of files found in PW. Highlight the file and typ a @ to see the symbol list quickly or select the file and super+r the get the symbol list.
    1 point
  27. Great! Oh yeah, there's a lot of examples here. Have in mind that importing different languages if just a matter of setting the language before importing the content, nothing more. Let's see, to begin, there's Ryan great case study on importing content from Wordpress And an example of mine, where I show how to import book contents in two languages (en and pt): <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>Import Books</title> </head> <body> <table border='1' width='100%'> <thead> <tr> <th>ID</th> <th>Title</th> </tr> </thead> <tbody> <?php // get access to WordPress wpautop() function //include("./wordpress/wp-includes/formatting.php"); $wpdb = new PDO("mysql:dbname=ricardo-wp4_db;host=localhost", "root", "root", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'")); $books = wire('pages')->get('/books/'); $sql = " SELECT * FROM wp_pods_books "; $query = $wpdb->prepare($sql); $query->execute(); while($row = $query->fetch(PDO::FETCH_ASSOC)) { $book = $books->child("wpid=$row[id]"); // do we already have this book? if(!$book->id) { // create a new post $book = new Page(); $book->template = 'book'; $book->parent = $books; echo "Creating new book...\n"; } $book->of(false); $book->wpid = $row['id']; $book->name = wire('sanitizer')->pageName($row['name'], true); $en = $languages->get("default"); $book->title->setLanguageValue($en, $row['name']); $book->body->setLanguageValue($en, $row['body']); $book->introduction->setLanguageValue($en, $row['intro']); //add more fields if needed $pt = $languages->get("pt"); $book->title->setLanguageValue($pt, $row['name_pt']); $book->body->setLanguageValue($pt, $markdown->convert($row['body_pt'])); $book->introduction->setLanguageValue($pt, $markdown->convert($row['intro'])); //We need to activate portuguese page so it will appear on the front-end $book->set("status$pt", 1); // give detailed report about this post echo "<tr>" . "<td>$row[id]</td>" . "<td>$book->title</td>" . "</tr>"; $book->save(); } ?> </tbody> </table> </body> </html> You can get the gist here. And last but no least, there's Adrian's Migrator module -> https://processwire.com/talk/topic/8660-migrator/?hl=migrator
    1 point
×
×
  • Create New...