Leaderboard
Popular Content
Showing content with the highest reputation on 03/12/2015 in all areas
-
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 version13 points
-
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
-
Sorting by subfields is now supported (see https://github.com/ryancramerdesign/ProcessWire/pull/862). It was my first contribution, so I'm pretty proud6 points
-
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
-
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/ProcessAccessOverview3 points
-
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 like3 points
-
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
-
@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 mistakes3 points
-
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
-
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
-
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 Marc2 points
-
@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 guessed2 points
-
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
-
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
-
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
-
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-Travis2 points
-
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
-
2 points
-
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
-
Call me old fashioned, but for minor sacrifices as writing a few more Characters I would not give up compatibility.2 points
-
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: Multiplier2 points
-
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
-
1 point
-
Until last week Hooks were mostly a mystery to me too...until I did this and this and got this1 point
-
The argument names are the method arguments/parameters ..So, in your method foo(), there are two arguments...bar and baz....with 0 and 1 indices respectively..So, you can get with either e.g. event->arguments(0) is the same as event->arguments('bar') but the numeric index is faster (although less readable)... http://processwire.com/api/hooks/#read_arguments1 point
-
I usually executed such scripts over the command line (bootstraping ProcessWire). Maybe you could use Hanna Code if you'd like to write the code in the admin?1 point
-
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
-
it's all working well here, got it on 3 live sites, and have tried various different configurations and it looks great1 point
-
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#L261 point
-
The code from my previous post generates a simple array of editor names, over which you can loop, for example like this: // $editorials is the array from the code above foreach ($editorials as $editorial) { echo $editorial; } The $lastEditorial variable is simply used to avoid duplicates: In the code of the previous post, each iteration first compares the editor ($item->editorial) with the value it had for the previous iteration ($lastEditorial), and if it is not the same, it include it in the new array ($editorials[] = $item->editorial). Then at the end of the loop, the $lastEditorial is updated to have the new value. So using the data that you gave, the iterations go like this: "Abada" !== "", so add "Abada" to the $editorials array. Set $lastEditorial to "Abada" "Abada" === "Abada", so don't add the new "Abada" to the loop. Set $lastEditorial to "Abada" "Abada" === "Abada", so don't add the new "Abada" to the loop. Set $lastEditorial to "Abada" "Academy of Mathematics" !== "Abada", so add "Academy of Mathematics to the $editorials array. Set $lastEditorial to "Academy of Mathematics" "Chinese Academy" !== "Academy of Mathematics", so add "Chinese Academy..." to the $editorials array. Set $lastEditorial to "Chinese Academy..." etc. The result of this all is to have an array of unique editors. But if you really just want to echo the editors in place, you can shorten your code to one loop (I also reduced the number of variable assignments): $editorials = array(); $lastEditorial = ""; foreach($pages->find("template=item, editorial!='', sort=editorial" as $item) { if ($item->editorial !== $lastEditorial) { echo $item->editorial; $lastEditorial = $item->editorial; } }1 point
-
I didn't know about them either until a few weeks ago, I discovered that while thinking about documenting PW. I've also asked Ryan on GitHub https://github.com/ryancramerdesign/ProcessWire/issues/1015 to start using them. That would really help us out.1 point
-
I don't think I've ever seen or (knowingly) used that tag. Good to know that it exists, would definitely make things easier1 point
-
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
-
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
-
First of all, welcome grumpy. It looks like what has happened is that downloading MobileDetect doesn't download the library it depends on - https://github.com/serbanghita/Mobile-Detect which, if I understand correctly, should go in the /blah/blah/blah/site/modules/MobileDetect/lib/ directory. Try downloading that and unzip it to the above folder. Fingers crossed, that should make everything work. If, erm, it doesn't, come back here and we'll try something else.1 point
-
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
-
Yeah, as the site does not need super dynamic content it does load the needed json only once in 10 minutes. After that it just reuses the previous response. Until now there isn't even a server side caching.1 point
-
Great site indeed! I love the simplicity about it. Even more, I love the solid appeal/feeling I get from it. Well done @ESRCH: I don't think ProCache is being used here. Mustache, the client-side templating system, would probably be speeding things up. I think it also has it's own cache.1 point
-
Seeing that Page::set isn't hookable, my best bet would be to store the "name{$language->id}" values away in a hook after Page::loaded and compare them after Pages::save.1 point
-
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 Marc1 point
-
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 Christophe1 point
-
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
-
Hi shawnadelic and welcome! Check out soma's new module: https://processwire.com/talk/topic/9299-pollino-simple-polls-for-processwire/ There really is no fool proof way of ensuring this sort of thing. Cookies can be cleared, IP addresses are usually not static and can be spoofed anyway. The best option is to require the user have a user account and that they be logged in, but if you are not concerned with complete accuracy, you can make use of a combination of techniques, which I believe soma's module allows for. Even if the module doesn't suit your needs exactly, I bet you will find lots of useful code snippets and ideas in there. I have built a couple of polls, one that relied solely on cookies, and one that required the users to be logged in, but have never gone the IP route, because in particular it might prevent users from the same house, place of work, or even the same ISP being able to vote.1 point
-
Wow thanks for using this code and thanks for improving it. Well A technique I use for avoid using sessions is a token and send that token with every request. I previously created a "token" field inside the user page. So when the user logs in a token (normally sha1) is created and saved to his token field. So for example you made a login request and the backend responds with this data. { "status" : "ok", "data" : { "id" : 142, "username" : "clsource", "name: "Camilo", "token": "abc123" } } And then you need to update its name sending a PUT request to the corresponding endpoint PUT { name : Camilo C. } https://api.example.com/v1/users/{token}/profile/name in this case {token} would be "abc123" As the token is saved in the user page you can check if the user exists quering by the token $usr = $users->get("token=$token"); As you may notice the token gives you access for seeing and modifying the user, so its better to keep it safe. A rule of thumb is creating a token with a 1 hour valid time. And when the token is invalid you should request a new one in background. Hope it helps1 point
-
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 them1 point
-
Hello and thanks for any help you can provide! I know I'm in way over my head, but Processwire has me drunk on power. I know so little and yet so far I've been able to do pretty much everything I've wanted to, including all sorts of crazy admin features that until recently I'd thought were the sole purview of programming ninjas. Anyway on to my problem: thanks to PW I've picked up a lot of PHP, but I know next to nothing about MySQL. So you can assume utter-beginner level knowledge, but also full readiness to learn what needs to be learned to get the following done: I have an existing database that has lots of data I want to use to model PW pages on. How would I build a PW site using some of the fields/tables in this database? The PW database would need to change and re-model this data in various ways. This existing database also keeps changing. How can I stay in sync with the database, even while my database no longer looks like the existing database (cause my data changes too)? Where can I start learning about what I need to know to start doing this and slowly get on top of it? Please point me in the right direction to start investigating - modules, books, tutorials - any input is welcome and deeply appreciated. Thanks again.1 point
-
Thanks man. By the way, I forked PW and added RESTful capabilities to the Core https://github.com/clsource/ProcessWire/tree/dev/wire1 point
-
1 point
-
@Marty: I do this by using a module. It's a copy from somas PageListShowPageId. the PHP code is public function init() { $this->addHookAfter('ProcessPageListRender::getPageLabel', $this, 'addPageArchivnummerLabel'); } public function addPageArchivnummerLabel($event) { $page = $event->arguments('page'); if('album'==$page->template) { //$event->return = str_pad(count($page->images), 3, '0', STR_PAD_LEFT) . " " . $event->return; $event->return = $event->return . ' <span class="jhpPageListArchivnummer">' . $page->archivnummer . '</span> ' . ' <span class="jhpPageListImageCount">' . count($page->images) . '</span>'; } } and the CSS is .content .PageListItem > a span + span.jhpPageListImageCount:before { content: 'count: ' !important; } .content .PageListItem > a span + span.jhpPageListArchivnummer:before { content: '' !important; } .content .PageListItem > a span + span.jhpPageListImageCount, .content .PageListItem > a span + span.jhpPageListArchivnummer { font-size:0.9em; background-color:#EEE; color: #777; padding: 2px 5px 1px 5px; border: medium none !important; border-radius: 3px 3px 3px 3px; }1 point
-
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