Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/12/2015 in all areas

  1. Get it from GitHub Setup Just put the module in you modules directory and install it via admin. Intro This module might come in handy if you like to keep your templates clean and free of unreadable and unmaintainable string concatenations and even free of any logic. It also comes with some handy features besides just embedding JS, CSS and image assets, see below. Yikes! <link href="<?php echo $config->urls->templates . 'styles/foo.css'; ?>"> <link href="<?php echo $config->urls->templates . 'styles/bar.css'; ?>"> <script src="<?php echo $config->urls->templates . 'scripts/foo.js'; ?>"></script> <script src="<?php echo $config->urls->templates . 'scripts/bar.js'; ?>"></script> <img src="<?php echo $config->urls->templates . 'images/sky-scraper.jpg'; ?>" alt="Some huge building"> <img src="<?php echo $config->urls->templates . 'images/owzim.jpg'; ?>" alt="Handsome!"> Way cleaner <?php echo $asset->css('foo'); ?> <?php echo $asset->js('foo'); ?> <?php echo $asset->img('sky-scraper.jpg', 'Some huge building'); ?> or with short syntax <?= $asset->css('bar') ?> <?= $asset->js('bar') ?> <?= $asset->img('owzim.jpg', 'Handsome!') ?> And prettier if you're using Twig {{ asset.css('foo') }} {{ asset.css('bar') }} {{ asset.js('foo') }} {{ asset.js('bar') }} {{ asset.img('sky-scraper.jpg', 'Some huge building') }} {{ asset.img('owzim.jpg', 'Handsome!') }} Usage JS example Let's use the js method an its configuration as an example, and assume we have the following files located in /site/templates/scripts - index.js - index.min.js - main.js $config->blick = array( 'jsPath' => $config->paths->templates . 'scripts', 'jsUrl' => $config->urls->templates . 'scripts', 'jsMarkup' => '<script src="{url}"></script>', 'jsDefault' => 'markup', 'jsVersioning' => true, 'jsVersioningFormat' => '?v={version}', 'jsMin' => true, 'jsMinFormat' => "{file}.min.{ext}", ); $asset = $modules->get('Blick'); $asset->js('index')->url; // returns /site/templates/scripts/index.min.js?v=1426170460935 // 'min' and version parameter added, which was fetched from the file modified date $asset->js('main')->url; // returns /site/templates/scripts/main.js?v=1426170460935 // without 'min', because there is no main.min.js $asset->js('main'); // returns <script src="/site/templates/scripts/main.js"></script> // because 'jsDefault' is set to 'markup' // you can also access it explicitly via $asset->js('main')->markup $asset->js('http://code.jquery.com/jquery-2.1.3.js'); // returns <script src="http://code.jquery.com/jquery-2.1.3.js"></script> // nothing is modified here, because it's a remote url You can use the file name with or without extension. Adding a version parameter only takes place, if jsVersioning is set to true, it's a local file and it exists. Modifying the file name to include min only takes place, if jsMin is set to true, it's a local file and it exists. The same applies for the $asset->css('file') method: $config->blick = array( 'cssPath' => $config->paths->templates . 'styles', 'cssUrl' => $config->urls->templates . 'styles', // and so on ... ); IMG example the img method lets you include images, crop and resize them, without them having to be a page image. $config->blick = array( 'imgPath' => $config->paths->templates . 'images', 'imgUrl' => $config->urls->templates . 'images', 'imgMarkup' => '<img {attrs} src="{url}" alt="{0}">', 'imgDefault' => 'markup', 'imgVariationSubDir' => 'variations', ); $asset = $modules->get('Blick'); $asset->img('sky-scraper.jpg')->url; // returns /site/templates/images/sky-scraper.jpg $asset->img('sky-scraper.jpg', 'Some huge building'); // returns <img src="/site/templates/images/sky-scraper.jpg" alt="Some huge building"> // any arguments following the filename are passed as an array // in this case the alt value is the 0th argument, so {0} get's replaced // you can set as many arguments as you want in 'imgMarkup' $asset->img('sky-scraper.jpg')->size(100, 100)->url; // returns /site/templates/images/variations/sky-scraper.100x100.jpg // the resized image is put into a subdir 'variations' as configured in 'imgVariationSubDir' // if 'imgVariationSubDir' is left empty, the variation will be put in the same directory $asset->img('sky-scraper.jpg', 'Some huge building')->attr('title', 'Some huge building'); // returns <img title="Some huge building" src="/site/templates/images/sky-scraper.jpg" alt="Some huge building"> // the resized image is put into a subdir 'variations' as configured in 'imgVariationSubDir' // if 'imgVariationSubDir' is left empty, the variation will be put in the same directory You can also setup predefined variation settings in imgVariations $config->blick = array( 'imgVariations' => array( 'header' => array( 'width' => 960, 'height' => 360, 'options' => array( 'suffix' => 'header', ), ), 'person' => array( // and so on ... ), ), ); And call it like so: $asset->img('sky-scraper.jpg')->variant('header')->url; // returns /site/templates/images/variations/sky-scraper.960x360-header.jpg $asset->img('sky-scraper.jpg')->variant('header', 50)->url; // returns /site/templates/images/variations/sky-scraper.480x180-header.jpg Attributes example Since version 0.4.0 you don't need to create arbitrary variable placeholders, if you want to use attributes only. Now you can use the {attrs} placeholder and set the attributes via $asset->attr('name', 'value'). The name argument can also be multiple names, split by a pipe |. $config->blick = array( // ... 'imgMarkup' => '<img {attrs} src="{url}">', // ... ); $asset->img('sky-scraper.jpg')->attr('alt|title', 'Some huge building'); // returns <img alt="Some huge building" title="Some huge building" src="/site/templates/images/sky-scraper.jpg" > Using files that are not in the configured directory If you want to include files, that are neither in the configured directory nor in one of its sub directores, just use an absolute path (actually, relative to your /site directory. $asset->js($config->urls->SomeModule . 'scripts/file-in-root'); Autoload the module If you don't want to include the module manually via $assets = $modules->get('Blick'); you can set it to be autoloaded under a custom name: $config->blick['autoloadAs'] = 'fiddle'; Now it becomes automatically available in your templates under the name fiddle $fiddle->css('foo'); $fiddle->js('foo'); $fiddle->img('baz.png', 'qux'); Please note, that, if you're using the TemplateTwigReplace.module you will have to add your chosen autoload name to the Auto-import fuel list on the module's config page. See config-example.php for all configurable settings. Change Log 0.5.0 add optional scale argument to variant-method: $asset->img('foo.jpg')->variant('header', 50) 0.4.0 add possibility to get/set and render attributes (see section Attributes example) 0.3.0 add $asset->variant('name') alias for $asset->getVariation('name') 0.2.0 fixes and internal refactorings 0.1.0 initial version
    13 points
  2. one of the first pieces of wisdom i got from Soma was about making a template from where you can execute api stuff; this is needed on every site, because you'll always need some "api playground" where you can test things and run things to change, add, fix content.. my setup is to have a template called tools, and then a subfolder in the templates where i store all of the scripts that i will run on that tools template, then i just include the one i'm currently using into it and go from there. Sometimes when developing a new part of a live site, i can just clone a template, run it under tools and set the virtual page to anything in the site (example, $page = $pages->get(1012); ) but mostly i'm doing complex imports from other systems like joomla, or from spreadsheets with a lot of custom stuff and dependant/related pages, formatting etc..
    6 points
  3. Sorting by subfields is now supported (see https://github.com/ryancramerdesign/ProcessWire/pull/862). It was my first contribution, so I'm pretty proud
    6 points
  4. When you're in charge of a large production setup, updates require planning, work, and may cause both issues and downtime, which is a huge no-no for the people (and/or services) relying on you. Everyone expects to get the latest and greatest stuff, but they're rarely willing to accept that it comes at a price. The whole point of LTS versions is that you don't have to be constantly updating your machines. It gives you peace of mind and your services much-needed stability.. and, of course, it helps keep your costs at a sustainable level You could always skip the version of any application provided by the OS package manager, but not everyone likes to do that, as it a) makes things more complicated, b) requires extra work and c) means you'll be ditching the benefits of your LTS OS for that particular application. I for one don't particularly enjoy working with old versions, but in some cases I've learned to acknowledge that it's a necessity.
    4 points
  5. I did another quick module, which plugs something I always kinda missed in ProcessWire. It's just a raw overview over the access management from the templates. Nothing fancy, but useful. The visible roles and the visibility of system templats can be setup in the modules settings. I'll add this to the modules directory later. But for now it would be great to hear if anyone is missing anything or if someone does have a better idea how to name the module. 12.03.15: Renamed the module and fixed wrong information for superusers and inherited rights from the required guest role. GitHub: https://github.com/LostKobrakai/ProcessAccessOverview
    3 points
  6. Interesting discussion! For me (as a developer), the advantages of ProcessWire's hooking system overweight the issues you mentioned. I agree, it's sometimes harder to find the relevant piece of code, because it gets added dynamically with hooks. Debugging is definitely harder too. But in my opinion you can never achieve the same flexibility with events. For example, how would you handle the beforeHooks, which are executed before the "real" method is invoked? How would you modify the passed arguments? Also the afterHook can modify the return value. It's so powerful and easy to do - just add those three little underscores and you can use this power in your own Wire-derived classes. I like
    3 points
  7. As I understand it, you have different items the same editorial (as a text field), and you want to get an array with unique editorils, right? If so, here is the code to get this: $editorials = array(); $lastEditorial = null; foreach ($pages->find("template=item, editorial!='', sort=editorial") as $item) { if ($item->editorial !== $lastEditorial) $editorials[] = $item->editorial; $lastEditorial = $item->editorial; } $a->unique() filters elements using the PHP array_unique function, which compares elements according to their string representation. Since the result of $pages->find() returns a PageArray containing Page objects, and since the string representation of a Page object is its id, $pages->find(...)->unique will return unique pages, without considering the editorial field (two different pages with the same editorial will be different, since they will have different ids). That's why $a->unique isn't usable in your case.
    3 points
  8. @Teppo, about ApiGen... I for one totally love it....but not the ProcessWire hosted one. I have rolled out my own locally hosted one, which I update regularly with the latest PW dev. It has been especially invaluable to me as a module developer and also as someone curious to find out how PW works and also just for learning to use PEAR . I can quickly and systematically go through PW cross-referenced code, reading Ryan's comments, finding out what extends what, inherited properties/methods, class properties and methods, etc. I have set it up to document not only .php files but also .module ones - so, the whole package. GitHub doesn't even come close IMHO - unless am missing something?. As stated previously elsewhere, my greatest influence as a developer has been through studying existing modules, notably by Ryan, Soma, Wanze, Pete, Antti and yours, and more recently others including Adrian. ApiGen has helped me 'decipher' Ryan's and Soma's advanced use of PW methods - quickly and efficiently (but I also use Grep a lot if I am looking for something specific). But I agree - this is something I would only recommend for advanced developers and has to be used with the full knowledge about 'Public vs Private API' as stated in the link to Ryan's comments... This has been written in a hurry so there could be mistakes
    3 points
  9. If this were a files/images field and you had 2m pages of data, you might have to take extra measures. But from what I can see, you are just removing an integer field. ProcessWire should be able to do this quickly, so long as you are running an up-to-date version. What version are you running? Have a look in /wire/core/Fields.php at the method ___deleteFieldDataByTemplate(). That's what is executing this. It has two options, the slow one, and the fast one. The slow one cycles through each page with the field and asks the Fieldtype module to handle the deletion. This is required for fields that have other assets that might need to be deleted too, like files or images. But for a field like an integer, it can use the fast method, which goes directly into the database and deletes it all with one query (which should just take a second). My guess is that the version of PW you are running is old enough to only have the slow method, or that for some reason or another the slow method is getting used when it shouldn't. The slow method would almost certainly fail in trying to deal with 2m pages, so I'm pretty sure that's what we're seeing here. So check your PW version. You may be able to resolve this simply by upgrading. If that's not it, let me know and we can look at other options.
    3 points
  10. Maybe I don't know the right words to describe it, but PHP 5.3 is somehow bundled with Ubuntu LongTermSystems support for example and it gets hardened and patched via their update channels, so it still is secure. Those Ubuntu systems are very common on hosting machines and it is a good idea for PW to orientate on their lifecycle.
    3 points
  11. Hello fellow wirecionados, looking at the roadmap to 3.0 always gives me the good feeling of being on the sunny, futureproof side when using Processwire. Everything we already have together with the features planned is all I ever needed from a CMS. At least this is what I thought until that next idea came to my mind. Actually, there's one thing about Processwire that I have been struggling with from the start and until now: the hook system. I know, the hook system is deeply enrooted in Processwire (as can be seen from the fact that it's implemented in the Wire class that virtually every other class extends) and therefore is one of the key aspects for its success in the first place. It allows for the relatively loose coupling of components, with a lot of the core functionalities being implemented as modules. This makes for the great extensibility that Processwire is famous for. There aren't many parts of Processwire that can't be tweaked and modified to anyones needs by developing a module that alters some core functionality. I'm really glad about that and probably wouldn't have started to use Processwire in the first place, if the hook system didn't exist. That all being said, I also see some more or less problematic downside to the current implementation. It's sometimes pretty hard to find out, where functions are actually implemented. How long did it take you to find out that the actual logic for Page::render() is implenented in PageRender::renderPage()? While this kind of flexibility is just great, the implementation is not very verbose. This makes things harder than they have to be – especially for beginners. Just to make the argument above worse, there are several different ways to define a hook: addHook(), addHookAfter() & addHookBefore(), and all can be called either with a Class name or on an object. This makes it even harder to find modules, that hook into a particular function. I know, we have Soma's fabulous Captain Hook, and I use it all the time, but it still feels more like a workaround than like a solution. Speaking of Captain Hook, there's still a „Crocodile Hooked“ missing, a central repository that keeps track of all methods that are hooked to another method, including (private) site modules – and if possibly available at run time. Hooks for classes are saved in a static variable of Wire, but local hooks can only be fetched from that particular instance of an object. The convention with the three underscores in front of a function name makes functions harder to find. It is a nightmare to debug. If you ever tried to follow a request through the system with Xdebug, maybe involving some hooks placed by modules, you probably know what I mean. You have to go through Wire::runHooks() and __call() again and again to get where you want. It also prevents some of the convenient features of your favourite IDE™ (e.g. code completion, parameter hints when using a function, go to the definition of a function by clicking on it, etc.) from working in some cases. While one could definitely argue, that this is the fault of the IDE used, it's still slowing down work and not likely to be changed anywhere soon. Please excuse me if this was too ranty and even more correct me if anything I mentioned is an error on my side. I have to emphasize that none of these downsides prevents anyone from getting something done, it just makes it a tad less obvious and comfortable. Thinking through the things that I don't like about the current implementation of the hooks system, it came to my mind, that what we currently have here is kind of a non-verbose version of a „traditional“ event system and maybe what's missing could be more verbosity, so maybe such an event system (that implements the mediator pattern) could be of help? What I have in my mind here is the Symfony EventDispatcher or the Drupal8 EventDispatcher component, which is similiar to the CakePHP Events System and the handling of Events in Laravel. Let's look at how a solution to the problems mentioned might work by way of example using the Symfony EventDispatcher. In this particular example, the difference would be that Page::render() would actually be implemented in the Page class but just dispatches an Event.render event. Functionally no real difference, but much more verbose and therefore it is much more clear, that you will find the implementation anywhere a listener for Event.render is added. A new implementation could use just one method to add listeners (e.g. EventDispatcher::addListener()). Execution before or after an event will have to be handled by multiple events where this makes sense. Because the event dispatcher is the application-wide sole instance where event listeners are stored, it's easy to get all registered event listeners at runtime (using EventDispatcher::getListeners()). No more underscore's, just calls to dispatch events and calls to listeners. Debugging get's as smooth as it get's with loose component coupling. No more running through __call() all the time, always be able to check the defined event listeners. Having separated before and after events prevents you from some running through runHooks(). The added verbosity also helps to grasp the existing architecture quickly. Most of the IDE comfort functionality that doesn't work now should work with the Event System in place, because functions are always really where they are expected and with the exact same name. As an added benefit this system is very well known to a lot of developers already. But there are also some downsides for sure: Added verbosity is, well, verbose. More keys will have to be hit to get something done. Method and property would have to be implemented differently. This can be done by sending events on missing methods/properties, but it certainly looses some of the appeal it has now. The key argument would for sure be, that it breaks compatibility with a lot of core and user contributed modules. This is definitely true, so there shouldn't be an easy decision on this one. I thought I'll use the time before 3.0 to bring this one up, because as I understand it, the next major will break some stuff either way. Also I think that it should be possible to do the switch by in the beginning only deprecating the old hook system while keeping it functional, by translating calls to the old functions to adding and calling event listeners. This will have a performance impact, but would make a gentle transition possible. Now I'm really interested on your opinions on this topic. Do you already like the current implementation and wouldn't want to loose it? Do you have other problems with hooks? What kind of solution could you imagine? Is such a deep change realistic (and worth it) at all? Thanks a lot for your feedback Marc
    2 points
  12. @owzim Here you go: https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/core/HookEvent.php#L144 PHP's reflection is the secret, like you already guessed
    2 points
  13. Updated the module to show the right access rights for superusers (can access everything no matter what) and access rights inherited to every user by the guest role. Also changed the modules name and will post it now to the modules directory.
    2 points
  14. ESRCH, you're such a great teacher. I've understood it perfectly. Very smart solution. I love this forum, I always learn something new. Now I have to up my PHP game to be able to return the favor and contribute more.
    2 points
  15. Yes I Use Git a Lot. Try to follow a flow for keeping things in order. Here´s a good read https://about.gitlab.com/2014/09/29/gitlab-flow/
    2 points
  16. Been planning to automate the process....either within or outside PW...e.g: https://github.com/apigen/apigen https://github.com/ApiGen/ApiGen/wiki/Generate-API-to-Github-pages-via-Travis
    2 points
  17. This bug was recently introduced, and just fixed today in the dev version - please check it out and report here: https://github.com/ryancramerdesign/ProcessWire/issues/1014 if you still have any trouble. PS Welcome to PW!
    2 points
  18. I know, but i was just meaning to say that there is movement.
    2 points
  19. Probably not a PHP 5.4 requirement till PW 3.0. Still too many PHP 5.3 systems out there. Though great to see lots of momentum with PHP versions (5.6 is great).
    2 points
  20. Call me old fashioned, but for minor sacrifices as writing a few more Characters I would not give up compatibility.
    2 points
  21. I recently finished my first bigger from start to finish ProcessWire website. It's a website for a local architecture agency which does lot's of the more prestige projects in my area. The image of the partners is still missing, but the rest is ready. I did the design together with a ex-fellow student of mine, the programming is completely done by me. The self-imposed challange was building something that does really cater to current standarts. Therefore templating is done with mustache. On the initial page request the page is rendered serverside, everything else works via ajax, as long as the history api is available. If not it's not even loading the whole templating stuff and pages are served traditionally. http://wunderle-partner.de/ Modules uses: ListerPro AdminCustomFiles Jumplinks MarkupSEO HannaCode ProFields: Multiplier
    2 points
  22. I exported a CSV file from freebase.com that contained all the skyscraper fields I wanted to use. Then I created a simple shell script to import it from the CSV. Note that I only used a shell script for convenience, you could just as easily do this from a ProcessWire template file if you preferred it or needed to do this from Windows, etc. Below is a simplified example of how to do this. The example is fictional and doesn't line up with the actual structure of the skyscrapers site, nor does it attempt to create page relations or import images. If you are interested in how to do that, let me know and I'll keep expanding on the example in this thread. But I wanted to keep it fairly simple to start. First, here is the contents of a CSV file with each line having a skyscraper building name, city, and height. /skyscrapers/skyscrapers.csv (Building, City, Height): Sears Tower, Chicago, 1400 John Hancock Tower, Chicago, 1210 Empire State Building, New York City, 1100 IBM Building, Atlanta, 860 Westin Peachtree, Atlanta, 790 Next, create a new template in ProcessWire and call it "skyscraper". Create a text field for "city", and an integer field for "height" and add them to the skyscraper template. Create a page called "/skyscrapers/" in ProcessWire, that will serve as the parent page for the skyscrapers we'll be adding. Here is the command-line script to load the ProcessWire API, read the CSV data, and create the pages. As I mentioned above, this could just as easily be done from a template, where the only difference would be that you wouldn't need the shebang (#!/usr/local/bin/php -q) at the beginning, nor would you need to include ProcessWire's index.php file. /skyscrapers/import_skyscrapers.sh: #!/usr/local/bin/php -q <?php // include ProcessWire's index file for API access // (this isn't necessary if you are doing this from a template file) include("./index.php"); $fp = fopen("./skyscrapers.csv", "r"); $template = wire('templates')->get("skyscraper"); $parent = wire('pages')->get("/skyscrapers/"); while(($data = fgetcsv($fp)) !== FALSE) { // create the page and set template and parent $skyscraper = new Page(); $skyscraper->template = $template; $skyscraper->parent = $parent; // set the skyscraper fields from the CSV list($building, $city, $height) = $data; $skyscraper->title = $building; $skyscraper->city = $city; $skyscraper->height = $height; // set the URL name, i.e. Sears Tower becomes "sears-tower" automatically $skyscraper->name = $building; // save the skyscraper $skyscraper->save(); echo "Created skyscraper: {$skyscraper->url}\n"; } To do the import, make the script executable and then run it from the command line: chmod +x .import_skyscrapers.sh ./import_skyscrapers.sh OR, if you are doing this from a template file, then load the page (that is using this template) in your web browser, and that will execute it. The output should be: Created skyscraper: /skyscrapers/sears-tower/ Created skyscraper: /skyscrapers/john-hancock-tower/ Created skyscraper: /skyscrapers/empire-state-building/ Created skyscraper: /skyscrapers/ibm-building/ Created skyscraper: /skyscrapers/westin-peachtree/ If you go into the ProcessWire admin, you should see your skyscrapers. I used an example of CSV file for simplicity, but the same method applies regardless of where you are pulling the data from (web service feeds, etc). For the actual skyscrapers demo site, I used Freebase's web services feed to pull the data and images, etc.
    1 point
  23. Until last week Hooks were mostly a mystery to me too...until I did this and this and got this
    1 point
  24. You can add anything you want in /site/templates/admin.php, which is the file that is first called when accessing the admin. Incidentally, this is actually also a place where you can add hooks that pertain only to the admin (though it is generally cleaner to do so in a module).
    1 point
  25. it's all working well here, got it on 3 live sites, and have tried various different configurations and it looks great
    1 point
  26. Hi quickjeff, You can create a permission "role-admin" and give your admin-role this permission. Any admin should then be able to manage roles. Note that I've not tested it myself, but found this comment: https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/modules/Process/ProcessRole/ProcessRole.module#L26
    1 point
  27. I don't think I've ever seen or (knowingly) used that tag. Good to know that it exists, would definitely make things easier
    1 point
  28. @teppo, providing version information (matched against PW versions) is one of my goals, among others. This is both for Cheatsheet and Docs. Ideally we'd have @since tags in phpdocs for classes/methods, but right now they ain't there.. so it might require some work to figure this part out. For those who aren't familiar with them, here's an example : /** * Example of @since usage. * * @since 2.5.20 Added the $b argument. * @since 2.5.8 Added the $a argument. * @since 2.4.0 */ I personally don't aim at using ApiGen, it's there but it's a totally different tool, though of course that's up for discussion as it's still useful but for different use cases. I wish we find a way to work on docs together, and decide how to organize things as a team. That said, I think right now our goal should be to arrange the docs in a more readable fashion, update the parts that haven't been updated and setup a good structure for docs content (this includes having info on which version the docs are for). My main inspiration is Symfony's documentation, it's just stellar; very clear with lots of code examples for common things. What Ryan has achieved so far is a very good starting point, let's improve on this. Definitely not!
    1 point
  29. A new version will be released when ProcessWire 2.6 stable hits the store and will be tagged accordingly by release (both stable and dev). Many translations in the latest dev are done, but we still have a some on our list. Thanks Jasper for your work until now and thanks for transferring the repo.
    1 point
  30. You could build this by yourself with language alternate fields and fieldset tab: http://processwire.com/api/multi-language-support/multi-language-fields/#language-alternate-field-values. It's a lot more clicking to get this does, but it does work. I think a automatic way wouldn't work as easily, as you can't depend on users having only a single tab with fields and duplicating 3 or 4 (or even more) tabs for each language will quite fastly lead to something, that's less clear than the current way. Maybe a "global" switch to change all fieldtabs of a page to the selected language would have the same effect, while keeping the current structure.
    1 point
  31. But 10.04 server is still live (till april 2015) and 12.04 till 2017!
    1 point
  32. While you can put hooks anywhere, the cleanest approach is usually to create a separate module for it. In this case, the code I gave you is the code for a complete module, so here's what you should do: Create a CustomLogout.module file in the /site/modules directory (you can rename the file if you want, but it must have the same name as the class that it defines). Paste the code from my previous post. Go to Modules > Refresh. The new module should appear, and you can click on install, to install it just like any Core module. After doing this, the redirection should work. To hide the login page, I would suggest using CSS to hide a specific item in the PageList. You will notice when examining the HTML generated by the PageList that each page is actually a div with (among other things) a class of "PageListID1234", where 1234 is the id of the page. So if your login page's id is 1025 for example, you could create a custom.css file (in /site/templates/styles) with the rule .PageListID1025 { display: none !important; } You now have to include this CSS file when showing the PageList page, but only when the user is not the superuser (or any another rule based on permissions or roles). You can try using the Admin Custom Files module and checking the different options there. I must admit that I have never used it, so I can't help you much. Otherwise, you can make a module (or include it in the above module in the init function) with this hook: $this->addHookBefore("ProcessPageList::execute", function($event) { if (!$this->user->isSuperUser()) // You can change the rule here $this->config-styles->add($this->config->urls->templates . "styles/custom.css"); // Adapt the link to the file }) As a final note, you can indeed add hooks (the $this->addHookAfter(...) call) more or less anywhere, but the syntax will change: Within a class (such as a module as defined in the code in my previous post), the hook is called on $this, e.g., $this->addHookBefore("Page::render") Outside of a class (such as in a template, or the admin.php file), the hook is called on wire(), so wire()->addHookBefore("Page::render"). You can also call addHook on an instance of a class, like $page->addHookBefore("render"). What you tried to do by including my entire module code in the default.php file wouldn't have worked, because the autoload looks for classes in files that have the same name as the class (so the CustomLogout class must be in the CustomLogout.module or CustomLogout.php file). Learning about hooks requires a little effort, but once you get it, it is very logical and it really opens the door to doing whatever you want with Processwire. The hook system is really well built, and most aspects of the application can be hooked into. I would suggest the following to learn about hooks (that's what I did): Read https://processwire.com/api/hooks/ Read http://wiki.processwire.com/index.php/Module_Creation Read through the HelloWorld.module file in your /site/modules directory. It's a sample module that shows very simply how to create a basic module.
    1 point
  33. LostKobrakai, I know but as long as PW supports 5.3.8 I'd like my modules to be compatible. If 5.4 is an official requirement, I can feel save making this a minimum for my modules as well.
    1 point
  34. You can always use this " 'requires' => 'PHP>=5.4.0' " (I don't know if it works without the minor version number) in the module's info. This way the core can still cater to a broader audience.
    1 point
  35. Stupid me. I turned on debug mode in wire instead of site folder. For my use it is enough because I need only a unique ID for further processing. But in general you are completely right. There should be an api variable to get both value and label from external tables. I will try to solve this. Access tables depending to PW is easy. For example if you choose 'templates' as source table the API is like: $templates->get($page->selectextoption)->name Returns a string if InputfieldSelect in use and array for InputfieldAsmSelect and InputfieldSelectMultiple.
    1 point
  36. I know this is a very late followup, but I recently posted an alternative over here: https://processwire.com/talk/topic/1716-integrating-a-member-visitor-login-form/?p=89599 and https://processwire.com/talk/topic/1716-integrating-a-member-visitor-login-form/?p=89616 Pros and cons to each version, so you should read all the posts in between as well. Personally I would go with the second one, just be aware that if you want it to work on the front-end you need to consider the name of your form fields.
    1 point
  37. Regarding this, Ryan did originally raise some points about the (intended) public API vs. features intended for internal use only, which this tool doesn't always distinguish properly. I'm going to agree with those points, and perhaps even take them one step further, by stating that in my opinion this is not useful at all, and should be deleted to avoid unnecessary confusion. Considering that it was last generated for ProcessWire 2.2, it would seem that I'm not entirely alone with that opinion either If you need to know how specific object behaves or what methods it has, this provides very little (or none at all) benefit over looking it up from GitHub where you'll also see the code, changelog, etc.
    1 point
  38. Google doesn't care about the CMS you use. If the content and url structure of the public site stay the same the pagerank should stay the same. Pagespeed and maybe https could have a little influence. If you do adjust the url's, be sure to setup the correct 301 redirects from old to new url's, and you should be fine.
    1 point
  39. You're right you can't sort them by simple drag and drop of programs. If you need this you'd do it the other way around, prepare programs independent of users and assing them to the user template via a page field.
    1 point
  40. I don't know where your memory error comes from, but your application is already stuffing >256MB inside your RAM. You should definitely start searching for the actual cause of that problem, or your application might run into this on other occasions. Are you fetching all your 2m+ records at some place? Install Xdebug and use the profiler to analyze what happens before you run into serious problems. That said, your problem of getting rid of a field can easily be solved by directly working with SQL commands on the database. You'll need shell access to the mysql command line tool or a tool like phpMyAdmin or Adminer to execute arbitrary SQL in your database. You probably already know a way to get this done so I'll leave it to you, how you do it. Before you do anything: do yourself a favor and dump the contents of your database and save that backup somewhere safe for later. Never work on a live instance of your application, especially not without a backup and a tested way to reset your database from it. Ok, now let's get to the work. Field data is, as you already mentioned, only saved in three places of the database: the actual content data of the pages for field is saved in it's own table (called field_fieldname), the field settings are saved in the fields table, and the association between a field and a fieldgroup and therefore a template is saved in the fieldgroups_fields table. So, let's get rid of it all. Let's assume that your field is called your_field. To keep things simple (without any joins) we'll get the id of your field in the first step and use it later as your_field_id. # Get the ID of your field. Note it down somewhere, you'll need it later SELECT `id` FROM `fields` WHERE `name` = 'your_field'; # Delete field data from fields table by it's ID DELETE FROM `fields` WHERE `id` = 'your_field_id'; # Delete field associations from fieldgroups_fields table by the field's ID DELETE FROM `fieldgroups_fields` WHERE `fields_id` = `your_fields_id`; # Now drop the table created for your field called field_your_field DROP TABLE `field_your_field`; That should have been all that was needed. With kind regards Marc
    1 point
  41. Let's say you have two users with the following sets of programs (for different weeks for example): - User 1: Program 1 (Week 1), Program 3 (Week 2), Program 7 (Week 3) - User 2: Program 3(Week 1), Program 4 (Week 2), Program 1 (Week 3) Now on the front-end, you need to show him his program by week: "Dear User 1, Here is your weekly program for the next month: Week #1: Program 1 Week #2: Program 3 Week #3: Program 7" Well, by defining the link to the user on the program template, you can't define in which order the programs can be sorted for a specific user, since the only selector you could use would be: $programs = $pages->find("template=program, user=$user, sort=??"); But we might be going a little off-topic here and mixing up Christophe
    1 point
  42. Why shouldn't it be sortable by user? But regarding "finding the right program" you can set up the program template to not only show it's title in the page tree, but also the users name.
    1 point
  43. If there is a clash between the modules, you might also want to try prioritizing the hooks, to see whether it makes any difference.
    1 point
  44. I like this kind of discussion Separating the fitness programs is a good idea, especially if they are reusable (can be attributed to different users). However, the problem with including a "user" field in the fitness-program template rather than a fitness_program field in the user template, is that it isn't possible to control the order in which the programs appear. The only available sorting mechanism would be from oldest to newest (or the opposite) by sorting by id. If the coach decides at some point that he wants to reuse the fitness programs, he can't set the order of the programs per user. So I would argue in favor of having a fitness_program field in the "user" template
    1 point
  45. It's like this: - Videos - Video - Video - Video - Fitness Programs - Program - Program - Program - Users - User - User You upload all the videos, than you create users and then you can create the program for each user. This program has some pagefield where the trainer can chose the videos for the program (e.g. videos) and a pagefield to select which user this program relates to (e.g. user). Then you can use selectors like this: $pages->get("template=program, user=$user")->videos; // Get all videos from this users program $pages->find("template=program, videos=$someVideoID"); // Get all programs featuring this one video
    1 point
  46. Hi pwired, It's related to a question I posted a couple of months ago (https://processwire.com/talk/topic/8604-how-to-build-pw-site-on-top-of-existing-database-that-keeps-changing/)... it's a feed-reading application with social features and whatnots. The people I am working with (and me most of all) might out of our league here, but I kept pushing them to pick Processwire and boy am I glad I did! One of the important features is searching through the 2+million pages by title etc, and it is really fast. There are of course other options (Solr/ Redis/ ?) if it slows down in another few million pages, but right now it is performing admirably. As for this empty field - after posting the above question I did poke around in the database, and saw that actually the extra field is taking up only 2-3 rows (in different tables) and an empty table for itself, even though theoretically it is 'added' to millions of pages. Very cool, and I figured I won't sweat it at the moment. (I want to create and delete a test field from a different template first, just to make sure... but it's not going to affect performance until I do, which is a relief.) I guess the above question has more relevance as a memory leak question maybe. I don't really know how to figure those out, so I'll leave this here in hopes someone who knows how to read the trace can help me understand what exactly happened.
    1 point
  47. I had a realization recently about the PW data-model that I would like to share. The PW model is actually very akin to that of the Entity/Component model. Pages can be seen as Entities, and Fields can be seen as Components of those Entities. Templates can be seen as Component Types. One important difference from the E/C model, in terms of implementation, is the shape of the resulting entities - Fields are mapped directly onto Pages; or in other words, Component properties are mapped directly onto Entities, which means the Components themselves are limited to a single property at the model-level, even though some Field types are backed by tables with more than one property value, e.g. more than one data-column. I can't help thinking the resulting model is really close, but one step removed from being anywhere near as powerful as a real E/C data-model. To explain why I feel this way, here's a very simple case example. Let's say you have a shop with different types of products, many of which have at least one thing in common - they have a unit price, product title, and manufacturer name. With the PW model, I will add these three fields individually to every Template that describes a product type, and I will address these as, say, $page->price, $page->title and $page->manufacturer. Fetching these three fields requires three joins to separate tables. Conceptually, these three fields are meaningless on their own - they belong to a single component that defines the necessary properties required for something to "be" a product in our system. The, let's say, shopping cart service, for example, is dependent on the presence of all of these three fields and doesn't function if one of them is missing. With the Entity/Component model, instead of adding three individual fields, you define a component type that defines these three fields. You would address them as, say, $page->product->price, $page->product->title and $page->product->manufacturer. In other words, these three fields, which belong together, are exposed in the model as a single, integral component. Fetching these three fields requires a single join to one table. The ability to group together related properties in components has performance advantages (fewer joins/queries) but more importantly, it has practical advantages in terms of programming. You wouldn't need to group together related fields by prefixing them with "name_" anymore, which seems like an unnatural way to create a kind of "pseudo component". Adding/removing properties to a component property would naturally propagate that change to every entity that uses that component, rather than having to run around and update them manually. Controllers/services could define their requirements in terms of component shape. I realize it's a pretty substantial departure from the PW data-model, but perhaps something to think about for an eventual future 3.0. Or perhaps just an idea to consider for a different CMF in the future. Or perhaps just something interesting to think about and do nothing
    1 point
  48. Hi Juergen, I'm the maintainer of the module. You could solve it by hooking after this method and return a PDF filename that depends on the language of the current user: https://github.com/wanze/Pages2Pdf/blob/master/Pages2Pdf.module#L454 I could make this method hookable for you. But I think supporting multilang would be a great feature for the module, I might need this myself soon. I'll find some time this weekend to implement it. My approach would be to create a new configuration option where you can select for what languages the PDFs are generated. When requesting a download, the PDF in the language of the current user is served. Cheers Edit: Could you post module related questions in the official module thread? Thanks. I'll try to merge them
    1 point
  49. Antti, yes, it's certainly possible. You can hook whenever and wherever you like. It's just a matter of being certain your hook has been registered before the event you're aiming for takes place. So you only need an autoload module to hook something you don't have full control yourself, and don't want to or are not able to require some initialization being called before using the hook. Here goes. And this one I tested a little so I know it works, for me at least . function myCustomAuthentication($event) { $user = $event->arguments[0]; $pass = $event->arguments[1]; // TODO: do whatever check is needed to authenticate $user // $pass has whatever you like, a token of some kind probably // must set replace-flag to prevent the original Session::authenticate() being called $event->replace = true; // return value is boolean // true: successfully authenticated // false: authentication failed $event->return = true; } // ...aquire a user name, somewhere, somehow... // hook *before* Session::authenticate() to override it // second argument is null because we're using a plain function and not a method inside an object $session->addHookBefore('authenticate', null, 'myCustomAuthentication'); // log in the user, passing whatever needed by myCustomAuthentication() as a password - if anything $user = $session->login("some-username", "some-token-with-a-meaning-in-this-very-context"); I'll actually be using this piece of code myself as well, this week I hope.
    1 point
  50. This looks very interesting--thanks for setting this up charliez! I do worry a little about documentation like this, because it steps outside of our public API. I don't consider every function in PW to be part of the public API that folks should use (the majority of it is not). ProcessWire isn't meant to be that low-level of a framework. There are so many components that are really just there to support the public API, rather than be part of it. Looking at the docs in this manner loses all context of that. The cheatsheet really has the right context. I would just suggest that people use our regular API docs (especially the cheatsheet) and then use something like this apigen documentation for zeroing in on more detail about a specific function or class, if they need it. I think the apigen documentation could really be useful for contextual links to more detailed function reference, where applicable (like in links from the cheatsheet). But I don't suggest integrating functions that aren't in the cheatsheet into your projects. The public API in ProcessWire is a constant, but everything else can change with versions. Also, I'm not so sure what I think about this service hosting our documentation (I don't know them well enough yet), so I went ahead and copied it to here: http://processwire.com/apigen/
    1 point
×
×
  • Create New...