Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 05/06/2019 in all areas

  1. No, as written two posts above, we have different purpose images. There could be, for example, uncompressed master images that serves as source and should not be displayed on screen! Following I show some more image file purposes, but want to start with a brief introduction to image processing procedures. Within a web environments very limited support, our master source images have to be uncompressed JPGs with 8 bit color depth per channel. Far from optimal. In other environments TIFF or PSD in 16 or 32 bit color depth is used for that purpose. The destructive compression method of JPGs is responsible for most of the user errors that occur during the processing steps. Once you saved a JPG compressed within a workflow, as source or intermediate, you damaged it unrecoverable! And one also cannot detect this programmatic within a common web environment! This is one reason why we keep the master source, the original image untouched, "as is" in PW! (besides other aspects) So, for our use cases, the JPG format can have different purposes: uncompressed master source images, uncompressed intermediate variations, final optimized and compressed variations for screen output. Also, if one has a use case for it, one can optimize and finalize JPGs only for the purpose of printing in PW. Or for embedding them into PDFs, or for serving them as single files or as zipped archives for different purposes per downloads. So, it's not: "all get displayed on screen". Some never should be displayed on screen. (I maintain sites for photographers who keeps 60k+ images as original (pw master source) images in the site. They only publicly show watermarked images within limited dimensions. The original images are protected against web access. And they are used for all the different above listed output purposes.) Luckily with the WebP format it behaves different. Due to its intended purpose, (that is, highly compressed and optimized for screen output), it is unusable for all other purposes! Sure, if you want to or don't care about anything, you can use everything wrong, but it is not that subtle like with JPGs. In German we have a saying: "gefährliches Halbwissen" ("dangerous half-knowledge"). No idea whether one can translate the sense connected with it by DeepL? But it means something like: If you already know a good part, you quickly run the risk of drawing (wrong) conclusions about the unknown parts. Personally, I am always happy when I get explanations, help or advice from people who have more specialist knowledge. After all, I can't know everything myself, right? ? One of my most important life experiences & wisdoms to fight against my "gefährliches Halbwissen": "If there are many people who study and learn for years to get a vocational qualification before they can "really" get into the subject, then I know that a huge part of it is invisible to me, even if I think I already understand a lot of it.".
    4 points
  2. Regarding compression and quality | webpQuality | lossy | lossless and how different the two rendering engines work, I want to show a little example, comparing two image motives with equal dimensions. I have created two master source images in the dimensions 3000 x 2000 px. The first one is a 24bit color jpeg, filled with noise in Photoshop. The second one is a checkerboard with 100x100 squares, only black and white, but also saved as 24bit color jpeg. I don't want to scare or confuse anybody. Look at the following tables with the file sizes for the different qualities, rendering engines and output formats. (Just let it sink, ...) Really interesting are the results of the IM files in 100% quality, compared to their jpegs and compared between both motives. ? EDIT: And here two more real world motives, but also with interesting IM 100 quality results:
    3 points
  3. Hie @tiefenbacher_bluetomato You should be able to do it in this way $first_image = $page->images->first(); $first_image->description('de'); As pageimage class extends pagefile.
    2 points
  4. I think it is independent from the source. The compression is set for the output format, that is webp, regardless what was the source format. It can be JPEG, PNG, GIF, TIFF, BMP or any other image file format that can be opened by the proceeding rendering engine. (For IMagick most often one out of 230+) In this context, we can use lossless for sources from JPEGs too. But because in my tests it seems to be exact or nearly the same like quality 100, I had not seen a valid reason to bloat our imageSizerOptions with another setting, what then would need to be checked for its existence and compared against webpQuality. Also: HINT: if one passes wrong values for webpQuality, out of range (1-100), the webp rendering method automatically falls back to lossless. So if you use 100 or anything above 100 for webpQuality, you get lossless. ?
    2 points
  5. Just wanted to add, that using one highly compressed WebP image as source image also would be bad for using responsive images, which is highly recommended to use, because of all those different devices with different screen resolutions. For responsive images you need generated variations of a uncompressed source image. ?
    2 points
  6. There are a few solutions and the core in the end even added an selector for the usecase.
    2 points
  7. Here is a hook method for /site/ready.php that gets the pages that are selected in a given Page Reference field: /** * Get pages selected in a Page Reference field * Use optional argument $options for limiting 'template' and/or 'parent_id', and to 'include_unpublished'. * Usage: $selected_pages = $fields->my_page_field->getSelectedPages(['template' => 'basic_page']); * The number of times each page is selected is available in a custom "occurrunces" property on each selected page. */ $wire->addHookMethod('Field(type=FieldtypePage)::getSelectedPages', function(HookEvent $event) { /* @var Field $field */ $field = $event->object; $options = $event->arguments(0); $get_options = []; if(isset($options['template'])) $get_options['template'] = $this->wire('templates')->get($options['template']); if(isset($options['parent_id'])) $get_options['parent_id'] = (int) $options['parent_id']; $table = $field->getTable(); $query = $this->wire('database')->query("SELECT data FROM $table"); $ids = $query->fetchAll(\PDO::FETCH_COLUMN); $count_values = array_count_values($ids); $items = $this->wire('pages')->getById(array_keys($count_values), $get_options); foreach($items as $item) { if(empty($options['include_unpublished']) && $item->status > Page::statusUnpublished) { $items->remove($item); } else { $item->occurrences = $count_values[$item->id]; } } $event->return = $items; }); The following is an example of how you would get the category pages that are selected in your category field. The number of times each category is selected is available in the custom "occurrences" property if you need it. // Get the selected category pages $selected_categories = $fields->categories->getSelectedPages(); // Demo output foreach($selected_categories as $selected_category) { echo "<p>Category {$selected_category->title} is selected in {$selected_category->occurrences} page(s).</p>"; }
    2 points
  8. Not that it really matters, but until now I was unaware that WebP even supported "lossy" compression for PNG images. Turns out that for PNGs one can indeed use either WebP-lossless or WebP-lossy, while for JPGs you're obviously always using WebP-lossy ? Thanks to @horst for explaining the use cases! I did also wonder why we wouldn't want WebP source images. Technically I still think that it could make sense (ProcessWire can be used for a number of use cases, but "displaying images online" is still the most popular one by a wide margin), but it isn't really such a big deal. Not to mention that I'm not sure how well supported WebP manipulation operations are (in GD/Imagemagick).
    1 point
  9. @elabx@joshuag Are you still planning on releasing the module? It would be a pity and a huge loss if such an awesome module would never see the light of the day. If you currently lack the time to continue on the module maybe you could publish it along with a list of things that still need to be done? I would gladly work on completing it.
    1 point
  10. Because it is not a "normal" image format by its intended purpose! As far as I understood, you upload already compressed images that you don't want to proceed further? Note: 1) Already (not lossless) compressed images should not be used for any transformation, because the images are lacking lots of information due to the compression. (like the webp format!) 2) If you do not want to use the resize functionality, you have to provide the final webp by yourself too. PW only provides webp support for PW created variations for final output. This is due to the file formats nature or its intended purpose. Maybe you don't want or need a webp variation for admin thumbs. Maybe other users don't care. Maybe some users really like it when opening a editor page with 300 images displayed as admin thumbs. I cannot answer this other then: It may depend on a lot of different things? I really would like to discuss the general webp support first, and the less common use cases like this later on, in a following fine tuning phase. ----------- AFAIK the most common case is to use the pageimage system with uploaded uncompressed images that serves as master images. Those master images are never get served to the outside "as is". They every time get transformed into a variation file that is optimized and finalized for a specific purpose! At least this is the only correct way for an error-free workflow with maximum possible quality combined with smallest possible file size! Whether people understand this or not, my almost 30 years of expertise in the photographic industry has never shown me anything else. And all my work within the PW images system is (and only can be) based on this. ----------- I know that there also is a minor usage of the pageimage fields for simply displaying the original files to the frontend, but my best bet is that this is much lower then 10%. In my opinion this is not the intended use case for pageimages. The use of a file field would suffice, if it also could display an admin thumbnail in the backend. But because this functionality is missing in a file field, pageimage fields are used as "compromise solution" for this purpose.
    1 point
  11. News Update - 3 May 2019 - Part Two Class Padloper - Public API We have re-written the Padloper Class (Padloper.module) to server as proxy class for use as a public API for everything you need to do with Padloper (except rendering; we don't do that :-), see previous post). Think of ProcessWire $pages variable. This is something similar in that it allows you to interact with (retrieval and manipulation) with Padloper at a higher level. Rather than multiple methods from multiple classes such as $modules->get("PadCart")->someMethod() or $modules->get("PadCheckout)->anotherMethod(), you will only need to interact with one class, Padloper. This will hopefully streamline your development process. Most (maybe all) of the current modules will be "converted" to Padloper core classes (.php) and/or absorbed in other classes. This means less modules will be installed. With one class (proxy class but also with native methods) you won't need to know (but good if you did :-)), the names of the fields that Padloper will ship with. Your code could be as simple as this: // get the class $padloper = $modules->get("Padloper"); // find a limited number of products $products = $padloper->findProducts(); // find products matching a criteria $selector = "brand=toshiba,price>200"; $products = $padloper->findProducts($selector); $selector = "tags|categories~=Vane,type*=Jac,description~=Some text,images.count>3"; $products = $padloper->findProducts($selector); $selector = "inventory<=10,variant_option=size|colour,variant_option_value=red|black"; $products = $padloper->findProducts($selector); // find related products $related = $padloper->findRelatedProducts($product); // get current cart $cart = $padloper->getCart(); // add item to current cart $padloper->addCart($item); // import products $padloper->importProducts($imports);// array/json/csv // get an order $order = $padloper->getOrder($id); // export orders $padloper->exportOrders($selectedOrders); // etc for customers, discounts, downloads, categories, tags, brands, variants, reports, inventory, types, etc Note the short 'field names' in the selectors. For instance, there are no fields known as brand or tags or categories in Padloper (they have other names and/or are subfields) but Padloper will convert them into something ProcessWire will be happy with. Currently, we don't support Or:groups, sub-selector selectors and similar using the 'short-field-names-selectors' . For such, you would need to pass the the full names and subfields of the Padloper fields you want to query to findProducts(), etc. The Padloper Class is also useful for debugging your shop, producing reports using the API, etc.. We'll talk about this and more in future posts.
    1 point
  12. News Update - 3 May 2019 - Part One The little speckled fella has been very busy on the trail. Despite being the smallest of its kind, she has lofty dreams. We've been working on the API and the Products GUI. In the next post, I'll tell you more about the API. Products Features and GUI Though there are a few issues still pending (aren't there always? sigh), I am relatively pleased with the results. Of course, during beta testing we'll received and incorporate feedback as best as we can. Only UI-Kit theme will be officially supported. Multilingual fields if site is multilingual Ajax powered inputs for fast and convenient editing Four types of products Physical product requiring shipping Physical product not requiring shipping (for collection) Digital product Service/Event (etc) products - e.g. Swimming lessons, Consultancy services, Hotel booking, etc Product Classification (ajax powered) by: Type: e.g. Belt, trousers, etc Brand: Puma, Sanyo, whatever (editors can type in or import brand names + planning to support logos) Categories (aka Collections): Multiple categories, e.g. Men, Girls, Hospitality Tags: Multiple tags can be entered, e.g. sale, amazing, etc Product Variants (consists of an Option (e.g. Colour) and an Option Value (e.g. Red) Add zero or n variants as you wish Live preview as you build variants Each created product variant can be enabled/disabled. Devs can then decide to either show that variant as unavailable or not show them at all. That's up to you :-). Apart from classifications, shipping class, inventory policy, weight and dimensions units, title and description (and this latter one may change), almost all product properties will vary by variant if variants are used Product properties include: Images Colour (more on this below) Downloads (centralised and reusable) Price and Compare Price (aka former price) Inventory policy (whether to track or not) Charge taxes SKU (stock keeping unit) Inventory (quantity) Allow back orders (aka overselling) - @note: this can be set per variant. This is useful if one variant can be restocked faster than others Weight Length Width Height Weight Unit (mg, g, kg, oz, lb, t) Dimensions Unit (mm, cm, m, in, ft ) Shipping Class - can be used for product-based shipping if needed (e.g. bulky goods, light, fragile, small items, perishable, etc) Images (more on this below) Downloads (more on this below) Images Multiple images can be added to both a product and its variants (in case its has some). Images add to the product itself can be used with all variants Images added to a variant are tracked/tagged as belonging to only that variant In some cases, it may not make much sense to add different images for similar variants. For instance, a small red hat and a large red hat could probably share the same images. Although we do not currently support specifying an already uploaded image as belonging to a group of variants, this may change in the future Colour Similar to images, can be set at both product and its variants level Colour saved in RGBA format Downloads Also similar to images, can be populated for both both product and/or its variants Multiple files can be added to a product A file designated for 'whole' product will be available to download irrespective which variant of the product was purchased Conversely, a file or files saved for a variant will only be available to the buyer if they buy that variant of the product The above is useful if you want buyers to be able to download different files of the same product. For instance, recently someone needed to sell two versions of a font as part of one product. This is a solution for such cases. TODO List Lots! e.g. default settings for some properties, e.g. weight unit, shop currency, etc. For the frontend, we are creating a rich language-aware API that you can use to build your shop however you want. There will be no rendering of markup (but see first post in this thread about a separate fully functional frontend shop). In the next post, we talk a bit about the API. Before that, here are some screenshots and a video demo (if you can spare some 20 minutes away from watching, er..., cat videos? ?) Screenshots Video Demo Thanks ?
    1 point
  13. What about this? $wire->addHookAfter("Pages::added", function($event) { $page = $event->arguments(0); // check for page template if necessary here $page->setAndSave('check', 1); }); PS: Did you see @Robin S module?
    1 point
  14. Another advantage of storing the template code in the database is you can attach all php/html, js, css, and images/files, into the one page so it is completely portable. So you could import a component page into your theme folder, and everything that is needed is there. And to uninstall you just delete the one page.
    1 point
  15. User registration example: public static function addUser($data) { RestApiHelper::checkAndSanitizeRequiredParameters($data, [ 'username|selectorValue', 'password|string', 'email|text', ]); $item = new User(); $item->setOutputFormatting(false); $item->name = $data->username; $item->pass = $data->password; $item->email = $data->email; $item->addRole('guest'); $item->save(); }
    1 point
  16. News Update - 6 March 2019 Hi all. How time flies! It's been a while since I gave you an update on progress. Taxes Most of the current work has focused on Taxes. Although the last mini update talked about the GUI, the main work has gone into taxes. I went back and forth on this one but I think I've finally arrived at something I am satisfied with. Looking at other systems, I liked both the approaches of Shopify and Woocommerce, but especially the former. The approach we'll take will marry the best of both worlds. In a nutshell, taxes will involve/feature the following: You will first need to set up countries that you will be shipping to (this is done when setting up shipping zones). An overview of the shops tax settings. Tax Overrides: Use this feature to override the base tax rates and taxes on shipping per country and/or country state/province. You can create a product category (aka collection) to which a specific tax override will apply. Tax Exemptions: At each product level, you will be able to specify whether a product should be charged tax or not. However, you will also be able to exempt some customers from paying taxes on purchases. Currently, email addresses will be used to identify such customers. Option to choose if taxes should be charged on shipping rates or not. In cases where more than one tax can be applied, choose whether taxes are compounded (e.g. a country + province tax) or one used instead of the other. Specify whether digital products should be charged tax (e.g. EU VAT) or not. Evidence of buyers location for EU VAT. Specify if taxes are calculated based on origin (where you are shipping from/your shop location) or destination (where you are shipping to)[default]. Specify if stated/displayed prices include taxes already or not. Print out tax reports (how much tax you've charged over a given period). Taxes in the United States are a bit tricky to determine. We'll do our best to accommodate the needs of Padloper users in this country. Please note that although Padloper 2 will ship with base rate taxes for most/many territories of the world, including their states/provinces where applicable, and whilst we'll endeavour to regularly update tax rates, the responsibility to ensure that a shop/business is charging and remitting the correct taxes lies solely with the shop owner. Shop owners can use overrides to update their taxes in cases where Padloper 2 base tax rates are not up to date. We will not accept any responsibility for any wrongful tax deductions due to incorrect base rates and/or overrides. GUI i was hoping to have something for you to look at by now but I needed to sort out tax related logic first. I wanted the logic to guide the GUI design and not the other way around. So, you'll have to take a rain check on this one. Files We had a bit of a think on how to handle files (downloadable/digital products, site-wise assets, etc). No final decision has been made but the goal is to be as versatile as possible. Notifications We've started drafting plans on how this will work. More on this later but this will involve email notification to yourself, customers and shop staff regarding their orders. Some notifications will be (semi-) automated, e.g. sending order confirmation, order status change, etc. That's it for now. By next update, I'm hoping to have something for you to look at, however crude :-). Thanks!
    1 point
  17. Yes I know, but afaik, this has not much in common with the way bootstrap works, so I don't think my "Gridbuilder" is a good starting point for implementing this. I don't know if it will ever be a single module. I'd call it a "setup" because it consists of several modules, plus a basic bootstrap setup using webpack (for front and backend) plus several templates etc. Release date? Not sure, I'm moving soon, so I have a lot to do.. ?
    1 point
  18. This can now be done with owner selectors (http://processwire.com/blog/posts/processwire-3.0.95-core-updates/) $tagsThatHaveBeenUsedOnPosts = $pages->find('template=tag, tags.owner.template=post'); Where tags is the name of the field on the post template that holds the tag pages. Also, for any given tag page you can check how many post pages reference it with $page->references('template=post')->count(); https://processwire.com/blog/posts/processwire-3.0.107-core-updates/#what-pages-point-to-this-one
    1 point
  19. These should also work (on dev): // save directly from page object $page->save(array('quiet' => true)); // save just 1 field from page object $page->save('myfield', array('quiet' => true)); When quiet mode is set, the modified date is set to whatever is specified in the page. Meaning, the modified date is not updated unless you change it yourself. Quiet mode also lets you modify the modifedUser and createdUser for the page, if you want to. These are two properties you can't usually modify, but quiet mode lets you do it.
    1 point
  20. Go ahead and make your module implement ConfigurableModule, even if you don't want any interactive configuration: class MyModule extends WireData implements Module, ConfigurableModule { Add a getModuleConfigInputfields function to your class that just does this: public function getModuleConfigInputfields(array $data) { return new InputfieldWrapper(); } For data that you want to store, call upon the $modules API var like this: $data = array('foo' => 'hello', 'bar' => 'goodbye'); wire('modules')->saveModuleConfigData($this, $data); For data that you want to retrieve: $data = wire('modules')->getModuleConfigData($this); Your config data will be populated to your module automatically after __construct() but before init(). As a result, you might want to set default values in your __construct(): public function __construct() { $this->set('foo', 'hi'); $this->set('bar', 'bye'); } public function init() { // $this->foo is now 'hello' (not 'hi') // $this->bar is now 'goodbye' (not 'bye') } Using this method, your module can always access it's config data directly via $this->foo and $this->bar. Meaning, you probably won't ever need to use the getModuleConfigData() function, but it's good to know its there. However, you will need to use the saveModuleConfigData() function since you are saving configuration data non-interactively.
    1 point
  21. Hi lance, welcome. PW is very flexible and has no "Site" title as it's mostly something you set/code in your template. However you could use any field or page title field as the site name from any page you like. So maybe use the root node "home" on the top of the tree to define a site title for your website. To for example simply output the title from the root page use echo $pages->get("/")->title; Or if you want you could create a simple textfield ie. "site_title" and add that to the "home" template in PW. Then you simply change the above to echo $pages->get("/")->site_title; Another possible route is to use config file to add a title in /site/config.php $config->site_title = "MyHomepage"; then to you can access it from any php template like echo $config->site_title;
    1 point
×
×
  • Create New...