Search the Community
Showing results for 'SEO' in topics.
-
Hello community! I want to share a new module I've been working on that I think could be a big boost for multi-language ProcessWire sites. Fluency is available in the ProcessWire Modules Directory, via Composer, and on Github Some background: I was looking for a way for our company website to be efficiently translated as working with human translators was pretty laborious and a lack of updating content created a divergence between languages. I, and several other devs here, have talked about translation integrations and the high quality services now available. Inspired by what is possible with ProcessWire, I built Fluency, a third-party translation service integration for ProcessWire. With Fluency you can: Translate any plain textarea or text input Translate any TinyMCE or CKEditor (inline, or regular) Translate page names/URLs Translate in-template translation function wrapped strings Translate modules, both core and add-ons Installation and usage is completely plug and play. Whether you're building a new multi-language site, need to update a site to multi-language, or simply want to stop manually translating a site and make any language a one-click deal, it could not be easier to do it. Fluency works by having you match the languages configured in ProcessWire to those offered by the third party translation service you choose. Currently Fluency works with DeepL and Google Cloud Translation. Module Features Translate any multilanguage field while editing any page. Translate fields in Repeater, Repeater Matrix, Table, Fieldset Page, Image descriptions, etc. Translate any file that added in the ProcessWire language pages. It's possible to translate the entire ProcessWire core in ~20 minutes Provide intuitive translation features that your clients and end-users can actually use. Fluency is designed for real-world use by individuals of all skill levels with little to no training. Its ease-of-use helps encourage users to adopt a multilanguage workflow. Start for free, use for free. Translation services supported by Fluency offer generous free tiers that can support regular usage levels. Fluency is, and will always be, free and open source. Use more than one Translation Engine. You may configure Fluency to use either DeepL, Google Cloud Translation, or both by switching between them as desired. AI powered translations that rival humans. DeepL provides the highest levels of accuracy in translation of any service available. Fluency has been used in many production sites around the world and in commercial applications where accuracy matters. Deliver impressive battle-tested translation features your clients can count on. Disable translation for individual fields. Disable translation for multilanguage fields where values aren't candidates for translation such as phone numbers or email addresses Configure translation caching. Caching can be enabled globally so that the same content translated more than once anywhere in ProcessWire doesn't count against your API usage and provides lightning fast responses. Set globally ignored words and text. Configure Fluency to add exclusionary indicators during translation so that specific words or phrases remain untranslated. This works either for specific strings alone, or present in other content while remaining grammatically correct in translation. Choose how translation is handled for fields. Configure Fluency to have buttons for either "Translate from {default language}" on each tab, or "Translate To All Languages" to populate every language for a field from any language to any language you have configured. No language limits. Configure as few or as many languages as you need. 2, 5, 10, 20 language website? Absolutely possible. If the translation service you choose offers a language, you can use it in ProcessWire. When new languages are introduced by third parties, they're ready to use in Fluency. Visually see what fields and language tabs have modified content. Fluency adds an visual indication to each field language tab to indicate which has different content than when opening the edit page. This helps ensure that content updated in one language should be updated in other languages to prevent content divergence between languages. Render language meta tags and ISO codes. Output alt language meta tags, add the current language's ISO code to your <html lang=""> attribute to your templates that are automatically generated from accurate data from the third party translation service. Build a standards-compliant multi-language SEO ready page in seconds with no additional configuration. Render language select elements. - Fluency can generate an unordered list of language links to switch between languages when viewing your pages. You can also embed a <select> element with JS baked in to switch between languages when viewing your pages. Render it without JS to use your own. Manage feature access for users. Fluency provides a permission that can be assigned to user roles for managing who can translate content. Track your translation account usage. View your current API usage, API account limit, and remaining allotment to keep an eye on and manage usage. (Currently only offered by DeepL) Use the global translation tool. Fluency provides translation on each field according to the languages you configure in ProcessWire. Use the global translation tool to translate any content to any language. Use Fluency from your templates and code. All translation features, usage statistics, cache control, and language data are accessible globally from the $fluency object. Perform any operation and get data for any language programmatically wherever you need it. Build custom AJAX powered admin translation features for yourself. Fluency provides a full RESTful API within the ProcessWire admin to allow developers to add new features for ProcessWire applications powered by the same API that Fluency uses. Robust plain-language documentation that helps you get up to speed fast. Fluency is extremely easy to use but also includes extensive documentation for all features both within the admin and for the Fluency programming API via the README.md document. The module code itself is also fully annotated for use with the ProDevTools API explorer. Is and will always be data safe. Adding, upgrading, or removing Fluency does not modify or remove your content. ProcessWire handles your data, Fluency sticks to translating. Full module localization. Translate Fluency itself to any language. All buttons, messages, and UI elements for Fluency will be presented in any language you choose for the ProcessWire admin. Built for expansion. Fluency provides translation services as modular "Translation Engines" with a full framework codebase to make adding new translation services easier and more reliable. Contributions for new translation services are welcome. Fluency is designed and built to provide everything you need to handle incredibly accurate translations and robust tools that make creating and managing multi-language sites a breeze. Built through research on translation plugins from around the web, it's the easiest and most friendly translation implementation for both end users and developers on any CMS/CMF anywhere. Fluency complements the built-in first class language features of ProcessWire. Fluency continues to be improved with great suggestions from the community and real-world use in production applications. Big thanks to everyone who has helped make Fluency better. Contributions, suggestions, and bug reports welcome! Please note that the browser plugin for Grammarly conflicts with Fluency (as it does with many web applications). To address this issue it is recommended that you disable Grammarly when using Fluency, or open the admin to edit pages in a private window where Grammarly may not be loaded. This is a long-standing issue in the larger web development community and creating a workaround may not be possible. If you have insight as to how this may be solved please visit the Github page and file a bugfix ticket. Enhancements Translate All Fields On A Page Compatibility with newest rewrite of module is in progress... An exciting companion module has been written by @robert which extends the functionality of Fluency to translate all fields on a page at once. The module has several useful features that can make Fluency even more useful and can come in handy for translating existing content more quickly. I recommend reading his comments for details on how it works and input on best practices later in this thread. Get the module at the Github repo: https://github.com/robertweiss/ProcessTranslatePage Requirements: ProcessWire 3.0+ UIKit Admin Theme That's Fluency in a nutshell. The Module Is Free This is my first real module and I want to give it back to the community as thanks. This is the best CMS I've worked with (thank you Ryan & contributors) and a great community (thank you dear reader). DeepL Developer Accounts In addition to paid Pro Developer accounts, DeepL now offers no-cost free accounts. All ProcessWire developers and users can use Fluency at no cost. Learn more about free and paid accounts by visiting the DeepL website. Sign up for a Developer account, get an API key, and start using Fluency. Download You can install Fluency by adding the module to your ProcessWire project using any of the following methods. Method 1: Within ProcessWire using 'Add Module From Directory' and the class name Fluency Method 2: Via Composer with composer require firewire/fluency Method 3: Download from the Github repository and unzip the contents into /site/modules/ Feedback File issues and feature requests here (your feedback and testing is greatly appreciated): https://github.com/SkyLundy/Fluency/issues Thank you! ¡Gracias! Ich danke Ihnen! Merci! Obrigado! Grazie! Dank u wel! Dziękuję! Спасибо! ありがとうございます! 谢谢你
- 233 replies
-
- 43
-
-
-
- translation
- language
-
(and 1 more)
Tagged with:
-
I would like to switch from TinyMCE to CKEditor. But I was stopped because I cannot implement a custom block element, like <div>. I need an extra element to be able to style some boxes within the content, normally for highlighting something, or a note. I ckeditor I was using a div and was able to allow it and even able to add a makeDiv button in the tools menu. Switching to CKEditor failed. I could not find out how to add a button for a custom tag. Is there a way? The disadvantage of using a <div> is, that the convenient filtering of divs had to be deactivated. While at it, I was thinking of using some other standard block element, like <section>, instead. (SEO does not matter). And the section got filtered by HTMLpurifier anyway. I found no way in Processwire to train HTMLpurifier to accept <section> as an additional tag. My workaround could be the use of HannaCode. But that is not as convenient.
-
Hi everyone. And thanks in advance for help. I'm considering switching to Processwire from Wordpress for small to medium websites. But I need your advice on business strategy. I already did 1 website on Processwire and I like the CMS. I'm not professional dev (Product design and frontend background) but it wasn't hard to make small website. I'm wondering what is your business strategy for maintenance of PW sites? What does maintenance plans include? For WP sites I have Basic plan which includes WP and plugin updates, security and backups. For Advanced plans I add ongoing SEO, performance optimization and content updates. Since I read that PW and modules don't need updates very often and there are no security issues, does that mean my basic plan is not needed for PW? Because, if it is obsolete that would be less monthly recurring revenue for my business. I’d really appreciate your advice.
-
Thanks for reply. I also dislike doing maintenance work. After all, my main job is Product designer. So, creative work is my passion. But, I am thinking of going full freelance and monthly recurring revenue is important to me. And clients need those basic updates so it is easy to sell. It doesn't take that much time to do maintenance work (it can be automated), majority of time is still creative work. I guess, I'll have to focus my plans more on SEO, Analytics and Content updates. It would be nicer to decrease some of those maintenance work if it is possible.
-
[SOLVED] How do I sort children pages by template?
virtualgadjo replied to Alpina's topic in Getting Started
Hi @AndZyk actually, the first thing i wuld think about is that when your post number grows, the two categories and tags folders will be a little less easy to find and modify but, honestly only an admin usability problem for me, the important thing is what you say about this url thing and not allowing url segments (no security issue with them if you use a simple regex to say what you allow and a simple die if somebody puts something taht doesn't match nor is in the retreived/checked arrays) as, i must be used to too complicated demands of categories and/or tags selections and most of all, even with simple ones the necessity to allow mutiple categories for one post, if only one category allowed by post i would simplify the structure with the one i showed in the previous one as yours 😄 in the end, when it comes to seo the real important thingis this only one url for a blog post whichever list/category/tag you're linking from and you are absolutely right, there no right or wrong structure, the only question for me is which one makes things the most simple to use depending on what you need (and, in a certain way, on who will use it, you and i are "a liitle" used to pw and its incredible features, it's not always the case of all the people we make website for and i tend to try and give them the most obvious admin i can, their love and addiction to pw will grow and let me be maybe a little more pw geeky along the website life 😄 Have a nice day -
[SOLVED] How do I sort children pages by template?
virtualgadjo replied to Alpina's topic in Getting Started
hi, well, first i must say for many reasons (seo, menu building, sitemap and so on) i wouldn't structure e blog this way, instead i would put the two parents (categories and tags) outside the main blog parent page and then use them with page reference and url segments but well... now if you want to easily cheat, blog posts are usually ordered by reverse chronological order, then guess, add your date field to the, categories and tags parent pages and set them with a last century date 😄 job done, those two parent pages children can still be ordered the way you want have a nice day just modified to say @AndZyk was faster to have the same king of idea while i was writing 🙂 -
Hey all. This year I launched a new website for Modernism Week, a nonprofit located in Palm Springs, California that celebrates midcentury architecture and hosts engaging educational annual events. https://modernismweek.com This is my most complex ProcessWire website to date and I wanted to share some of the things that were implemented behind the scenes. Project Requirements When I met with stakeholders at the organization, their website was a 5 page brochure site with minimal content and links to buy tickets. The ticketing platform they use is robust, but doesn't provide an experience you would expect from an event this significant. The project was a blank canvas and after an assessment I provided a list of features that would benefit the organization, address shortcomings, identify opportunities, and support future growth and business goals. These included: A vibrant design that mirrors the architecture they celebrate to create a more inspiring experience for visitors Implement stronger adherence to brand standards and new branding to celebrating the 20th anniversary of Modernism Week Feature dedicated pages for events, activities, and offers Promote event and organization sponsors Challenges At first glance, the site looks pretty standard however, it features a full event section that displays everything visitors can do at each activity during the 10 day event. Leading up to and during the event, the information changes to stay up to date with newly added activities and updates to locations, ticket availability, descriptions, pictures, etc. During planning we identified unique challenges. Annual site growth of over 300 new pages for activities and events on top of any normal site growth Reduce the amount of work needed to initially enter the information for each event Add complexity without compounding increases in work to maintain actively changing information Maintain content accuracy by mirroring regularly changing information that is available on pages located on the ticketing platform Synchronize event and activity information with a ticketing platform that does not provide a public API Allow visitors to browse activities when previewing them prior to the ticket sales launch day Keep cross-referenced content across the site, such as promoted activities and offers, current Provide an integrated workflow between people managing the ticketing platform and maintaining the website Improve SEO and implement structured data derived from cumulative page content for all activities Ensure that all activities in an event are centrally searchable and filterable not only when using whole-website search, but within a dedicated activity search feature for each event Deliver a fast and performant experience for visitors Manage server and database loads to prevent overloading resources or rendering the site or admin unusable Oh, and the deadline was the day tickets went on sale, on a Friday, where traffic spikes to thousands of users per minute. We broke every rule in the book. Manually entering and managing each page is a task too large for one or even two people. Events and activities must be added to the site while they are simultaneously added to the ticketing platform in the months leading up to events. The amount of overhead for project management and task delegation would overburden a team already working tirelessly to successfully put on an event of this size. The timeline to design, build, test, and deploy the site was only 4 months. The solution was automation. The Application Layer The only way that the site could effectively and successfully be managed accurately is to automate tasks and consolidate complex processes into single-button clicks and cronjobs. This is where ProcessWire really flexes its muscle. The API and practically limitless flexibility in custom implementation provided the ability to build out everything that was required. The site has its own custom module that provides an interface for executing back end work to synchronize data between the ticketing platform and the site. The site also employs over 60 custom hooks and hook-supporting classes (some of them I shared here) that make chained and background actions possible. As mentioned, the ticketing platform does not provide an API so there was no conventional method to easily retrieve data. This means that all of the data pulled from the ticketing platform is pulled via web scraping. To more easily and accurately retrieve this data, I built a separate internal tool for the ticketing team where they enter activity information, pre-formats it for them to a design, and adds encoded data attributes on elements in the markup that helps make scraping more accurate. The data this retrieves is then parsed, cleaned, and fed through processors that compare, convert, and input into ProcessWire where pages are created or updated. The RoachPHP library facilitated scraping and it's great. If you're interested in web scraping, it's a great tool. Performance There are a lot of relatively expensive operations required to sync activities in large numbers. The way that unique data is stored, such as ticket pricing, availability, and schedule requires more complex fields like RepeaterMatrix. Often the data required to calculate or display information makes using selectors alone to query/filter data difficult, inefficient, or not possible. I chose to use RepeaterMatrix fields over individual pages because of the the reduction in complexity when managing the schedule, pricing, and ticket availability. Some activities occur once, other occur multiple times per day over the course of 10 days. I fully expect that the amount and complexity of information stored will increase over time due to future improvements and feature implementations. Being able to manage all of this data, compare it, and adjust it one one page is important for accuracy and time spent on task when manual edits need to be made and cross referencing multiple pages would be cumbersome. Regardless of page or field choices, nested loops of up to 200 pages involves significant server loads, especially when that process may involve resizing images to multiple dimensions and generating required output data from multiple fields. A good example is the live search feature. Thousands of visitors querying the database is not feasible for even the reasonably powered VPS it's on and I wanted to create something closer to "live search". To address the workload required, the search modal is empty on page load and a separate page is used on the back end that is separately cached using ProCache. This allows one page to serve modals being cached with every page. If the modal content was cached with all activity pages then event changing the ticket availability, title, summary, image, etc. would require expiring the cache of over 200 pages that have complex data. Opening the "All Activities" modal executes an htmx request, and the cached modal contents are loaded near instantaneously. The slowest part of that is the animating the loading animation in/out. Underneath the hood, the live search/filter uses keywords that are embedded in data attributes on each row. These keywords are taken from multiple fields, parsed, stripped/prepared for filtering, and combined. Doing this in a loop is noticeably slow to perform every time the page is called. Filters for categories. sold out, activities happening today, search by keyword, and viewing activities on a specific day is all done on the client side and require calculating per-day ticket availability, first and last occurrence dates, and during the event whether an activity has passed- which when displayed overrides a sold out status. Expiring this page is still an expensive task and calculating the dates, times, and statuses to produce the data needed has taken up to an unacceptable 6+ seconds. If too many pages are not cached this can cause performance issues for multiple users on multiple pages. It's not just about front end performance, it's also important to think about admin performance. To remedy this, I implemented a type of field pre-caching. Front end performance is entirely dependent on ProCache. Without it the server load would be far too high and performance hits would affect time on site and ticket sales. Overall, the goal is to touch the database as little as possible. ProcessWire is indeed fast, but there are always limitations across many different moving parts. Synchronizing Event/Activity Data Importing data can involve multiple background HTTP requests, reading and updating high numbers of pages and dozens of repeater pages and fields. Syncing is done by scraping pages on the ticketing platform. There may be instances that after analyzing the data retrieved does not require updating a page because there were no changes. If there are no changes then the page shouldn't be saved. Rather than check all of the fields for changes in values, data is hashed and stored for later comparison. Every activity page can be updated from the ticketing platform via either the main event page that summarizes useful data, or the individual ticketing page. Sync can be enabled or disabled for the activity entirely, if it is enabled, then sync can be toggled on/off for every individual field in case local management of data is preferred. The retrieved data is hashed and compared to the page hash value. If the last sync datetime is older than the time limit set in the event settings, or the hash does not match, the the page is updated and saved. If not, the page is then ignored. This saves a lot of processing time, power, and database hits while maintaining state. Example of an activity page. Ticketing occurrence data is parsed and run through regex to extract individual data and formatting for dedicated fields. The three fields on the bottom are taken from the Instances Summary field when parsed on import. Looping within loops to create renderable values for the front end is taxing. But using custom page classes to organize business logic with methods that handle processing, formatting, and caching data are immensely powerful and make short work of interacting with pages when bespoke functionality is required. Because of the high number of hooks on various operations, the ability to save quietly without triggering them is used extensively as an efficiency measure and also to prevent unwanted side effects when operations do not call for them. Sync uses this feature the most. Field Pre-Caching A global field called "Field Support Metadata" is used to move significant amounts of data processing from the page view event over to the page save event. This takes the multiple field value processing and image sizing out of loops for rendering and takes the extra time on the back end operations side. The activity page example above uses this method almost entirely for front end rendering and makes pre-calculated values accessible in one field. This was inspired by the SearchEngine module by @teppo that uses a single field to cache values for search. It really saved my bacon 🤣 big thanks! Page data is saved as JSON and then retrieved on demand. Metadata includes a wide array of values. Because it's JSON, adding additional data later is very easy. While i could have used the the $page->meta feature, being able to check the contents of the field visually in the admin without dumping or testing values has been useful. All of that data is processed and stored on a Pages::saveReady hook using methods defined on the custom page class. This process is skipped entirely if the activity has no occurrences scheduled. <?php wire()->addHookAfter('Pages::saveReady(template=event_activity)', function(HookEvent $e) { $page = $e->arguments('page'); $occurrences = $page->activity_occurrences; if (!$occurrences->count()) { return; } $firstOccurrenceDate = $page->firstOccurrenceDate(fromMetadata: false); $lastOccurrenceDate = $page->lastOccurrenceDate(fromMetadata: false); $timezone = wire('config')->timezone; $allOccurrenceDateTimeStart = array_map( fn ($date) => CarbonImmutable::parse($date)->setTimezone($timezone)->toDateTimeString(), $occurrences->explode('date_time_start'), ); $page->pushToFieldMetadata([ 'totalOccurrences' => $occurrences->count(), 'totalInstances' => array_sum($occurrences->explode('number')), 'firstOccurrenceDate' => $firstOccurrenceDate?->toDateTimeString(), 'lastOccurrenceDate' => $lastOccurrenceDate?->toDateTimeString(), 'isSoldOut' => $page->isSoldOut(fromMetadata: false), 'isUncategorized' => $page->isUncategorized(fromMetadata: false), 'ticketingUrl' => $page->ticketingUrl(fromMetadata: false), 'activityDates' => $page->activityDates(fromMetadata: false), 'allOccurrenceDateTimeStart' => $allOccurrenceDateTimeStart, 'listFilterKeywordString' => $page->listFilterKeywordString(fromMetadata: false), 'structuredData' => $page->getStructuredData( includeOffers: false, includeLocation: true, includeOrganizer: false, includeDescription: true, includeImages: true, includeParentEvent: false, fromMetadata: false, ) ]); }); Editing one hook makes adding or modifying pre-processed data available everywhere on demand at runtime via one field which is pretty nice. The 'fromMetadate' set to true will return cached values, if false, it will return values that are generated from field data. This allows for the same method to be used anywhere in the application with control over the data source. Any data that can be memoized is via the page class so that if a value is calculated at runtime (wasn't yet cached to metadata yet for some reason) and accessed more than once during a request/response loop then it will be pulled from memory. Granular ProCache Control Not every field changed should invalidate cache. For example, if sync is toggled on/off, that does not affect the rendered page so the cache for that page should not be cleared. Page save events for activity pages fire ProCache hooks that analyze which fields on the page have changed and only clear the cache if it will affect pages that rely on it. Activities are referenced elsewhere and added as featured items to help promote them on other parts of the website. So if an activity page is saved where the title is changed and that activity is featured on various other pages, that could clear that page, the Event page, the All Events modal page, the category page for that activity, and the home page. Using hooks allows for very specific cache operations that cannot be configured in the admin UI. Example of how activities can be featured and promoted in many places elsewhere on the site. There's a little more about this on a ProCache support thread I posted to discuss efficient caching strategies. Thanks to @ryan for the insight and recommendations 👍 Protip: Pre-Warming Your Cache ProCache is excellent but it can't cache a page until it's visited. You can seriously speed this up by using quicklink. Quicklink watches the page for links as they're scrolled into view where the browser makes a background prefetch request and caches the HTML document locally. If ProCache has already cached the page, the background request/response is so quick that the browser pulls every link on the screen near instantly. When you click on the link, you get response times like this. Which aren't response times, they're the speed that the browser cache operates locally. Total insanity. And the nice thing is that if they don't visit the page, they've kindly cached it for everyone else. Thank you, kind visitor. Dynamically Generated Content One of the features built in thanks to hooks is automatic content generation. Examples are the "more in category" and "you might also be interested in" type suggestions at the bottom of every activity page. This is designed to help visitors explore other things that they might like. This provides a great navigation experience on the site that can also increase ticket sales. These are automatically selected when a page is created during first sync/import using a Page::saveReady hook. Activities are given tags on the ticketing platform to help organize them. These are pulled during import, analyzed, and activities are automatically selected by ranking the most similar according to matching tag count. This boosts usable content on the page with zero work by a human. While they're automated, they can also be changed or re-ordered when editing an activity page. You can delete any or all of them and they'll be repopulated with new activities when the page is saved. The code that populates activities lives in a custom page class method for the activity so it can be called anywhere. Future Features The site as it stands currently is not in it's final state. Some planned features had to be cut due to prioritizing event and activities which are revenue generating. Future features include: A full robust blog that delivers a magazine-like experience which also requires importing posts dating back to 2015 from a separate WordPress site. An improved workflow that provides partners the ability to submit information about their activities for events via forms, integrates that tool the ticketing team now uses while adding the ability to save their progress and pre-create ProcessWire activity pages pages to reduce the overhead of major data import operations on the site. Adding a separate section that contains a web app that attendees can use during activities for additional educational information while at the event Create an "itinerary" feature that lets visitors browse the site and add activities that they plan to purchase tickets for. Use this feature to send email reminders when tickets are available and marketing leading up to the event Possibly implement an event calendar that lets people get an overview of the schedule and avoid schedule conflicts. A "Past Events" section of the site where events are moved to but can still be browsed. Where It Can Be Improved There are places in the code that should be using selectors rather than pulling larger numbers of pages since selectors and queries are optimized and efficient. Having a short deadline takes a toll on planning and execution. Offloading more to cronjobs. I had to keep a lot of operations manually triggered that I want truly handled by background automation but I needed an opportunity to analyzej real-world performance in production and have the chance to review the quality and accuracy of the data imported. Scraping is great, but sometimes it takes fine tuning to get things right. Not to mention the amount of regex used vs my skills with regex... More robust features for browsing activities. More filters and options to choose which activities to browse. A lot of great ideas have come out of living with the site and there are some basics I'd like to see implemented. At its heart, this site is still a website but ProcessWire really shines when it's given a big job. It eliminates the need to consider a full application framework and is faster to develop for when you need strong core features for content management. As you can imagine, trying to build something like this on a platform that is not as developer oriented as ProcessWire is would be very not fun and the high quality Pro modules, community modules, and outstanding API made it possible to plan and execute without compromise. If you've made it this far, thanks for reading!
- 1 reply
-
- 16
-
-
-
Combine RPB resources such as JS and CSS
herr rilke replied to herr rilke's topic in RockPageBuilder
ok, wondering, why minification is still on, but found in _init.php <?php namespace ProcessWire; include_once(__DIR__ . '/_functions.php'); /** @var RockFrontend $rockfrontend */ $htmlLang = "de"; $rockfrontend->styles() ->add('/site/templates/uikit/src/less/uikit.theme.less') ->add('/site/modules/RockFrontend/less/defaults.less') ->addDefaultFolders() ->minify(true); $rockfrontend->scripts() ->add('/site/templates/uikit/dist/js/uikit.min.js') ->add('/site/templates/uikit/dist/js/uikit-icons.min.js') ->add('/site/templates/scripts/main.js', 'defer') ->minify(true); $seo = $rockfrontend->seo(createManifest: false) ->title($page->title .... setting minify(false) results in: ... <link rel='stylesheet' href='/site/modules/RockFrontend/topbar/topbar.min.css?m=1730134652'><script>var RockFrontend = {"rootUrl":"\/","defaultVspaceScale":"0.66"}</script> <!-- StylesArray 'main' --> <!-- These comments are only visible when $config->debug = true; --> <!-- loading /site/templates/RockPageBuilder/blocks/Accordion/Accordion.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/ArtikelMitBild/ArtikelMitBild.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/CallToAction/CallToAction.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/Features/Features.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/Gallery/Gallery.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/Hero/Hero.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/Quotes/Quotes.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/Spalten/Spalten.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/RockPageBuilder/blocks/Text/Text.less (RockPageBuilder.module.php:193) --> <!-- loading /site/templates/uikit/src/less/uikit.theme.less (_init.php:13) --> <!-- loading /site/modules/RockFrontend/less/defaults.less (_init.php:14) --> <!-- loading /site/templates/less/_global.less (_init.php:15) --> <!-- loading /site/templates/less/_variables.less (_init.php:15) --> <link href='/site/templates/bundle/main.css?m=1737361090' rel='stylesheet'><!-- LESS compiled by RockFrontend --> <link href='/site/modules/RockFields/assets/RockPageBuilder.min.css?m=1728917625' rel='stylesheet'><!-- RockPageBuilder.module.php:440 --> <link href='/site/modules/RockFields/assets/frontend-loggedin.min.css?m=1728917625' rel='stylesheet'><!-- RockPageBuilder.module.php:447 --> <link href='/site/templates/RockPageBuilder/blocks/Accordion/Accordion.css?m=1728929117' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/RockPageBuilder/blocks/Accordion/Accordion.min.css?m=1737183931' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/RockPageBuilder/blocks/ArtikelMitBild/ArtikelMitBild.css?m=1729433195' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/RockPageBuilder/blocks/ArtikelMitBild/ArtikelMitBild.min.css?m=1737183931' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/RockPageBuilder/blocks/Hero/Hero.css?m=1729243731' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/RockPageBuilder/blocks/Hero/Hero.min.css?m=1737183931' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/RockPageBuilder/blocks/Text/Text.css?m=1729321204' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/RockPageBuilder/blocks/Text/Text.min.css?m=1737183931' rel='stylesheet'><!-- RockPageBuilder.module.php:193 --> <link href='/site/templates/styles/admin.css?m=1731420429' rel='stylesheet'><!-- _init.php:15 --> <link href='/site/templates/styles/admin.min.css?m=1737183931' rel='stylesheet'><!-- _init.php:15 --> <link href='/site/templates/styles/main.css?m=1732860276' rel='stylesheet'><!-- _init.php:15 --> <link href='/site/templates/styles/main.min.css?m=1737183931' rel='stylesheet'><!-- _init.php:15 --> <link href='/site/templates/less/_global.css?m=1732810062' rel='stylesheet'><!-- _init.php:15 --> <link href='/site/templates/less/_global.min.css?m=1737183931' rel='stylesheet'><!-- _init.php:15 --> <link href='/site/templates/webfonts/webfonts.css?m=1728940414' rel='stylesheet'><!-- _init.php:15 --> <!-- StylesArray 'rockfrontend' --> <!-- These comments are only visible when $config->debug = true; --> <link href='/site/modules/RockFrontend/Alfred.css?m=1730133700' rel='stylesheet'><!-- RockFrontend.module.php:490 --> <!-- ScriptsArray 'main' --> <!-- These comments are only visible when $config->debug = true; --> <script src='/site/modules/RockFields/lib/Sortable.min.js?m=1728917625'></script><!-- RockPageBuilder.module.php:445 --> <script src='/site/modules/RockFields/assets/frontend-loggedin.min.js?m=1728924165'></script><!-- RockPageBuilder.module.php:446 --> <script src='/site/templates/uikit/dist/js/uikit.min.js?m=1727317894'></script><!-- _init.php:19 --> <script src='/site/templates/uikit/dist/js/uikit-icons.min.js?m=1727317894'></script><!-- _init.php:20 --> <script src='/site/templates/scripts/main.js?m=1722353958' defer></script><!-- _init.php:21 --> <!-- ScriptsArray 'rockfrontend' --> <!-- These comments are only visible when $config->debug = true; --> <script src='/site/modules/RockFrontend/Alfred.min.js?m=1730133700' defer></script><!-- RockFrontend.module.php:339 --> </head> -
This module adds a "SEO" tab to every page where you can define a special title, description, keywords, etc. Try it http://aluminum-j4f.lightningpw.com/processwire/ Name: demo Pass: demo123 How to use You can choose between include automatically or use the following methods: $config->seo // includes all the default values and configuration settings // e.g.: $config->seo->title $config->seo->keywords $page->seo // includes all the default values mixed with the page related seo data // e.g.: $page->seo->title $page->seo->keywords // for rendering: $page->seo->render . . Screenshot Download You can download it in the modules repository: http://modules.processwire.com/modules/markup-seo/
-
Hüttenzauber - The magic of the Swiss Alps. Eat, celebrate and sleep in the most beautiful places in the mountains. Today, I am presenting to you a very cool and challenging project we tackled and successfully finished last summer. Obviously, Fruitcake is 100% a ProcessWire agency at this point but still, this project especially proves again and again that ProcessWire’s flexibility and unopinionated structure just works for us every time. Gone are the days where we are breaking and bending other CMSs to work the way we need it to work. “Hüttenzauber” is a well-known brand in the Bernese Alps skiing and hiking destinations. Lately, they expanded into other regions of the Swiss Alps and accumulated a variety of locations they are both managing and running from their central offices at the birthplace of the enterprise, Lenk im Simmental. Coding one of our latest projects to date was a cool but also daring challenge. We set out to replace a few dozen single websites for each of the different locations with one big website. The general goal was to streamline all the information and present a concise yet still quite independent experience to the website’s visitors. In addition to the independent experiences, the website features a plethora of central features like a search map, an illustrative blog, cool events and a web shop whose contents however, are again compiled together from blog articles written for or events happening at the different locations. The website was conceptualized, designed and programmed 100% in-house by us. It features tons of content which is completely available in both German and English (with a small JavaScript language detection function). ProcessWire admin: have exactly one source of truth I think we can be proud of the challenge we set out to achieve: Have every information only ever written down once. This is most beneficial for the client since they can for example change the hotel’s address once and it is then displayed at many different locations automatically. The client factually only needs to work with our database we created in the ProcessWire admin area and the website presents that information in a variety of places automatically. One example of that in action is restaurants. There are two types of restaurants: locations which actually are restaurants but also restaurants inside other locations, e.g. hotels. The client can easily a new restaurant inside a hotel (in PW terms that is just add a “restaurant” as a child of a “hotel”) and just set up all of it’s information like descriptions, menus, booking links and images. This entry will automatically display on the search map as part of the hotel, be added to the “book a table” buttons everywhere across the website and also have it’s information and download links be displayed on the hotel’s detail page. And by the way, the client also can (and does!) add hotels inside hotels, e.g. a small resort with independent booking but which factually is part of a bigger hotel complex. They add, press save and “it just works!” 🤯 To achieve this goal, we made extensive use of the beloved “addHookProperty” method to for example output a list of all the “book-a-table” links for any specific page which makes programming the front end of the website a whole lot easier! 🥳 The culmination of all this is a simple and easy tree structure in the admin area like this (this is just part of it): All the information one might add about a restaurant or hotel is entered in each entry’s fields. Every coordinate, address and image is only ever entered once. All of this for example results in the search map and floating booking buttons completely automatically: Not only there, but also in the menus: Content «Page Builder» In addition to all the meta information, all of the pages should allow to have a completely independent experience for a visitor. That is why any restaurant and hotel gets it’s own landing page which acts as a mini landing page. There, the information is broken down: Booking links show only for the location itself (remember, there still might be multiple 😉), events are automatically filtered by location and sub-restaurants are displayed automatically. Yet still, all of the content feels dynamic because we make heavy use of @ryan’s Repeater Matrix module. For some of the blocks, the information is entered directly but for others, the information is grabbed from the events catalog or the blog entries and automatically filtered as appropriate for the page where the block is displayed on. For example, the events block on a hotel page only displays events for that specific location whereas if the events block is used on the homepage, everything is displayed. Here is a few of the blocks the client can use on any page: Webshop with Print@Home vouchers To finish up this showcase, now for the most interesting part for all developers here, the webshop and all it’s interfaces to external services. From the very beginning, we knew, we needed to use something which will offload the cart and checkout parts of the shop completely because we don’t have the capacity to create a full webshop application for this project and there was nothing around we could build upon (this is only partly true, there is @Gadgetto's SnipWire which was a big inspiration). Obviously, nowadays there are alternatives around the corner like @bernhard’s RockCommerce which might just be the on-page solution for cart and checkout ProcessWire needs. Although the shop might seem small and unimpressive when looking at it from the user’s perspective, a lot has to happen in the background. Part of it is that we had to combine two types of products and part of it is the actual technologies we ended up using. The first product type is your standard product which gets shipped to the buyers. These products are easy compared to the second type - a streamlined experience starting in the shop where the user picks a value and a greeting for a print-at-home voucher, pays for it and together with the order confirmation is sent the voucher as a PDF ready to be printed. This lead us on an adventure where we came across asynchronous payment confirmation, custom payment processors for SnipCart and a small translation layer mapping one API to the other. In the end, we built a system of three modules for ProcessWire like this: The heart of the system is our «Snipart Integration» module. Think of it as a baby-SnipWire. It adds webhook handling, a custom payment provider API and JSON product info endpoints for SnipCart to work on the website. Building on the custom payment provider API, we have the «Wallee interface» which acts as a translation layer from the asynchronous API Wallee speaks to the synchronous one SnipCart uses. Further, we have the «Boncard interface» which adds webhook handlers to reach out to the print-at-home provider to generate and fetch the PDFs and finally sending them to the client, using our fourth and last external provider: SendGrid. Since SnipCart already uses SendGrid, this one was easy to decide. Also, there is a very good integration available with WireMailSendGrid. All of the modules have been built with reusability and modularity in mind: all of them have a configuration screen to add API secrets amongst other settings. They can be used as a package or in parts. This is useful if for example, you don’t need Wallee as a payment provider or do not have Boncard’s print-at-home vouchers in your webshop. Also, more custom payment providers are easily added using the main module’s API and webhook handlers. Conclusion There is still much to tell especially about the shop and custom payment providers’ implementations and challenges we faced. If you guys are interested, I can start working on a case study. Let me know! I will leave you with a few links for you to look at and/or get more information: https://huettenzauber.ch/ our main subject https://www.fruitcake.ch/projekte/huettenzauber/ our portfolio entry about the project COMING SOON link to page on ProcessWire Showcase Also, I don't want you to miss out on what's running behind the scenes: ProFields: Combo ProFields: Repeater Matrix Seo Maestro ProcessRedirects Tracy Debugger WireMailSendGrid All of this rocks on ProcessWire v227.
-
Hi @All, I made the page with only admin access so I can check in frontend visually (a chart) and links - what pages are most linked from blog pages and where I forgot to use this opportunity. This is important for SEO and removes manual labor. If you have any suggestions, feel free to shoot. Simple explanation: I got template=post and want to see stats with inner links to the homepage and service pages. Thanks @poljpocketfor helping me out! <?php namespace ProcessWire; // Calculate Internal Link Counts and Linked Pages $pages = wire('pages'); // Get all relevant pages directly in one query $relevantPages = $pages->find("template=post|service|home"); $blogPosts = $relevantPages->find("template=post"); $mainPages = $relevantPages->find("template=home|service"); // Function to check if a post DOES NOT link to ANY PUBLISHED service OR home page /** @var PageArray $mainPagesLinks */ $mainPagesLinks = $wire->wire(new PageArray()); foreach ($mainPages as $mainPage) { // this will remove duplicates by default $mainPagesLinks->append($mainPage->links("template=post")); } // Calculate Internal Link Counts and Linked Pages $linkCounts = []; $linkedPages = []; foreach ($relevantPages as $page) { $linkCounts[$page->id] = 0; $linkedPages[$page->id] = []; if ($page) { $items = $page->links(); if ($items->count()) { foreach ($items as $item) { if ($item->template != 'admin') { $linkCounts[$page->id]++; $linkedPages[$page->id][] = $item; } } } } } arsort($linkCounts); // Prepare data for the circle chart $chartLabels = []; $chartValues = []; foreach ($linkCounts as $pageId => $count) { if ($count > 0) { $page = $pages->get($pageId); $chartLabels[] = $page->title; $chartValues[] = $count; } } ?> <div class="container py-5 mb-5 links"> <div class="row py-5"> <div class="col-12 col-md-4 offset-md-4 pb-5"> <canvas id="linkCountChart" width="200" height="200"></canvas> </div> <div class="col-12 col-md-8 offset-md-2"> <div class="accordion" id="internalLinksAccordion"> <?php foreach ($linkCounts as $pageId => $count): ?> <?php if ($count > 0): ?> <?php $page = $pages->get($pageId); ?> <div class="accordion-item"> <h2 class="accordion-header" id="heading<?= $page->id ?>"> <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse<?= $page->id ?>" aria-expanded="false" aria-controls="collapse<?= $page->id ?>"> <?= $page->title ?> (<?= $count ?>) </button> </h2> <div id="collapse<?= $page->id ?>" class="accordion-collapse collapse" aria-labelledby="heading<?= $page->id ?>" data-bs-parent="#internalLinksAccordion"> <div class="accordion-body"> <?php if (!empty($linkedPages[$page->id])): ?> <ul> <?php foreach ($linkedPages[$page->id] as $linkedPage): ?> <?php if ($linkedPage->template != 'service'): ?> <li><a href="<?= $linkedPage->url ?>"><?= $linkedPage->title ?></a></li> <?php endif; ?> <?php endforeach; ?> </ul> <?php else: ?> <p>No internal links found for this page.</p> <?php endif; ?> </div> </div> </div> <?php endif; ?> <?php endforeach; ?> </div> <h3 class="pt-5">Posts without Links to Service and Home Pages</h3> <table class="table"> <thead> <tr> <th>Post Title</th> <th>Edit Link</th> </tr> </thead> <tbody> <?php // echo results foreach ($blogPosts as $post) { if (!$mainPagesLinks->has($post)) { echo "<tr>"; echo "<td><a href=\"{$post->url}\">{$post->title}</a></td>"; echo "<td><a href=\"{$post->editUrl}\">Edit</a></td>"; echo "</tr>"; } } ?> </tbody> </table> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> document.addEventListener('DOMContentLoaded', function () { // Chart data is now defined within the JavaScript scope const chartData = { labels: <?= json_encode($chartLabels); ?>, datasets: [{ data: <?= json_encode($chartValues); ?>, backgroundColor: [ 'rgba(255, 99, 132, 0.8)', 'rgba(54, 162, 235, 0.8)', 'rgba(255, 206, 86, 0.8)', // Add more colors as needed ], }] }; const ctx = document.getElementById('linkCountChart').getContext('2d'); const linkCountChart = new Chart(ctx, { type: 'doughnut', data: chartData, options: { plugins: { legend: { display: true, position: 'bottom', }, } } }); }); </script>
-
Export Data: Use WordPress tools or plugins to export posts, pages, media, and metadata as CSV/XML. Set Up ProcessWire: Install ProcessWire and create fields/templates to match your content structure. Import Content: Use ProcessWire’s Import Pages module or custom scripts to bring in data. Transfer Media: Upload files via FTP and update paths in your content. Redirect URLs: Map WordPress URLs to ProcessWire pages to retain SEO. Test Thoroughly: Verify all pages, media, and functionality before going live. I hope it'd be helpful.
- 1 reply
-
- 1
-
-
Fruitcake has been using ProcessWire for a while now but I didn't get around to posting any of the around 30 sites we did so far. But let me start off with our own new website which launched earlier this year. Our new brand website had the goal to be modern and simple and uses a minimalistic approach. The centerpiece of the page of course is our portfolio which most of the effort went into. It uses a playful and seemingly random (it's not!) layout. The portfolio detail page uses a vertical slider which from a technical point of view was the most challenging part. As for modules we are using, we like to do most of the stuff ourselves so it really only boils down to the Repeater Matrix Fieldtype from @ryan's ProFields package and SEO is done using SeoMaestro by @Wanze. Here you can have a look yourself: Die Kommunikationsagentur | fruitcake.ch Here is a preview of the masthead: Let me know what you think ?
- 9 replies
-
- 11
-
-
Here's my next ProcessWire build. After dipping my toe in the ProcessWater with a simple holding page ( https://www.threehills.farm/ ) followed by a blog ( https://www.eltikon.online/ ) that I shared here, this time I have focussed on a site that uses the Repeater Matrix to manage blocks of content, in order to see how easy it is to transfer my WordPress "Advanced Custom Fields Flexible Content" approach to ProcessWire. Result: it's really easy once you get the hang of it. Site: https://www.zigpress.com/ This is my business site, and is a single-page site (plus 404 etc) where each section of content is managed in a Repeater Matrix. Modules installed: AdminStyleRock (so I can use my Nord admin stylesheet!), Pro Fields Repeater Matrix, Seo Maestro, FrontendForms, Less, Redirects, my own ZP Traffic module, and Wire Mail SMTP. I also included Perishable Press's 7G Firewall. I still have more testing to do now that it's live, but it seems to be behaving OK so far. All feedback welcome, including criticism/bugs/whatever.
- 6 replies
-
- 11
-
-
Are URL segments indexable & search engine optimizable? Site-mappable? With or without SEO meta tags rendered in the native site template rendering the URL segment, drawing page data from a separate PW installation? if only rendering when the urlSegment1 matches the name of a page of a specified template with the same parent? I don't know how to test this locally. Thank you.
- 2 replies
-
- seo
- urlsegment
-
(and 2 more)
Tagged with:
-
I would like to see SEO support integrated into PW by default. I know that there is the SEO Maestro module, which is fabulous, but I think every CMS should have a good SEO support by default. So it would be great if a new tab will be added to the pages like this: In addition to this, there should be a new property inside the configuration file to disable/enable the tab globally in the way like this. $config->seo = false; // or true And in the settings of the page, there could be a checkbox to enable/disable the SEO tab per page. The content of the SEO tab could be as it is in the SEO Maestro module.
-
Hi, through this site I generated a json-LD code for page definition from SEO point of view. They say that this language is the last frontier and better than RDF language. So I would like to understand how to integrate it with processwire. Integrating html meta tags with php language in processwire is easy and painless. I didn't understand if I can do the same thing with a json language. So the suggested scheme is this: <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "headline": "", "image": "", "author": { "@type": "", "name": "" }, "publisher": { "@type": "Organization", "name": "", "logo": { "@type": "ImageObject", "url": "" } }, "datePublished": "" } </script> Through the wireinclude function the template could link to a .php file with this .json code in it, however with processwire/php strings in it. In this way: <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "headline": "<?=$page->title?>", "image": "", "author": { "@type": "", "name": "<?=$page->name_author?>" }, "publisher": { "@type": "Organization", "name": "<?=$page->name_site?>", "logo": { "@type": "ImageObject", "url": "" } }, "datePublished": "<?=$page->date_article?>" } </script> Can it be done or is it a mess? ?
-
I haven't used WordPress in a long time, I never liked it really for reasons I won't bother going into. One thing that comes up from time to time, not just for PW site but in general, is something like “Will there be something similar to Yoast in my new site?” As a default in PW I add a SEO fieldset with optional meta title, description and H1 (title property from PW is used for meta title and h1 if these fields are not set) with some simple guidelines in the notes on how to fill them out. I understand Yoast has some additional tools that advise on the actual body content (word count, paragraph length, etc). Obviously PW doesn't have this. To be honest, I have always thought things like that are really for making poor SEO become mediocre and they can't do the job of a human and create excellent SEO. Also one of the things I love about PW is that I can let clients build pages on a modular basis (using repeater matrices) as opposed to a single body HTML field and so I do not even know that such tools could effectively evaluate such content since it is assembled dynamically. I have been familiar with SEO since before it was a term and have worked with a lot of pro SEOs and none of them have ever been bothered about having something like Yoast so long as the fields I mentioned above are editable. I may be wrong but Yoast seems to appeal more to those less familiar to SEO. No offence is intended if you are a pro SEO and use Yoast. ? What are your thoughts on this?
-
This is my first module. It is primitive, like my neighbor who loves to fire up his new Makita breaker hammer after 21:30h. Please, feel free to do whatever you want. I don't promise I will add better functionality but maybe... If you do, please ping and send me back. Thanks! It loads every page on the backend. It calculates the text width in two fields you choose on the configuration page.. If there are any updates, I will post them here. Happy ProcessWiring! PS: These measurements are specifically for desktop and Google's SERP (Search Engine Results Page). UPDATE on 15 May 2024: Google started to calculate (truncate) the meta title for 20px Arial yesterday. The Module has been updated. Here is the link to download/install the latest version: https://processwire.com/modules/seo-text-width/ EDIT: The version 0.0.5. Added two dropdown fields on the configuration page of the module. PROVE IMAGE: You can check the meta title, which was measured with 18px Arial and it was less than 512 pixels.
- 14 replies
-
- 13
-
-
With regards to SEO related features, I think that being natively built in to ProcessWire is not the best choice as it goes against the unopinionated nature of ProcessWire. If one were to develop a webapp where SEO is not necessary, those fields would cause bloat. But if this were to be an official Profield, it would be wonderful. Personally, I use the FieldsetGroup Pro field (I gave SEOMaestro a try but it didn't work the way I liked), but since that's now legacy, I will have to switch my approach at some point in the future. It looks like this (note: it used to have XML sitemap fields as well, however when WireSitemapXML came out, I removed them and let that module handle it): If there were to be an new SEO module similar to my screenshot (meaning it supports fields for X, Facebook and other social networks), I would hope it's not officially called "FieldtypeSEO" but rather something like "FieldtypePresence" (ChatGPT helped me with that one) since it's more fitting (it helps improve your site's "presence" via search engines and social media networks). Personally I dislike the word SEO as it represents a lot of what's wrong with the internet and the snake-oil behind it.
-
Hey, so we all know about SEO and the importance of performance. Basically we do it, because if no one finds the website we just built, it´s frustrating. We all try to write clean markup, css and js code and most might have a webpack/gulp/whatever pipeline to minimize css&js. But when thinking about it, optimizing your pipeline might save you a few (hundreds) of kb, compared to loading an image with 1 mb that´s literally nothing and frankly just ridiculous. Don´t get me wrong, frontend pipelines are great and should be used, but let´s shift your "I will optimize the shit out of that 3 css lines" focus to something different: try to serve images as fast as possible, this is where the performance boost really happens. I´m no pro on processwire so far, but I built a very easy to use picture element, which some of you could find interesting: 1. the picture comes with 3 different sizes: one for mobile (keep in mind the double dpi, therefore width of 828px), one for tablet and one for desktop 2. the picture generates a webp version and the original file extension as a fallback 3. the filesize of each element is rendered within the "data" attribute 4. lazy loading(sooo important!!!) is done via the native 'loading="lazy"' attribute. Please try it out and see the difference ? I posted this so others can easily optimize their images, but I would also like to hear your suggestions in making it better. Maybe you could decrease the rendering time or maybe you have some easy improvements. Please let me know. Greetings from Austria! <picture> <source data="<?php echo($oElement->repeater_image->width(828)->webp->filesize);?>" media="(max-width: 414px)" srcset="<?php echo($oElement->repeater_image->width(828)->webp->url) ?> 2x" type="image/webp"> <source data="<?php echo($oElement->repeater_image->width(828)->filesize) ?>" media="(max-width: 414px)" srcset="<?php echo($oElement->repeater_image->width(828)->url) ?> 2x" type="image/<?php echo($oElement->repeater_image->ext)?>"> <source data="<?php echo($oElement->repeater_image->width(767)->webp->filesize) ?>" media="(min-width: 415) and (max-width: 767px)" srcset="<?php echo($oElement->repeater_image->width(767)->webp->url) ?> 2x" type="image/webp"> <source data="<?php echo($oElement->repeater_image->width(767)->filesize) ?>" media="(min-width: 415) and (max-width: 767px)" srcset="<?php echo($oElement->repeater_image->width(767)->url) ?> 2x" type="image/<?php echo($oElement->repeater_image->ext)?>"> <source data="<?php echo($oElement->repeater_image->webp->filesize) ?>" media="(min-width: 768px)" srcset="<?php echo($oElement->repeater_image->webp->url) ?>" type="image/webp"> <source data="<?php echo($oElement->repeater_image->filesize) ?>" media="(min-width: 768px)" srcset="<?php echo($oElement->repeater_image->url) ?>" type="image/<?php echo($oElement->repeater_image->ext)?>"> <img data="<?php echo($oElement->repeater_image->filesize) ?>" class="img-fluid" loading="lazy" src="<?php echo($oElement->repeater_image->url) ?>" alt="<?php echo($oElement->repeater_image->description) ?>" type="image/<?php echo($oElement->repeater_image->ext)?>"> </picture>
- 2 replies
-
- 2
-
-
- seo
- optimisation
-
(and 3 more)
Tagged with:
-
Hey, @kuba2! The issue is not PW itself, but rather a 3rd party module - MarkupSEO. It has not been updated for awhile (8 years))) You should: Solve the issues in this module yourself (the author is not around anymore AFAIK). Use a more modern SEO module like this one. Remove all the SEO modules and cope with SEO needs with regular fields and creativity. Anyway, you need to be able to change PHP versions back and forth and see what other modules might need an update.
-
ProcessWire Improvement Suggestions I’m pleased to see ideas being gathered to improve ProcessWire, and as a loyal user and contributor since 2014, I'd like to share my list and thoughts on the matter. Over the past few years, there has been a lack of clarity on whether requests and ideas will be implemented, and if so, when. Requests and issues often remain open for years without updates. I'll address how this can be optimized. There is also no clear way to influence these decisions. Years ago, we introduced the idea that thumbs-ups in the Requests Repo should mark the importance of requests. However, there's still no way to know if any of these points will be implemented. My idea for the future is as follows: Increase Community Involvement and Establish a Fixed Release Cycle Optimize Integration of Requests It could be beneficial to review which GitHub issues have the most thumbs-ups within a set period (e.g., quarterly or biannually) and then fix or implement them. If there are too many features or if requests have the same number of thumbs-ups, a poll could be conducted in the forum to see what the community wants implemented first. Introduce New Status Labels for Requests Under consideration In Progress Completed Faster Closure of Issues When a request is integrated, it should be closed immediately rather than marked as “completed.” Often, the requester doesn’t provide feedback or forgets to close the issue (which has happened to me as well). If something doesn’t work, the issue can be reopened. Automatically Close Stale Issues There is a stale action for GitHub that automatically closes issues after a period of inactivity. The issue creator can reopen it by responding. Better Definition of ProcessWire’s Target Audience The target audience for ProcessWire is not clearly defined. This makes it difficult to determine which features are truly necessary. Developers will likely have different requirements compared to someone looking to create a website quickly and cheaply. Developers eventually hand over the website to a client, who then manages the content themselves. Non-developers struggle to know which modules exist to manage their website. The module installation process is cumbersome, which is why I developed the ModuleManager2 years ago (link). Despite user interest, it hasn’t been widely used. There was a discussion that module installation directly through ProcessWire was rejected for security reasons (though my module only uses core methods). Integrated module management, similar to systems like WordPress, Prestashop, or Shopware, would simplify module discovery, installation, and maintenance. Discovery Process The discovery process is beneficial for all users, including power users. Sometimes I can’t remember a module’s name, but with MM2, I can search for a term in the description and find the corresponding module. Security is a concern, but I believe it’s possible to implement this in a secure way. PHP 8 and Named Arguments Finally, PHP 8 and named arguments will greatly assist in module development. Easier Customization of the Admin Theme Custom Navigation It's cumbersome to change the navigation or create a completely custom one without creating a new admin theme. This has been mentioned before (insert link). This is usually only necessary when developing custom software with ProcessWire. It would be helpful to create menu items that link to modules or specific actions within those modules, or simply a URL path. Introduce a dark mode (toggle switch). A quick solution is with one line of CSS: nightowl, but an integrated dark mode would be better. Global Media Manager A global media manager is a great idea, especially for certain assets like logos or seals used across multiple pages. While it’s possible to create a page and field to source these images, it’s not user-friendly for newcomers. For instance, I have a client with 30 product pages that require seals to be updated annually. A central management point would simplify this process. I’ve implemented this for my client, but it’s challenging for those accustomed to systems like WordPress and conceptually difficult to grasp. Aligning with Conventions from Other Frameworks Environment Configuration A .env file to store database connections, httpHosts, etc., which are read by site/config.php and site/config-dev.php. The config-dev.php file should include config.php and allow properties to be modified/overridden. This is how I and others currently do it (insert reference). Automated Deployments and Module Updates Composer is a stable package manager for PHP and a convention widely used in the PHP community, making automated deployments and updates easier. Built-In SEO Built-in rudimentary SEO would be a significant plus for websites. During installation, users could choose between a website or custom software without a frontend. If they choose a website, either ProcessWire-Core-SEO or the SEOMaestro module could be selected for installation. Lack of Visibility on ProcessWire Adoption There is no clear overview of how widely ProcessWire is adopted, such as the number of installations. This lack of visibility can make it difficult to gauge the platform's popularity and growth. Absence of a Marketplace Ecosystem The absence of a marketplace ecosystem makes it uncertain for module developers like Bernhard or myself to determine whether and how much money can be made from developing modules for ProcessWire. Other systems like Kirby, Statamic, and WordPress have a store that can be accessed directly through the admin interface. These stores also include license management, making it easier for developers to monetize their work and for users to manage their purchases. Proposed Marketplace and Revenue Sharing One idea could be to introduce a marketplace for ProcessWire, where a portion of the revenue from sales is reinvested into the development of ProcessWire. However, this is not a necessity and should be a point of discussion, considering the controversies seen with Epic or the Apple Store.
-
Elementor is not part of Wordpress, but a plugin developed by someone else to run in WordPress. Maybe this is an opportunity for someone in ProcessWire. And there are already RockPageBuilder and PageGRID in ProcessWire to consider as well. The dominance of WordPress has always been the case, and it will likely continue to be the case. ProcessWire is unique and has always been something completely different from WordPress. The CMS projects that are trying to mirror WordPress never seem to last long because, if you want something like WordPress, then why not just use WordPress? Though I don't know anyone that really knows both well who prefers to develop in WordPress. Instead, WordPress dominance is largely a matter of name familiarity with clients, which is completely understandable. But there are also several things it genuinely does well, and I always believe in using the best tool for the job. If the types of projects you work on are a good match for WordPress, then great, but I hope you also have projects that are a good match for ProcessWire as well. ProcessWire fully supports it. Many (most?) of us is have used exclusively on new installations for years. I don't know what those are. it sounds like another opportunity for modules. But more info needed. Good idea. This is something I think I can do. Can you describe further? In the majority of cases, older modules will still work fine. I'm not aware of anything in the modules directory that does not work, but if any become apparent I will remove them. Maybe it would be a good community effort to identify modules that could be removed from the directory. I think this might turn off as many people as it turns on. I know I wouldn't like it. But if it can be automated as an installation option then sure, why not. ProcessWire already supports WEBP and has for awhile. I was working on core AVIF support, but Robin S. released an AVIF module so I put mine on the back burner. And this may be the sort of thing that is better as a module, or maybe it'll be better in the core at some point, I'm not sure. Right now we don't build core versions in a way that breaks things from one to another. At least not in a way that is known at the time of assigning a version number. When that changes, I think we'll want to adopt semantic versioning, and maybe that's a part of 4.x. Maybe it would benefit 3rd party modules more in the shorter term, so it's worth looking at there first. Sounds interesting but I need more details. If there's anyone that is an nginx expert that wants to step up to have this responsibility I'd welcome it. I've mentioned this before too, but nobody has stepped up. I'm not an nginx expert so don't feel qualified to support it on my own. For real? This is the first I've heard of any chaos. Good to hear. We do have the PageAutosave module which does Live Preview even better than ProDrafts. This module is available in ProDevTools or ProDrafts boards, but maybe it can have a wider release. This is exactly the sort of thing that PW is great at supporting already. But I have been exploring building an everything-SEO module here, as it's something I'd like to use and support, but just not as part of the core. The hard-core SEO experts will still want to do their own SEO stuff like they do now, but for those that want something they can turn-key and have it all ready, a module is ideal. I'll very likely be building one within the next year hopefully.