Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/18/2019 in all areas

  1. Thank you all for helping me with this! the culprit has been found. An “<a>” that should have been an “</a>” further up the page (which must have been there for months uncaught). Strange that today revealed symptoms of it for the first time! I feel silly, but I’m glad that’s over! thanks again!
    4 points
  2. While I also mostly use the alternative syntax for the very same reason, it is also worth noting that (at least on the frontend) Tracy Debugger's Validator panel is quite handy. I keep using it all the time.
    3 points
  3. This worked for me: <?php namespace ProcessWire; include("../index.php"); //bootstrap ProcessWire // var_dump($wire->pages); $http = new WireHttp(); var_dump($http);
    3 points
  4. aaah, the missing 'namespace ProcessWire;' was the problem. thx!
    2 points
  5. I had the same problem, hope this will help someone. :) I have a multi language website with templates containing a page field called Tags, that the user can create on the fly. Here's a quick way to activate the secondary languages when a new tag is created, using the cool IftRunner module. Install the module https://github.com/ryancramerdesign/IftRunner Create a .module name inside /site/modules/IftRunner using the code below. Install this module "IftActionActivePageLanguages" you just created Go to Setup > Trigger/Action inside the admin dashboard Create a new trigger for a "Pages::saved" hook like the one shown in the screenshot.
    2 points
  6. Here's a demo that loosely reproduces your screenshot: https://codepen.io/jacmaes/pen/qBWJrRY
    2 points
  7. Hi! I have an issue when I upload an image into the field. This is one of the log messages ("matalampi" is the crop setting? /../site/assets/files/1328/testiimagenodashes-1.-matalampi.0x48.jpg - Unable to copy /../site/assets/files/1328/testiimagenodashes-1.-matalampi.jpg => /../site/assets/cache/WireTempDir/.PFM0.06231100T1568804893RIbnRkmpEmZApFan/0/testiimagenodashes-1.-matalampi.0x48.jpg Same error comes from all variations. Apparently the field tries to create file variations from the image immediately after upload, but for some reason it doesn't work and a corrupted file is created. This becomes a problem when I try to check if there exists a cropped version of the image: It thinks that there is one, but since it's corrupted, image doesn't show in site. Is there a way to prevent variations being created before user actually crops the image? var_dump of image, if it helps anything ("matalampi", "korkeampi" and "normaali" are crop settings? object(ProcessWire\Pageimage)#370 (13) { ["url"]=> string(71) "/../site/assets/files/1448/img.-normaali.jpg" ["filename"]=> string(100) "/../site/assets/files/1448/img.-normaali.jpg" ["filesize"]=> bool(false) ["description"]=> string(0) "" ["tags"]=> string(0) "" ["created"]=> string(18) "17.9.2019 14:09:57" ["modified"]=> string(18) "17.9.2019 14:09:57" ["filemtime"]=> string(17) "1.1.1970 02:00:00" ["width"]=> int(0) ["height"]=> int(0) ["suffix"]=> string(0) "" ["original"]=> string(34) "img.jpg" ["variations"]=> array(5) { [0]=> string(40) "img.0x260.jpg" [1]=> string(50) "img.-matalampi.0x48.jpg" [2]=> string(50) "img.-korkeampi.0x48.jpg" [3]=> string(50) "img.-normaali.670x0.jpg" [4]=> string(49) "img.-normaali.0x48.jpg" } } "filemtime" looks kinda fishy: Does it affect anything? Edit: Aaand as soon as I decided to ask this, it seems that the issue was that my crop setting names had capital letters! So heads up for that to others as well ?
    2 points
  8. OK. Here it is. ProcessWire 3.0.123 PHP 7.2.18 1. In "product" template turned on "Allow URL segments". 2. Made text field: "hash_field" and added it to product.php template. Set visibility to: Open when populated + closed when blanked + Locked (not editable) In site/ready.php <?php namespace ProcessWire; //* conf segments of URL of template product *// wire()->addHook("Page(template=product)::path", function($e) { $page = $e->object; $e->return = "/{$page->name}/pr/{$page->hash_field}/"; }); Made a module "AddHashField" with simple hash CRC32 to populate "hash_field" so the URL will be as short as possible. <?php namespace ProcessWire; class AddHashField extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Add Hash Field', 'summary' => 'Populate hash field of product template', 'href' => '', 'version' => 001, 'permanent' => false, 'autoload' => true, 'singular' => true, ); } public function init() { $this->pages->addHookBefore('saveReady', $this, 'AddHashField'); } public function AddHashField($event) { $page = $event->arguments[0]; if($page->template != 'product') return; if(!$page->hash_field){ date_default_timezone_set('Europe/Podgorica'); $new_date = new \DateTime(date('Y/m/d 00:00')); $formated_date = $new_date->format(YmdHi); $hash = hash('CRC32', $formated_date); $page->set('hash_field', $hash); $this->message("Hesh is set " . $page->hash_field); } } } Now, I am using 'views/' folder for templating so, instead of using home.php I am placing this on the top of the _main.php file. <?php namespace ProcessWire; if ($input->urlSegment1 && $input->urlSegment2 == 'pr' && $input->urlSegment3) : $pagename = $input->urlSegment1; $hash = $input->urlSegment3; $match = $page->findOne("template=product, name={$pagename}, hash_field={$hash}"); if (!$match->id || !$match->hash_field) throw new Wire404Exception(); include("./head.inc"); include('./nav.php'); include('views/' . $match->template->name . '.php'); include("./foot.inc"); // echo $match->render(); // problem with looping page endlessly + fatal error return $this->halt(); endif; NOTE: echo $match->render is causing massive looping until Fatal Error. I don't have idea what is causing that. Tried to turn off xdebug etc. Special THANKS to @Zeka
    2 points
  9. Modules Manager 2 provides an easy to use interface to download, update, install, uninstall and configure modules. It is meant to provide an optimized alternative to the core ProcessModule dashboard. Maybe @ryan agrees to merge it to the core at some point when it is finished and polished. Features: Seamlessly download, update, install, uninstall or delete modules Live-Search (aka find as you type) for module names Live-Search (aka find as you type) for categories Browse new and unkown modules from the modules directory on modules.processwire.com Choose your favorite layout (cards, reduced cards, table) Modern UIKit design (therefore only works with AdminThemeUikit) Caches the module list from modules.processwire.com directory locally. What is Modules Manager 2? Why a new module manager? Some people including myself think that the actual module installation in ProcessWire could be improved in some places. Make it easy for ProcessWire beginners and power users Offer better discoverbility to find the right module Make it easier and faster for powerusers to manage modules A manager that list all official modules is a feature, that many other frameworks/CMS's like ModX, WordPress or PrestaShop have by default. What are the disadvantages of the actual core module interface? Installation of a module is not very user-friendly: You have to be aware where to get new modules, search for a module, copy or remember the module name or URL, go back to your ProcessWire installation, paste the module name(URL, click on "get module info" and finally install the module It only displays installed modules, not the ones that are available in the modules directory Uninstalling a module requires you to go to the module detail page, click a checkbox and then submit the change. After that you have to go back to the module overview page. It only displays installed modules, not the ones that are available in the modules directory, so it makes discovering modules hard BETA software Use this module at your own risk. I am not responsible for any damage or unexpected behaviour. Some things might not work fully, please see the TODO list for details. I need your feedback and help This module is still in development and I am happy to discuss with you and get some feedback. What do you like? What is missing? What could make the process even easier? Ask, suggest or provide pull requests. You can download the module at https://modules.processwire.com/modules/modules-manager2/ or from Github: https://github.com/jmartsch/processwire-modules-manager2
    1 point
  10. Assign edit access to individual users on a per-page basis The user must already have page-edit permission on one of their roles in order to get edit access to assigned pages. Otherwise, they will only gain view access. This module is fully functional as-is, but intended as a proof-of-concept for those wanting to go further with adding custom edit and/or view access. The main functionality in this module consists of only a few lines of code, so it should be a simple edit for anyone wanting to take it further. If you make some useful additions to it, please post them. How to use Create a new role called "editor" (or whatever you want to call it) and give the role "page-edit" permission. If you already have a role in place that you want to use, that is fine too. Under "Access > Users" locate the user you want to assign edit access to. Edit this user. For this user's "Roles" field: choose the new role you added, "editor" (or whatever you called it). For this user's "Editable Pages" field: select one or more pages you want them to be able to edit. Save the user and you are done. To test, login as the user you edited to confirm it works how you expect. http://modules.processwire.com/modules/page-edit-per-user/ https://github.com/ryancramerdesign/PageEditPerUser
    1 point
  11. // Source: https://processwire.com/talk/topic/3553-handling-categories-on-a-product-catalogue/?do=findComment&comment=37833 $manufacturers = new PageArray(); foreach($pages->findMany("template=carTemplate") as $car) { // add manufacturer from car page to manufacturers array $manufacturers->add($car->manufacturerField); }; // do whatever you want with the $manufacturers pages Something like this?
    1 point
  12. /documents/human-resources/ is a page accessible because it has a template file and is published. If you set, to its children, a template that does not have a template file (.php), they will not be accessible directly. So you can use the API to get their content and show to the user. So: /documents/human-resources/ works /documents/human-resources/document-name will get a 404
    1 point
  13. AdminBar 2.4.0 is out and adds support for the "data-adminbar-adjust" attribute. The idea here is to automatically modify (or adjust) certain CSS properties whenever the height of the Admin Bar is recalculated. Note: AdminBar already automatically adds "padding-top: [Admin Bar height in px]" to the <html> element, so this feature mainly applies to elements with "position: fixed". Assuming that Admin Bar is displayed and is 100px tall at the moment, the following markup... <div data-adminbar-adjust="top max-height"></div> ... would result in this: <div style="top: 100px; max-height: calc(100% - 100px);" data-adminbar-adjust="top max-height"></div> Thanks to @Fokke for the idea ?
    1 point
  14. This.. I run into little things like this all the time. It is one reason I moved away from outputting html in code/text blocks (unless it it really small), and moved to the php alternant syntax: <?php foreach ($services as $service) : ?> <div class="service"> <h1>Sample Text</h1> <p><a href="<?php echo $service->url; ?>"><?php echo $service->title; ?></a></p> </div> <?php endforeach; ?> At least to me, it is much easier to find an issue since Visual Studio Code doesn't highlight the different syntax in code/text blocks (unless there is a way and I cant find it). I might start integrating twig into my templates as well. I do like the syntax, but I have not really tried it out much.
    1 point
  15. @lokomotivan public function executeEdit() { // breadcrumb $this->fuel->breadcrumbs->add(new Breadcrumb($this->page->url, $this->page->title)); // Execute Page Edit $processEdit = $this->modules->get('ProcessPageEdit'); return $processEdit->execute(); } Probably you will use something similar for adding new pages, but with ProcessPageAdd. So, by using this aproach for adding new pages you will get an issue with initial languages values. As you can see all non-default languages are active. But, after page save they become disabled. I was trying to resolve this issue but with no luck. So I went with creating addition page under the module page which reffers to custom module which extend ProcessPageAdd class ProcessCustomPageAdd extends ProcessPageAdd { /** * @return array * */ public static function getModuleInfo() { return array( 'title' => __('Page Add', __FILE__), 'summary' => __('Add a new page', __FILE__), 'version' => 108, 'permanent' => true, 'permission' => 'page-edit', 'icon' => 'plus-circle', // 'useNavJSON' => true, ); } public function init() { if ($this->wire('page')->parent->name == 'news-manager') { $this->set('parent_id', 1036); } else { ..... } } } Hope it helps.
    1 point
  16. <div class="headshot center-block" style="background-image:url('<br /> <b>Notice</b>: Trying to get property of non-object in <b>/home/drumming/ams.staging.gooi.ltd/site/assets/cache/FileCompiler/site/templates/home.php</b> on line <b>214</b><br /> ');"></div> Not the reason but something I found twice in your code and you might want to fix ?
    1 point
  17. @lokomotivan Not sure, but try to implement WirePageEditor by your module class class YourCustomLister extends Process implements WirePageEditor
    1 point
  18. I recommend Sarah Edo's grid generator. This is what I used for my quick demo: https://cssgrid-generator.netlify.com/
    1 point
  19. I might be cutting some corners here, but I'm just lazy. Tables.
    1 point
  20. Hello Horst, thanks again for your hints, the following solution works for me, replaces the original image and the merged image is usable in the editor afterwards: (Code is embedded in ready.php) $this->addHookBefore("InputfieldImage::fileAdded", function ($event) { // Get the image $image = $event->argumentsByName('pagefile'); // Location of watermark image $pngAlphaImage = $config->paths->templates . 'watermark.png'; //Save image with watermark $master = $image->pim2Load('full', false)->watermarkLogo($pngAlphaImage,'SE', 0)->pimSave(); //Setting some variations $sizeArray = array(array(448, 336), array(678, 506), array(908, 676)); foreach($sizeArray as $sizes) { $master->size($sizes[0], $sizes[1], array('upscaling'=>false, 'cropping'=>false, 'quality'=>90, 'sharpening'=>'soft')); } $image->setFilename($master); //Set generated image as main image $image->setOriginal($master); });
    1 point
  21. You could just rename this to "Inactive" (downloaded and not installed) and "Active" (downloaded and installed).
    1 point
  22. @Leftfield The path hook should be in ready.php and all logic should be in home.php or in the template file that you are using for root page. if (input()->urlSegment(1) && input()->urlSegment(2) == 'pr' && input()->urlSegment(3)) { $pagename = input()->urlSegment(1); $hash = $input()->urlSegment(3); $match = pages()->findOne("template=product, name={$pagename}, hash_field={$hash}"); if (!$match->id) throw new Wire404Exception(); echo $match->render(); return $this->halt(); } Also you should enable URL segment for root ( home ) page. Generate hash and save it to hash field you can by hooking in Pages::saveReady. You can find a lot of exaples in the forum.
    1 point
  23. Teppo, I'm writing my first message on this forum (hi!) to thank you for this module. I'm testing it on a small local test site at the moment, and really liking it so far!
    1 point
  24. I just wanted to mention. I found a new job as a front-end dev at https://backslash.ch. Since 2 months there already and enjoy it. We work with in-house CMS specially targeted to governments. So a lot less PW for me in the future, but I'll use it as my tool for private projects.
    1 point
  25. Nginx' performance advantages over Apache were built on three factors: modern-day multiprocessing in the server, a lot less overhead due to reduced functionality and memory caching. Over the last five years, Apache has greatly reduced that gap by adapting Nginx' multiprocessing approach (one keyword there is the event MPM module), so Apache isn't spending most of its time spinning up and tearing down whole server instances anymore. File system access has greatly improved with solid state disks, too. Apache still has a lot more functionality, and its distributed config file approach, most prominently the ability to make configuration changes with a .htaccess file inside the web directories, hurts performance. Its dynamic module loading approach and the dozens of pre-installed modules most distributions ship also take up processing time and memory. Nowadays, Apache can be stripped down a lot and compiled to be head to head with Nginx, though few actually care to do that, since it also means removing functionality one might need in the future. A stock Apache is usually still quite a bit slower and reaches its limits faster (about the factor 2). This becomes an issue under heavy load or on slow machines. Where Nginx still shines brightly is load balancing. Apache can do it too, but with Nginx it is straight forward and well documented, having been there for a long time. For those interested in a bit of (highly subjective) history: for a long time (speak eighties and nineties), the classic forking mechanism that was common on *nix OSes was the way to do multiprocessing in network servers, and therefore in Apache too. This meant spawning a full copy of the server process and initializing it, then tearing it down when the request was done. Apache brought a small revolution to that approach by implementing preforking, meaning to keep spare server instances around to fulfill requests with little delay. After a while, there were other approaches too when faster multiprocessing approaches become part of common operating systems, like multi threading, which is supported by Apache's "worker" multiprocessing module (MPM). There were, however, big caveats with using other MPMs. Since file systems used to be slow, sometimes awfully so, in the old days, and since the classic CGI approach of starting an executable from the file system, supplying it with information through environment variables and standard input and capturing its standard output was a security nightmare - even without thinking about shared hosting - nifty programmers included full languages interpreters inside Apache modules. mod_perl and mod_php became the big thing, the latter coming to dominate the web after a few years. These interpreters, though, often had memory leaks and issues with thread isolation, meaning at best that an error in one thread tore down numerous other sessions and at worst that the server had a propensity for information leaks, remote code execution and privilege escalation attacks, the former security nightmare squared. Thus, these tightly integrated interpreters more or less locked their users into the classic prefork approach where every instance is its own, basically independent process. With PHP as the market leader not evolving in that regard, things were frozen for quite some time. This was when Nginx conquered the market, first by serving static HTML and associated resources with lightning speed (CMSes generating static HTML were still a big thing for a while), but soon by taking care of all the static stuff while handling the dynamic things off to Apache and caching parts of its responses in memory. Finally, though, PHP finally got a fresh boost and grew stable enough for its engine to re-use interpreter instances. It was easier to contain things inside an interpreter-only process instead of dealing with all the server peculiarities, so FastCGI daemons finally became stable, known and used, and suddenly the need to have the language interpreter contained in the web server fell away. Apache got leaner and Nginx more flexible. Caching servers like Varnish became popular since it suddenly was relatively easy to build a fast, nice, layered caching solution with a combination of Nginx, Varnish and a full fledged web server like Apache or IIS, able to serve thousands of highly dynamic and media rich pages per minute. About that time, SSL grew in importance too, and hosting providers learned to love Nginx as a means to route domains to changing backends and provide fast and easily configurable SSL endpoint termination. Over the last years, Nginx got other features like generic TCP protocol load balancing that offset it from other servers and make it more into a one-stop solution for modern web applications. It does boost its popularity that Nginx is often the first (or the first major) web server to ship evolving technologies, making the front pages and pulling in early adopters, http/2 being one of the most prominent examples there.
    1 point
  26. Hi @Hardoman I post you a code example below that works for me since ages and also with recent PW versions. It includes watermarking too! It is called in a custom module and the event is >before "InputfieldFile::fileAdded" <, but you can call it in ready.php too. Hopefully it is of help for you. Otherwise please ask further. :) public function importImage($event) { $inputfield = $event->object; // handle to the image field if(!$inputfield instanceof InputfieldImage) { // we need an images field, not a file field return; // early return } if(version_compare(wire('config')->version, '2.8.0', '<')) { $p = $inputfield->value['page']; // get the page, PW < 2.8 } else { $p = $inputfield->attributes['value']->page; // get the page, PW >= 2.8 | 3.0 (or only from 3.0.17+ ??) } if('images' != $inputfield->name) return; // we assume a field with name: images if('album' != $p->template) return; // don't do it on other pages than archive album $image = $event->argumentsByName('pagefile'); // get the image // prebuild variations // AdminThumb $image->height(260); // AlbumThumbnail $portrait = $image->height > $image->width; $w = 228; if($portrait) { $w1 = intval($w); $h1 = intval(($w1 / 3 * 4) + 38); } else { $w1 = intval($w); $h1 = intval(($w1 / 3 * 2)); } $image->crop("width=$w1, height=$h1"); // Slick-Slideshow $wmPng = $this->pages->get('id=13967')->getUnformatted('watermark')->first()->width(403); // sharpening added, quality from 80 to 90 $master = $image->contain('width=1000, height=700, quality=100, sharpening=none'); $master = $master->pim2Load('full', false)->watermarkLogo($wmPng)->setQuality(100)->setUpscaling(true)->setSharpening('none')->pimSave(); $sizeArray = array(array(448, 336), array(678, 506), array(908, 676)); foreach($sizeArray as $sizes) { $master->size($sizes[0], $sizes[1], array('upscaling'=>false, 'cropping'=>false, 'quality'=>90, 'sharpening'=>'soft')); } // sharpening added, quality from 80 to 90 // prebuild variations // check / import IPTC data $additionalInfo = array(); $info = @getimagesize($image->filename, $additionalInfo); if($info !== false && is_array($additionalInfo) && isset($additionalInfo['APP13'])) { $iptc = iptcparse($additionalInfo["APP13"]); if(is_array($iptc) && isset($iptc["2#025"]) && is_array($iptc["2#025"]) && count($iptc["2#025"])>0) { $tmp = $iptc["2#025"]; $tags = array(); foreach($tmp as $k=>$v) { if(empty($v)) continue; $tags[] = jhpTextConversion(trim(strtolower($v))); } $p->images->trackChange('tags'); // prepare page to keep track for changes $image->tags = implode(', ', $tags); $p->save('images'); // save the page } } // check / import IPTC data }
    1 point
  27. Just tried tailwindcss as base stylesheet for mpdf ? <?php require_once __DIR__ . '/vendor/autoload.php'; $mpdf = new \Mpdf\Mpdf([ 'defaultCssFile' => 'css/prod.css', 'format' => [190, 236], 'orientation' => 'L', 'default_font' => 'arial', ]); $mpdf->WriteHTML('<h1 class="text-center bg-blue-700 text-yellow-200 mx-64 py-2 font-extrabold text-3xl rounded-full">Hello world!</h1>'); $mpdf->WriteHTML('<svg class="stroke-current text-purple-500 inline-block h-12 w-12" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="8" cy="21" r="2"></circle> <circle cx="20" cy="21" r="2"></circle> <path d="M5.67 6H23l-1.68 8.39a2 2 0 0 1-2 1.61H8.75a2 2 0 0 1-2-1.74L5.23 2.74A2 2 0 0 0 3.25 1H1"></path> </svg>'); $mpdf->WriteHTML('<div class="my-10 bg-red-100 border border-8 border-red-400 text-red-700 px-12 py-3 rounded relative" role="alert" style="border: 1px solid red"> <strong class="font-bold">Holy smokes!</strong> <span class="block sm:inline">Something seriously bad happened.</span> <span class="absolute top-0 bottom-0 right-0 px-4 py-3"> <svg class="fill-current h-6 w-6 text-red-500" role="button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>Close</title><path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/></svg> </span> </div>'); $mpdf->WriteHTML('<hr class="my-2">'); $mpdf->WriteHTML('<div class="bg-indigo-900 text-center py-4 lg:px-4"> <div class="p-2 bg-indigo-800 items-center text-indigo-100 leading-none lg:rounded-full flex lg:inline-flex" role="alert"> <span class="flex rounded-full bg-indigo-500 uppercase px-2 py-1 text-xs font-bold mr-3">New</span> <span class="font-semibold mr-2 text-left flex-auto">Get the coolest t-shirts from our brand new store</span> <svg class="fill-current opacity-75 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/></svg> </div> </div>'); $mpdf->WriteHTML('<div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4" role="alert"> <p class="font-bold">Be Warned</p> <p>Something not ideal might be happening.</p> </div>'); $mpdf->WriteHTML('<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"> Button </button>'); $mpdf->WriteHTML('<div class="max-w-sm rounded overflow-hidden shadow-lg"> <img class="w-full" src="/img/card-top.jpg" alt="Sunset in the mountains"> <div class="px-6 py-4"> <div class="font-bold text-xl mb-2">The Coldest Sunset</div> <p class="text-gray-700 text-base"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus quia, nulla! Maiores et perferendis eaque, exercitationem praesentium nihil. </p> </div> <div class="px-6 py-4"> <span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2">#photography</span> <span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2">#travel</span> <span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700">#winter</span> </div> </div>'); $mpdf->Output(); At least for paddings, margins, text-alignments and colors this could also be helpful. It seems borders do not work - the red border is done manually via "1px solid red" style rule.
    1 point
  28. Thanks for your answers and for you generously shared work, @teppo! I really like your Wireframe idea - a well-established and documented way to structure code in ProcessWire MVC way. So one could follow rules that are thought out and described. I understand quite well that in order for this to work one should give up some of his own preferences) Will be looking forward to next additions to the project we've been talking about here.
    1 point
  29. For the sake of marketing and brand recognition I'd recommend not going with a generic name such as pw + [some commerce related term]. Best known brand names tend to be unique, and they don't really have to have any connection with the company/product (although they can). Trader is a decent idea, but I still think that Padloper is better -- not to mention that it already has some brand value, and an awesome logo ? just my five cents.
    1 point
  30. Well, I use personally use UIkit 3 + TailwindCSS. Tailwind you are going to hear a whole lot more about in the upcoming months. I know you are talking about file size and this is exactly why UIkit + Tailwind is great! I don't really use UIkit's CSS at all. I just use the JavaScript because the amount of utilities they have manage to pack into 130kb (less than most images) is amazing. I'm talking Parallax, Sliders, Lazy Loading Images, Srcset utilities, placeholder generators, scrollspy, sticky. Most of which can be and usually is utilised in any project. I challenge you to get all of that under 130kb. Also it doesn't have any dependencies. Tailwind you can get down to about 3KB per website using Purge and Gulp/Webpack. It truly is amazing and Utility first CSS is such a refreshing way to work with CSS. I know all the arguments, and I'm a minimalist. I don't like all the classes too. But Tailwind's benifits out weigh the cost. 1. You don't have to struggle with inherited styles, or coming up with class names to define each section, some of which may look the same but be very different in content. 2. Consistency, it puts pressure on you to keep things consistent. So you hardly stray to loads of different type sizes and colours. 3. With Purge, you can have an entire website styles in ~3kb. I believe Ryan chose UIkit 3 because he has learnt the classes and is confident with the framework, and is impressed with the JavaScript utilities. And because you can build website much quicker using it. I see it as a hybrid between utility and a standard framework. It offers many utility classes. You could also Purge UIkit's CSS and get it around 160KB. I don't believe that this isn't a good fit for ProcessWire as you have previously mentioned. Many who use ProcessWire love UIkit. It follows the same philosophy: A powerful framework, that is easy to understand and learn to get things done quicker. Just like ProcessWire gives developers confidence in that they can do challenging bits of functionality easily with the power of ProcessWire's amazing API (like seriously, I built a real estate system using ProcessWire, something I wouldn't have dreamed of doing on any other platform). Well UIkit also gives that confidence to the front-end. But if I was to choose for myself, I much prefer pairing Tailwind for the CSS and UIkit for the JavaScript. That ~140KB (Tailwind + UIkit) covers me for most websites I build.
    1 point
  31. A quick tutorial how to create file downloads using pages You will be able to create a new page using template "PDF" (or any you setup), upload a pdf file. You then can select this page using page fields, or links in Wysiwyg. The url will be to the page and NOT the file itself. This will allow to keep a readable permanent unique url (as you define it), unlike /site/assets/files/1239/download-1.pdf, and you'll be able to update/replace the uploaded file without worring about its filename. Further more the file will also have an id, the one of the page where it lives. Clicking those links will download or open the file (when target="_blank") like it would be a real file on server with a path like /downloads/project/yourfile.pdf. You'll be also able to use the "view" action directly in the page list tree to view the file. Further more you'll be able to esaily track downloads simply by adding a counter integer field to the template and increase it every time the page is viewed. Since the file is basicly a page. This all works very well and requires only minimal setup, no modules and best of it it works in the same way for multi-language fields: Just create the language alternative fields like "pdf, pdf_de, pdf_es" and it will work without modifying any code! Still with me? ok PW setup Download folder: Create a template "folder" or "download-folder" with only a title needed. Create pages in the root like /downloads/project/ using this template. Setup the template for the pdf files 1. Create a new template in PW. Name it pdf 2. Goto template -> URLs tab and set the URL end with slash to no. (So we can have /path/myfile.pdf as the URL) 3. Create a new custom file field, name it pdf. Set its maximal count to 1 under -> Details tab. 4. Add the pdf field created to the pdf template. Easy. 5. Create a new "pdf" page using the pdf template under a download folder you created earlier. 6. Give it the title and in the name field add ".pdf" to the end (could also leave as is) Template PHP file for the pdf files 1. Create the template file pdf.php in your /site/templates folder 2. add the following code: <?php // pdf.php if($page->pdf){ wireSendFile($page->pdf->filename); } Done. To see the options you have with PW's wireSendFile() you can also overwrite defaults <?php // pdf.php if($page->pdf){ $options = array( // boolean: halt program execution after file send 'exit' => true, // boolean|null: whether file should force download (null=let content-type header decide) 'forceDownload' => false, // string: filename you want the download to show on the user's computer, or blank to use existing. 'downloadFilename' => '', ); wireSendFile($page->pdf->filename, $options); } Simple and powerful isn't it? Try it out. Some thoughts advanced Create as many file types as you like. It might also be possible to use one "filedownload" template that isn't restricted to one field type but evaluate it when being output using $page->file->ext, or save the file extension to the page name after uploading using a hook. One last thing. You can add other meta fields or preview images to the template and use those to create lists or detail pages. It's all open to goodness. Again all without "coding" and third-party modules. Further more you can use the excellent TemplateDecorator to add icons per template and have a nice pdf icon for those pages. This as a base one could also easily create a simple admin page for mass uploading files in a simple manner, and create the pages for the files automaticly. ImagesManager work in the same way. Cheers
    1 point
  32. OK, so I had a stab at this using $cache and ready.php. It seems to work fine. Throw the following code in ready.php or similar...The code and in-line comments are purposefully verbose to make it easy to follow. // Hook into login/logout sessions wire()->addHookAfter('Session::loginSuccess', null, 'checkLoggedIn'); wire()->addHookBefore('Session::logout', null, 'removeLoggedIn');// Hook before to get $user->id /** * Check if a user is already logged in * * If user logged in, take an action (notify,logout,etc). * Else, cache user as logged in to check for duplicate logins. * * @param HookEvent $event The object (Session::loginSuccess) we are hooking into. * @return void * */ function checkLoggedIn(HookEvent $event) { $user = $event->arguments('user'); $session = wire('session'); $userDuplicateLogin = checkUserDuplicateLogin($user);// returns boolean // if user logged in, do something. Here, we log them out and redirect to home page // you could make an exception for Superusers, or exception by role, permission, etc if($userDuplicateLogin) { $session->logout(); $session->redirect('/'); } // set cache else setLoggedInUserCache($user); /* @note: testing only $log = wire('log'); $log->save("user-logs","Successful login for '$user->name'"); */ } /** * Check if a user is logged in more than once. * * @param User $user The user to whose logins to check. * @return Boolean $duplicateLogIn True if user already logged in, else false. * */ function checkUserDuplicateLogin(User $user) { $cache = wire('cache'); $duplicateLogIn = false; $userID = $user->id; $cachedUsersIDs = $cache->get('loggedInUserIDs');// array OR null if(is_array($cachedUsersIDs) && isset($cachedUsersIDs[$userID])) $duplicateLogIn = true; return $duplicateLogIn; } /** * Create or update cache for logged in user. * * @param User $user The user whose cache to set. * @return void * */ function setLoggedInUserCache(User $user) { $cache = wire('cache'); $userID = $user->id; $cachedUsersIDs = $cache->get('loggedInUserIDs'); // cache does not exist, create empty array ready to cache if(!count($cachedUsersIDs) || is_null($cachedUsersIDs)) $cachedUsersIDs = array(); // save/update cache // for value, can use whatever, even $user->name; doesn't matter, key is the important thing here // outer array: we use $user->id to group same user; // in inner array, we use session_id() to ensure uniqueness when removing cache $cachedUsersIDs[$userID][session_id()] = $userID; $cachedUsersIDsStr = json_encode($cachedUsersIDs);// JSON to save as cache $cache->save('loggedInUserIDs',$cachedUsersIDsStr);// @note: set expiration of cache if you wish to } /** * Remove a logged out user's cache. * * This to allow subsequent logins. * * @param HookEvent $event The object (Session::logout) we are hooking into. * @return void * */ function removeLoggedIn(HookEvent $event) { $user = wire('user'); removeLoggedInUserCache($user); /* @note: for testing only $log = wire('log'); $log->save("user-logs","Successful logout for '$user->name'"); */ } /** * Remove the cache for a user who has logged out. * * @param User $user The user whose logged-in cache we are removing. * @return void * */ function removeLoggedInUserCache(User $user) { $cache = wire('cache'); $userID = $user->id; $cachedUsersIDs = $cache->get('loggedInUserIDs'); // cache does not exist/empty, nothing to do if(!count($cachedUsersIDs) || is_null($cachedUsersIDs)) return; // save/update cache // @note: we check for current logged in user but we remove the whole user group (outer array) // this is because the user logged in 'validly' is logging out. if(isset($cachedUsersIDs[$userID][session_id()])) unset($cachedUsersIDs[$userID]); $cachedUsersIDsStr = json_encode($cachedUsersIDs); // save updated cached $cache->save('loggedInUserIDs',$cachedUsersIDsStr);// @note: set expiration of cache if you wish to }
    1 point
×
×
  • Create New...