Leaderboard
Popular Content
Showing content with the highest reputation on 12/11/2018 in all areas
-
If you don't already know the page and field name that $imageOK belongs to and have to work it out from the Pageimage then I suggest: $p = $imageOK->page; $field_name = $imageOK->field->name; $p->of(false); $p->$field_name->add('/full/disk/path/to/image_new.jpg'); $p->save($field_name);3 points
-
I've been working with ProcessWire for a while now, and I've noticed that using Composer to manage dependencies and autoload external libraries isn't as prevalent in ProcessWire development as in other areas of PHP programming. I started out by using the default setup recommend in this blogpost. However, one major problem I have with this approach is that all external dependencies live in the webroot (the directory the server points to), which is unfavourable from a security standpoint and, in my opinion, just feels a bit messy. In this tutorial, I want to go through a quick setup of Composer and ProcessWire that keeps the dependencies, all custom-written code and other source material outside of the webroot, and makes full usage of the Composer autoloader. This setup is pretty basic, so this tutorial is probably more useful to beginners (this is why I'll also include some general information on Composer), but hopefully everyone can take something away from this for their personal workflow. Site structure after setup This is what the directory structure can look like after the setup: . ├── composer.json ├── composer.lock ├── node_modules │ └── ... ├── public │ ├── index.php │ ├── site │ ├── wire │ └── ... ├── packacke-lock.json ├── package.json ├── sass │ ├── main.scss │ ├── _variables.scss │ └── ... ├── src │ ├── ContentBag.php │ └── ... └── vendor ├── autoload.php ├── composer ├── league ├── symfony └── ... As mentioned, the main point of this setup is to keep all external libraries, all other custom source code and resources out of the webroot. That includes Composer's vendor folder, your node_modules and JavaScript source folder if you are compiling JavaScript with webpack or something similar and including external scripts via NPM, or your CSS preprocessor files if you are using SASS or LESS. In this setup, the public directory acts as the webroot (the directory that is used as the entry point by the server, DocumentRoot in the Apache configuration). So all other files and directories in the mysite folder aren't accessible over the web, even if something goes wrong. One caveat of this setup is that it's not possible to install ProcessWire modules through Composer using the PW Module Installer (see Blogpost above), but that's just a minor inconvenience in my experience. Installation You'll need to have composer installed on your system for this. Installation guides can be found on getcomposer.org. First, open up your shell and navigate to the mysite folder. $ cd /path/to/mysite/ Now, we'll initialize a new Composer project: $ composer init The CLI will ask some questions about your projects. Some hints if you are unsure how to answer the prompts: Package names are in the format <vendor>/<project>, where vendor is your developer handle. I use my Github account, so I'll put moritzlost/mysite (all lowercase). Project type is project if you are creating a website. Author should be in the format Name <email>. Minimum Stability: I prefer stable, this way you only get stable versions of dependencies. License will be proprietary unless you plan on sharing your code under a FOSS license. Answer no to the interactive dependencies prompts. This creates the composer.json file, which will be used to keep track of your dependencies. For now, you only need to run the composer install command to initialize the vendor directory and the autoloader: $ composer install Now it's time to download and install ProcessWire into the public directory: $ git clone https://github.com/processwire/processwire public If you don't use git, you can also download ProcessWire manually. I like to clean up the directory after that: $ cd public $ rm -r .git .gitattributes .gitignore CONTRIBUTING.md LICENSE.TXT README.md Now, setup your development server to point to the /path/to/mysite/public/ directory (mind the public/ at the end!) and install ProcessWire normally. Including & using the autoloader With ProcessWire installed, we need to include the composer autoloader. If you check ProcessWire's index.php file, you'll see that it tries to include the autoloader if present. However, this assumes the vendor folder is inside the webroot, so it won't work in our case. One good place to include the autoloader is using a site hook file. We need the autoloader as early as possible, so we'll use init.php: EDIT: As @horst pointed out, it's much better to put this code inside the config.php file instead, as the autoloader will be included much earlier: // public/site/config.php <?php namespace Processwire; require '../../vendor/autoload.php'; The following also doesn't apply when including the autoloader in the config-file. This has one caveat: Since this file is executed by ProcessWire after all modules had their init methods called, the autoloader will not be available in those. I haven't come across a case where I needed it this early so far; however, if you really need to include the autoloader earlier than that, you could just edit the lines in the index.php file linked above to include the correct autoloader path. In this case, make sure not to overwrite this when you update the core! Now we can finally include external libraries and use them in our code without hassle! I'll give you an example. For one project, I needed to parse URLs and check some properties of the path, host et c. I could use parse_url, however that has a couple of downsides (specifically, it doesn't throw exceptions, but just fails silently). Since I didn't want to write a huge error-prone regex myself, I looked for a package that would help me out. I decided to use this URI parser, since it's included in the PHP League directory, which generally stands for high quality. First, install the dependency (from the project root, the folder your composer.json file lives in): $ composer require league/uri-parser This will download the package into your vendor directory and refresh the autoloader. Now you can just use the package in your own code, and composer will autoload the required class files: // public/site/templates/basic-page.php <?php namespace Processwire; use \League\Uri\Parser; // ... if ($url = $page->get('url')) { $parser = new Parser(); $parsed_url = $parser->parse($url); // do stuff with $parsed_url ... } Wiring up custom classes and code Another topic that I find really useful but often gets overlooked in Composer tutorials is the ability to wire up your own namespace to a folder. So if you want to write some object-oriented code outside of your template files, this gives you an easy way to autoload those using Composer as well. If you look at the tree above, you'll see there's a src/ directory inside the project root, and a ContentBag.php file inside. I want to connect classes in this directory with a custom namespace to be able to have them autoloaded when I use them in my templates. To do this, you need to edit your composer.json file: { "name": "moritzlost/mysite", "type": "project", "license": "proprietary", "authors": [ { "name": "Moritz L'Hoest", "email": "info@herebedragons.world" } ], "minimum-stability": "stable", "require": {}, "autoload": { "psr-4": { "MoritzLost\\MySite\\": "src/" } } } Most of this stuff was added during initialization, for now take note of the autoload information. The syntax is a bit tricky, since you have to escape the namespace seperator (backslash) with another backslash (see the documentation for more information). Also note the PSR-4 key, since that's the standard I use to namespace my classes. The line "MoritzLost\\MySite\\": "src/" tells Composer to look for classes under the namespace \MoritzLost\MySite\ in the src/ directory in my project root. After adding the autoload information, you have to tell composer to refresh the autoloader information: $ composer dump-autoload Now I'm ready to use my classes in my templates. So, if I have this file: // src/ContentBag.php <?php namespace MoritzLost\MySite; class ContentBag { // class stuff } I can now use the ContentBag class freely in my templates without having to include those files manually: // public/site/templates/home.php <?php namespace Processwire; use MoritzLost\MySite\ContentBag; $contentbag = new ContentBag(); // do stuff with contentbag ... Awesome! By the way, in PSR-4, sub-namespaces correspond to folders, so I can put the class MoritzLost\MySite\Stuff\SomeStuff in src/Stuff/SomeStuff.php and it will get autoloaded as well. If you have a lot of classes, you can group them this way. Conclusion With this setup, you are following secure practices and have much flexibility over what you want to include in your project. For example, you can just as well initialize a JavaScript project by typing npm init in the project root. You can also start tracking the source code of your project inside your src/ directory independently of the ProcessWire installation. All in all, you have good seperation of concerns between ProcessWire, external dependencies, your templates and your OOP-code, as well as another level of security should your Server or CGI-handler ever go AWOL. You can also build upon this approach. For example, it's good practice to keep credentials for your database outside the webroot. So you could modify the public/site/config.php file to include a config or .env file in your project root and read the database credentials from there. Anyway, that's the setup I came up with. I'm sure it's not perfect yet; also this tutorial is probably missing some information or isn't detailed enough in some areas depending on your level of experience. Feel free to ask for clarification, and to point out the things I got wrong. I like to learn as well ? Thanks for making it all the way to the bottom. Cheers!2 points
-
This seems to be an index page. Are you sure the error happens on the first item of the collection? In other words did you check all items for that key as you‘ve only posted one here.2 points
-
That's exactly the point. You might regret it. Doing such a complex project without experience is never a good idea.2 points
-
Here is the description of Pageimages class ProcessWire Pageimages Pageimages are a collection of Pageimage objects. Typically a Pageimages object will be associated with a specific field attached to a Page. There may be multiple instances of Pageimages attached to a given Page (depending on what fields are in it's fieldgroup). https://processwire.com/apigen/class-Pageimages.html2 points
-
Hi all, I have posted this in the VIP support forum of Padloper as well. Some of you do not have access to that board so posting here as well. Hopefully it doesn't count as spamming?! In June 2018, Antti announced that he was looking for a new product owner for Padloper. Sometime after, I had a fruitful discussion with him about my vision for the project if I was to take over. We agreed that commitment, motivation and a concrete plan were all important ingredients for the continued success of Padloper. I would like to officially announce that I am now the product owner and lead developer of Padloper. For those who may not know, I am the author and maintainer of several ProcessWire modules, both free and commercial. I am also a moderator in the ProcessWire forums. I would like to share with you a number of things regarding what’s going to happen next. This will be a long read. First, I would like to thank Antti for developing a great product. A lot of man-hours, dedication, passion and love has gone into making Padloper what it is today. Secondly, I would like to thank all users of Padloper. A great product is nothing without active users utilising it, putting it to the test, reporting bugs (even offering possible solutions) and proposing new features. So, thank you for helping make Padloper great! Support Thousands of hours have gone into developing Padloper. Although the code is well-written and easy to follow, Padloper is a big application with many moving parts. As such, it will take some time before I can fully grasp its inner workings. To make this transition as smooth as possible, Antti will help me with support for Padloper for some time. Currently, Padloper has a dedicated support forum. This is an arrangement between Ryan and Antti. The support forum works great as it allows the opening of multiple support threads to cover different issues. I have yet to speak to Ryan whether this arrangement can continue. However, given that I have other pro modules that I support in the open forums, it is unlikely that I will be requesting Ryan to let Padloper’s dedicated forum carry forth. A dedicated forum for one of my pro modules and open forums for my other pro modules will lead to confusion and questions from users of those other modules. Hence, Padloper support in the forums will move to the open forums. The disadvantage here is obviously the fact that support will be offered in one single (and maybe massive) support thread. To get around a ‘single thread support forum’, I am thinking of developing a simple online support queue system for all my modules. Meanwhile, support will continue in a new single thread and via email. Roadmap This list is neither exhaustive nor cast in stone. Its aim is to give an overview of my plans for Padloper. · Padloper 2 – a new major release · New backend for Padloper · Optional pro frontend module for Padloper · Documentation · New payment modules Let’s talk a bit about this list. Padloper 2 Release Padloper 2 will be a major release that incorporates a new, central backend shop for Padloper. This will be a new process module that pulls from the existing parts of Padloper (data models, etc) into one interface (more on this below). This version will also be extensible in the frontend, allowing for the plugging in of a new, optional, commercial frontend shop (full featured shop profile). Padloper 2 will not support the current ‘any page can be a product’ paradigm. Technically, products will still be pages. However, all products will utilise the same Padloper template. These will be invisible to the shop users themselves (e.g., hidden in admin tree). Only superusers will have full control of the Padloper system stuff. Support The current Padloper will continue to be supported until the new Padloper 2 is released. New features will be included in Padloper 2 only. Once Padloper 2 is released, legacy Padloper will only receive security fixes. All other support will cease. Upgrade There will be no upgrade path from the current Padloper to Padloper 2. Although their underlying architecture is the same, making sure that everything works in different setups and environments will be time consuming. However, for those who really need to migrate, if time allows and for an agreed fee, I could develop a custom script for the migration. Backend A new backend interface will be the major visual difference between the existing Padloper and Padloper 2. It goes beyond visual differences though. The new backend will be the single gateway for managing all shop-related features, both current and new ones. The backend will unify and include: · Easily add shop products. · Ability to add as little or as many custom fields to products as required (title, SKU, price, discount field, image/photo, description, categories, tags, etc). · Discounts manager (including auto start/expire discount codes). · Customers manager. · Invoices manager. · Taxes management. · Payment gateways manager. · Improved digital products management. · Stock management. · Manual order creation. · Graphical sales report. · Customer support. · Access-controlled shop editors/staff. · Dashboard for shop metrics. · Shop settings. · Product variations. · Import/export products as CSV or JSON. · Products search/filter. · Etc. Users will be able to turn off backend features that they do not need. This will enable a more streamlined experience for users. I plan to release Padloper 2 within 4 - 6 months, hopefully sooner. This is a major undertaking, hence the timescale. Please note that the first release of Padloper 2 will not include all of the above planned features. The idea is to build incrementally, adding new features in minor updates, focusing on stability, usability and security. Frontend Past requests have included the development of a full featured frontend shop. This is planned for Padloper 2. However, this will be an optional pro module priced separately from Padloper itself. The ability to build own frontend shops using Padloper API will still continue. For those who want a plug-n-play solution, this frontend shop will come in handy. The frontend shop profile will feature an ajax-powered shopping cart and a customisable ready-to-go theme. Pricing Model There are no plans to change the current prices of the 3 Padloper licences (Single, Developer and Agency). However, in order to continue to provide Padloper as a stable product with great features, it is also important that it remains a competitive and financially sustainable project. In order for this to happen and to also bring Padloper in line with my existing pro modules, the pricing model itself has to change. Starting from Padloper 2, the pricing model will shift to an ‘annual subscription’ model rather than the current ‘lifetime licence model’. I am fully aware that there are different opinions for and against annual subscriptions. However, I believe that this model is the most equitable approach that suits both the developer and the clients. The annual subscription will allow users (licence holders) to get 12 months of free VIP support for Padloper as well as future updates available within that time period. After the 12 months, users will be able to renew (online) their subscription at a discounted cost (worked as a fraction of the full purchase price) for a further 12 months (perpetually). Users will be able to continue to use Padloper for life even if they don’t renew their subscriptions. Upgrading current licences to Padloper 2 will be a paid upgrade. Current users of Padloper will get an attractive discount. This will be a time-limited offer (maybe a couple of months) that will start with the release of Padloper 2. New customers will pay the full price for Padloper 2. I hope the planned features are reason enough for you to consider upgrading to Padloper 2. Payment Modules I will be taking over as the maintainer and lead developer of the existing payment gateways (Payment base class, PayPal and Stripe). New payment modules are also planned. Payment modules will continue to be free. However, only ProcessWire 3+ support will be provided going forward. Padloper Domain and Future Downloads I have also taken charge of the Padloper domain. Within the next 12 months, purchase and download of Padloper will shift to processwireshop.pw. Please note that this is not the official shop for ProcessWire! It just bears a name that reflects its product offerings ?. Eventually, traffic to padloper.pw will redirect to processwireshop.pw. Feedback I would love to hear your thoughts about the upcoming changes and any feature requests you might have for Padloper 2. Whilst I cannot guarantee that any request will be implemented, I can promise that I will thoughtfully consider all feedback. Thanks for reading and thank you for supporting Padloper! kongondo1 point
-
@bernhard I was just looking at https://doc.nette.org/en/2.4/dependency-injection and the instructions are remarkably well explained. Just learned a bunch of stuff reading that.1 point
-
1 + 2 yes, but this one sounds strange. But it depends a lot on how you plan everything. I'd start with a simpler module first to get familiar with module development. This one would be a nice project to start with: You'd have some user input (teaser: image uploads can be tricky), some methods to generate markup, http requests via the API, but not all the complexity that you have in your full blown module. I'd always be available for help on the road ?1 point
-
Good luck @kongondo, although I haven't used it yet, Padloper looks pretty sweet.1 point
-
I've used Nette image functions for that once I needed it: https://doc.nette.org/en/2.4/images#toc-image-modification but your linked library looks more advanced ?1 point
-
I just saw this. I was able to install on a new website using the instructions detailed by @darrenc. Thanks1 point
-
1 point
-
@kongondo As for me it should be changed to setArray as in current implementation WireArray() looks like an alias (in most scenarios) for WireArray->import and not new WireArray().1 point
-
No, it behaves in that way when different 'keys' have the same values. One more example $array = WireArray([ 'test' => 'test1', 'test1' => 'test1', 'test2' => 'test2' ]); d($array); count => 2 items => array (2) - test => "test1" (5) - test2 => "test2" (5) Actually, I think that it's a bug in WireArray() function as if use new WireArray() it works as expected $test = [ 'test' => 'test1', 'test1' => 'test1', 'test2' => 'test2' ]; $array = new WireArray(); $array->setArray($test); d($array); count => 3 items => array (3) - test => "test1" (5) - test1 => "test1" (5) - test2 => "test2" (5) Here is code for WireArray(); public static function newInstance($items = null, $class = '') { if(empty($class)) $class = get_called_class(); /** @var WireArray $a */ $a = new $class(); if($items instanceof WireArray) { $items->wire($a); $a->import($items); } else if(is_array($items)) { $a->import($items); } else if($items !== null) { $a->add($items); } return $a; } So the issue is in the usage of import method as it skips any items already present in WireArray.1 point
-
Hi Sam, there is PageimageManipulator available for PW: With it you can do something like: $imageBase = // get the base image here $imageOverlay = // get the thumbnail overlay image here // first wrap a 50px transparent (rgba) canvas around the overlay image and convert it to png format: $imageOverlay = $imageOverlay->pim2Load("prefix")->canvas($imageOverlay->width + 100, $imageOverlay->height + 100, array(0,0,0,0), "center")->setOutputFormat("png")->save(); // now stretch the overlay image canvas to match the base image dimensions: $imageOverlay = $imageOverlay->pim2Load("prefix")->canvas($imageBase->width, $imageBase->height, array(0,0,0,0), "northwest")->save(); // now merge both images together $imageResult = $imageBase->pim2Load("prefix")->watermarkLogo($imageOverlay, "center", 0)->save(); // The resulting pageimage can also be adjusted using regular pw image functions, if necessary $imageResultThumb = $imageResult->width(150); Better is to chain the manipulations of the overlay image into one call: $imageBase = // get the base image here $imageOverlay = // get the thumbnail overlay image here // two steps to create the canvas with the thumb at the desired position and convert the output to png format: $imageOverlay = $imageOverlay->pim2Load("prefix")->setOutputFormat("png")->canvas($imageOverlay->width + 100, $imageOverlay->height + 100, array(0,0,0,0), "center")->canvas($imageBase->width, $imageBase->height, array(0,0,0,0), "northwest")->save(); // now merge both images together $imageResult = $imageBase->pim2Load("prefix")->watermarkLogo($imageOverlay, "center", 0)->save(); Please refer to the PageimageManipulator page for the documentation of all methods and params, but ask here, if something is not clear. ?1 point
-
Also take a look at Ryan's helpful ProcessHello demo module: https://github.com/ryancramerdesign/ProcessHello It's a lot simpler to use getModuleInfo() (or its equivalent if using the other module configuration options Horst linked to above) to specify the page used by the Process module because then it will be automatically created on module install and removed on module uninstall. See the ProcessHello example: https://github.com/ryancramerdesign/ProcessHello/blob/9c1aa18eb40d069c7fb227f09badddc90f0b3276/ProcessHello.info.php#L41-L461 point
-
I think you are mixing ProcessModules with Other modules. The class skeleton of a process module has to look like this: class ProcessGoodNews extends Process implements Module { ... First add the prefix "Process" to your modules classname and also to the filename, and second you have to extend Process and not WireData. PS: the title can stay as is, Good News, or GoodNews, but the class name and filename should be prefixed with Process. PPS: I think you also have to prefix the execute() function name by three underscores: ___execute() < Thats wrong, as @teppo mentioned below. Sorry. PPPS: maybe some interesting reads about ProcessModules and different possibilities: https://processwire.com/blog/posts/building-custom-admin-pages-with-process-modules/ https://processwire.com/blog/posts/new-module-configuration-options/ https://processwire.com/talk/topic/13618-does-using-dedicated-module-files-help-a-lazy-programmer/1 point
-
I've just created a frontendtheming module that does take the source files of uikit (in a separate module to be up-/downgradeable), injects custom LESS files as you want and parses it on the fly with a PHP less parser. That works really well. It's not something I can release soon, but maybe someone wants to take the same approach for the backend theme. Just had a look and this approach would also be possible for the backend as Ryan is also using LESS to compile the pw backend css: https://github.com/ryancramerdesign/AdminThemeUikit/blob/master/uikit/custom/pw.less https://github.com/ryancramerdesign/AdminThemeUikit#uikit-css-development-with-this-admin-theme1 point
-
As @adrian said – that's why AdminThemeBoss is built just as a CSS Layer on replacing the AdminThemeUikit CSS… But the way to go there is pretty difficult – with the current AdminThemeUikit it's hard to find the relevant LESS variables and use these or override stuff... Also hooking into AdminThemeUikit and replacing or extending stuff is not strait forward. Would love to see more in this regard, a Theme that is built with extensibility in mind.1 point
-
Hi LAPS, based on the error message it appears the file your function lives in is not namespaced. So your type-hint Page $item is telling PHP that you expect an object of the class Page in the global namespace. All ProcessWire classes are in the Processwire namespace (check out the namespace declaration at the top of any given core PHP file). So you need to type-hint against the class in that namespace: function functionName(\Processwire\Page $item) { //... } Alternatively, you can alias the Page class for the file with your function, or put that file in the Processwire namespace itself. At the top of the PHP file, add one of those: // importing the Page class, this tells PHP that `Page` should be interpretated as `\Processwire\Page` <?php use \Processwire\Page; // namespacing the file itself, so ´Page´ will resolve to ´\Processwire\Page´ <?php namespace Processwire; Hope this works for you. Cheers!1 point
-
Hey! I'm developing a module for easy and fast frontend development (something like theming) and implemented favicons yesterday. Referencing one favicon for browsers is one thing, but referencing all different versions another... I used http://www.favicomatic.com/ that generates all the favicons for me and it also creates a code.txt file with the necessary markup. Now in my module I just have to upload a favicon to their website and place all the files in a specific folder of my module. Everything else is done automatically. That's quite nice, but I was wondering how you guys are doing it? Is my linked service a good choice? There is also https://realfavicongenerator.net/ but this has the drawback for me that it does not generate a codefile so I'd have one extra step in the process. Thx for your opinions!1 point
-
i just use a function to output the code from realfavicongenerator function favicons($options = []) { $defaults = array( 'appleColor' => '#4d9db4', 'msColor' => '#da532c', 'themeColor' => '#ffffff', 'path' => null ); $options = array_merge($defaults, $options); $folder = 'site/templates/favicons/'; if($options['path']) $folder = $options['path']; $folderUrl = wire('config')->urls->root . $folder; $filePath = wire('config')->paths->root . $folder; if(!is_dir($filePath)) return; $out = <<<OUT <link rel="apple-touch-icon" sizes="180x180" href="{$folderUrl}apple-touch-icon.png"> <link rel="icon" type="image/png" href="{$folderUrl}favicon-32x32.png" sizes="32x32"> <link rel="icon" type="image/png" href="{$folderUrl}favicon-16x16.png" sizes="16x16"> <link rel="mask-icon" href="{$folderUrl}safari-pinned-tab.svg" color="{$options['appleColor']}"> <link rel="shortcut icon" href="{$folderUrl}favicon.ico"> <link rel="manifest" href="{$folderUrl}site.webmanifest"> <meta name="msapplication-config" content="{$folderUrl}browserconfig.xml"> <meta name="theme-color" content="{$options['themeColor']}"> OUT; return $out; }1 point
-
@ryan Another thought - I quite like using @wumbo's FieldtypeAssistedURL (https://processwire.com/talk/topic/10530-module-fieldtypeassistedurl/) - or more accurately, my fork of it: https://github.com/adrianbj/processwire-fieldtype-assisted-url/commits/master which adds support for storing local pages as ID, not URL. I would hate to have to choose between your new URL field as this one. I wonder if you'd consider adding the functionality of the assisted url field to your new one?1 point