Jump to content


Popular Content

Showing content with the highest reputation since 07/04/2020 in all areas

  1. 15 points
    This week I'm not bumping the version number just yet because I've got lots of work in progress. The biggest thing so far is something I hinted at last week. Basically, I like what the addition of the MySQL query expansion operators have brought (per posts last week and week before), but they also reveal what's lacking: something as simple as a search for "books" still can't directly match the word "book". But that's the most basic example. It's not a limitation of ProcessWire, but just the type of database indexes in general. I think it'd be amazing if ProcessWire had the ability of being really smart about this stuff, able to interpolate not just plurals vs. singulars but related words. In a perfect world, this is what query expansion would do (in addition to what it already does). But the reality is that it involves all kinds of complicated logic, rules and dictionaries; well beyond the scope of even a database. And it can be vastly different depending on the language. So this isn't something we can just add to the core and have it work. On the other hand, I figured maybe we should just put in a hookable method that just pretends the ability was there. Then people could hook it and make it respond with variations of words, according to their needs. The searches that use query expansion could then call this method and use whatever it returns... for when someday the ability is there. So I went ahead and added that hook — WireTextTools::wordAlternates(). And our database-searching class (DatabaseQuerySelectFulltext) now calls upon it, just in case an implementation is available. Well, after getting that hook added and having our class call it, naturally I wanted to test it out. So I got to work on it and came up with this module: WireWordTools. The WireWordTools module provides an API for English word inflection and lemmatisation. And it hooks that new method mentioned above, so that you can install it and immediately have it bring your searches to the next level. While it only helps for English-language searches, maybe we'll be able to add more languages to it, or maybe it'll lead to other modules that do the same thing for other languages. The expanded/alternate words are only used for searches that use the new query expansion operators, which are the ones that have a "+" in them: ~+=, ~|+=, *+=, **+=. They all can return similar results, but are weighted differently. Unlike most operators, where the logic is direct and you can expect them to always behave the same way, these query expansion operators are more subjective, and ones I think we should intend to keep tweaking and improving over time to continually improve the quality of the results they return. Basically, they are geared towards building site search engines, so I think it makes sense for us to pursue anything that makes them better at that, rather than aiming to always have them return the same thing. I am currently testing out the ~|+= operator ("contains any words expand") on our main site search engine here, along with the WireWordTools module. Finally, searching for "books" does match "book" too, and a lot more. More to be done here, but it's a good start hopefully.
  2. 11 points
    Relative to ProcessWire 3.0.161, version 3.0.162 contains 24 commits that continue upgrades/improvements to selector operators, fix various minor issues, add new API convenience methods, improve documentation, optimize and refactor various portions of code and DB queries, and much more. For full details, see the dev branch commit log as well as last week’s post. Next week I hope to finally finish up a new version of ProCache and continue with some additional core to-do items. By early August my hope is that we’ll have the next master branch version ready. Also added this week is a new dedicated documentation page on this site that covers all of ProcessWire’s selector operators, including all the newly added ones here: selector operators. Thanks for reading and have a great weekend!
  3. 8 points
    Using SSL should be quite straight forward, assuming that everything is configured correctly on the server side. The enforcing happens on the server the moment you issue an ALTER USER your-processwire-user@your-mysql-server REQUIRE SSL The moment you do that, you'll get a database error when you access your site. To enable PHP to talk over an encrypted MySQL connection, you now need to point it to the MySQL server's CA certificate. Copy that to a location where the web server can read it and add an entry in site/config.php (adapt the path to match your ca cert location): $config->dbOptions = array( \PDO::MYSQL_ATTR_SSL_CA => 'C:/temp/mysql-ca.pem' ); There may be scenarios where the name you use to access the server doesn't match the name in the certificate and you get the error "SQLSTATE[HY000] [2002]". The same error occurs when you use a self-signed certificate in the server (that's the case when you leave things to default after installing MySQL on most distributions). In that case, you need at least one of the following PHP versions: PHP 7.2, 7.3, 7.4 or 8 all versions PHP 7.1 >= 7.1.4 PHP 7.0 >= 7.0.18 The reason is that earlier versions of the MySQL PDO module didn't have the flag to disable certificate verification. You need to expand your entry in site/config.php: $config->dbOptions = array( \PDO::MYSQL_ATTR_SSL_CA => 'C:/temp/mysql-ca.pem', \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false ); Most (hopefully all) PW modules should be using the PDO interface by now, but you may stumble upon one that still makes use of the old mysqli wrapper. Those won't work with an SSL connection.
  4. 6 points
    I was going through the Hanna Code "Readme" in Bitpoet's editorial blog based off kongondo's Blog module and this particular line caught my attention: $f = $modules->InputfieldCheckboxes; $f->name = 'vegetables'; // Set name to match attribute $f->id = 'vegetables'; // Set id to match attribute $f->label = 'Vegetables'; $f->description = 'Please select some vegetables.'; $f->notes = "If you don't eat your vegetables you can't have any pudding.";//<<<<<<<<<<<< $f->addOptions(['Carrot', 'Cabbage', 'Celery'], false); $form->add($f); Very funny... 😁
  5. 5 points
    Hello @ all Today I want to share an inputfield/fieldtype to store 2 or 3 dimensions of an object. This fieldtype was inspired by the amazing fieldtype "Fieldtype Dimensions" from SOMA (https://modules.processwire.com/modules/fieldtype-dimension/). This fieldtype was introduced in 2013 - so its time for a relaunch. This new fieldtype offers more possibilities than the old one from SOMA. This inputfield/fieldtype let you enter max. 3 dimensions (width/height/depth) of an object (fe a product), but you can select if you want to display inputs for 2 or 3 dimensions. 2 dimension can be used fe for wallpapers or photos, 3 dimensions fe for furnitures or other objects. There are several configuration options for this fieldtype in the backend. set type (2 or 3 dimensional) set width attribute for the inputfield in px (default is 100px) set size unit as suffix after each inputfield (default is cm) set max number of digits that can be entered in each field (default is 10) set max number of decimals (default is 2) show/hide a hint to the user how much digits/decimals are allowed If the number of decimals or digits will be changed, the database schema for each dimension column will also change after saving the field in the backend. For example: If the schema for each dimension field in the DB is f.e. decimal(10,2) and you will set the number of digits in the configuration to 12 and the number of decimals to 1, then the schema in the DB will also change to decimal(12,1) after saving the inputfield. You can download this inputfield at https://github.com/juergenweb/FieldtypeObjectDimensions There you will find more detailed information and explanation too. If you find any bugs or you have an idea to improve it (also code improvements) please report it on Github. Have a nice day!
  6. 4 points
    If you're dealing with multiple timezones I'd strongly suggest not involving the db in it. There are two types of datetimes: absolute time (times you want to compare with each other even across timezones) and wall time (11 o'clock stays 11 o'clock for your user). Because timezone defintions can potentially change for future datetimes it's not always as easy to keep both properties as one might think. If only the first one is important to you you should keep everything in UTC. If only the last one is important you could use a datetime in the timezone of the user, but it's rarely the case you don't compare timestamps or it doesn't become a requirement (e.g. for ordering). For past datetimes it's enough to store a utc timestamp and the timezone of the user to get to both the absolute time and the wall time as timezone defintions rarely change in retrospect. For future datetimes you'd need to make sure to save enough information so you can detect changes in the timezone definition when they happen. Then you or your user can decide if absolute time or wall time was meant to be consistent.
  7. 4 points
    @bernhard I didn't come up with the dictionary words in the JSON files, they are converted from an existing one (here) and apparently the original source is wordnet.princeton.edu. So I'm not sure if those particular words are intended or mistakes. New to me, but "wa" and "wo" are actual English words. Though as far as I can tell they aren't related to "was" or "will". I can't imagine those two instances will ever be helpful for our intended use case so maybe it makes sense to remove them. My plan was to keep looking for more existing dictionaries and continue to merge them into the one in WireWordTools so that it becomes more comprehensive over time.
  8. 4 points
    Thanks for your contributions to the community 😄 Good luck in all that you do - we'll see you around pwFoo 🙂
  9. 3 points
    Good article and postgresql looks interesting with its search capabilities, thanks. Though none of these really solve what I was after here. I experimented quite as bit with stemming and different stemming libraries. Though they all did roughly the same thing. When it came to searching, stemming just wasn’t that useful. WireWordTools originally had a stemming library and methods, and the appropriate fulltext queries included the word stems with wildcards. In the end, it just wasn’t helpful most of the time. And in the few cases where it was worthwhile, it was redundant, though far less thorough, than what we already had with inflection and lemmatisation. So while stemming can have its uses, it’s not even half way there, if trying to build a smart search. Cool nevertheless that they have it built-in apparently. As far as accent support, ranking and fuzzy search, these are all things that MySQL does as well, though maybe there are differences in how they do them. For instance, MySQL supports “sounds like” and also supports pluggable parsers for fulltext searches. Fuzzy search also isn't what I'm after here, but certainly interested in exploring in the future. For me the most useful thing by far is boolean mode searches, particularly in InnoDB, which has a full-text engine modeled on Sphinx. Boolean mode searches are really very powerful, enabling you to specify what’s required, what’s excluded, matching of words or phrases, partial matching of words with wildcards, specifying noise words, isolating distance between words, adjusting ranking up or down on a per-word basis, grouped expressions and nested subexpressions. All while being incredibly fast. I’m pretty thrilled with what MySQL supports here and what it brings to ProcessWire. Postgresql looks very nice too, but for our needs, I don’t feel we are lacking anything relative to it. I think anyone that would say that as a general thing is not very familiar with what MySQL fulltext supports, or maybe is thinking of fulltext support where it was back a long time ago. For ProcessWire and the scale that most use it at, MySQL fulltext is really a sweet spot, enabling PW to deliver enormous power and capability when it comes to search features.
  10. 3 points
    This is where postgresql outshines mysql by far. It can do stemming and accent support for multiple languages out of the box, ranking, fuzzy search, … which are the things you found missing. Many people/resources seem to suggest not bothering with mysql for advanced full text search needs, but directly going to purpose built external technologies for that, while postgresql provides a stepping stone, before needing to go that route.
  11. 3 points
    Try calling wire('pages') instead of $pages.
  12. 3 points
    I'm using SessionHandlerDB: $s = $modules->get('SessionHandlerDB'); foreach($s->getSessions() as $session) { $s->destroy($session['id']); } If not using SessionHandlerDB it should be enough to clear /site/assets/sessions
  13. 3 points
    JavaScript to the rescue 😄 Have a look at this: Intl.DateTimeFormat https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat and in particular this example in SO: https://stackoverflow.com/a/34602679 const tz = Intl.DateTimeFormat().resolvedOptions().timeZone As usual, won't work in all browsers, you know, the usual suspects... 😉
  14. 3 points
    I think this is a bug in ProcessWire. When you use the ~= operator, ProcessWire translates it into MySQL’s select /*[…]*/ match(field_title.data) against('+New York' in boolean mode) as _score_field_title_data1 /*[…]*/ order by _score_field_title_data1 desc The score it uses to sort is generated by the database according to the number of times the string appears in the text (probably more complicated than that, but whatever). The problem is that several results will have the same score, so the order among them is not deterministic. The database just does whatever it wants at that point, so the order may change inbetween queries, even if the queries are the same, but especially if they differ, even just in LIMIT and OFFSET. If you add "sort" to your selector string, ProcessWire will still use the score to sort, but only after your specified fields. What ProcessWire should do is ALWAYS sort by page id at the end, to keep the order deterministic. It might also be nice to make the relevance scores accessible in selector strings, so you could do something like "title|trader~='New York', sort=-title_relevance, sort=-trader_relevance, sort=-modified, sort=-id" Now you could have results in order of textual relevance, and if the relevance is equal, get the most current ones first, or whatever you desire. Obviously the id is a pretty stupid way to sort, but since it’s strictly unique, it’s a stable way to break ties, so I’d always put it last.
  15. 3 points
    Hi, module isn't maintained or tested anymore. I love PW, but at the moment I don't need / use it. Because of missing time I stopped web development / fun tasks some time ago...
  16. 2 points
    You're right, thanks for letting me know. Links should be fixed now. I just switched to a different GitHub username and hadn't come around to updating the links yet. The docs can be found here now: daun.github.io/processwire-dashboard/
  17. 2 points
    Another approach to try: // Store page changes in custom property on $wire $wire->addHookAfter('Page(template=contact|sample)::changed', function(HookEvent $event) { $page = $event->object; $what = $event->arguments(0); $old = $event->arguments(1); $new = $event->arguments(2); $page_changes = $event->wire('page_changes') ?: []; $page_changes[$page->id][$what] = [ 'old' => $old, 'new' => $new, ]; $event->wire('page_changes', $page_changes); }); // Send email if there are page changes after the request is finished $wire->addHookAfter('ProcessWire::finished', function(HookEvent $event) { $page_changes = $event->wire('page_changes'); if($page_changes) { // Send the email using the values in $page_changes ... } });
  18. 2 points
    @LostKobrakai thanks for the explanation! very helpful. Actually I created a second datetime field now, where I store the walltime of the user when the post is created. I just use this field then to display the time on frontend, instead of created. Messing with the actual created date could really make problems - thanks for the insights - especially because you still want to keep the real "order" (dependend on UTC or a universal time) of posts, even tho individual local time is different.
  19. 2 points
    Updated to 1.0.1 (Stable), mainly reducing hook priority < 200 so it runs before ProCache.
  20. 2 points
    I could be too tired to wrap my head around this properly, but it seems to me that your module's init/ready is going to be called after the (admin) page::render method is called, which would explain why it has no effect at all. Your module is not autoloaded, so the init/ready should only trigger when this specific inputfield is being rendered, which is likely too late in the process. Might be easier to go along the lines of what Adrian suggested and a) split the hook into a separate module that extends Wire, and b) make that separate module autoload (preferably with conditional autoloading, i.e. when the template is admin or something) 🙂 (Note: making the Inputfield module itself autoloading should probably work too, but this way you'll end up loading some unnecessary baggage even when it's not necessarily needed.)
  21. 2 points
    If the hook works in ready.php but not in your module it will most likely not get called. Did you try this? public function ready() { bd('fired!'); // or die('fired'); if you are not using tracy } In your module? I guess it will NOT fire and I guess the reason is that your module does simply not get loaded in your request. An Inputfield does for example never get loaded when visiting any frontend page or for example when viewing the page tree in the backend. It does only get loaded in ProcessPageEdit! So the first step is to find out when your ready() method in your module gets fired and when not. ready.php loads on every request. Inputfields do also have the renderReady() method that get called even when the Inputfield is loaded via AJAX. Is your Inputfield collapsed somehow?
  22. 2 points
    I think it might be because you are extending InputfieldPageTable rather than Wire or WireData. Sorry, I have to run now, but maybe that will get you on the right track. Usually with a custom inputfield you just need a css file named to match the class of the module and it will be automatically loaded, eg InputfieldPageTableExtendedGrid.css
  23. 2 points
    Try this: $this->wire()->addHook('ProcessWire::ready', function($event) { $event->page->addHookAfter('render', function($event) { $value = $event->return; // Return Content $style = "<style type='text/css'>". $this->pages->get((int) wire('input')->get('id'))->style ."</style>"; // Add Style inside bottom head $event->return = str_replace("</head>", "\n\t$style</head>", $value); // Return All Changes }); }); inside your module's init() method.
  24. 2 points
    Not sure I fully understand the scenario, however, technically, there is no 'adding children to a page'. Conversely, you give a child a parent 😄. This means, there is no difference between creating a parent page and its children except for specifying the parent. // create parent $p = new Page(); $p->template = 'basic-page'; $p->parent = $pages->get(1234); $p->title = 'Parent Page'; $p->save(); // create child $c = new Page(); $c->template = 'child-template'; $c->parent = $p;// new parent above $c->title = 'Child Page'; $c->save(); It seems to me though that your question is mainly related to getting info to create the child page after some event has occurred? if that's the case, please provide more information about the form submission and handling process.
  25. 2 points
    I had the same issue. The problem was that there was a templates record in the database created with no-name. When I deleted this record (with Adminer), everything was working again.
  26. 2 points
    Step 1: Change your form element’s action attribute to <?php echo $page->url; ?> Step 2: There is no step 2. edit: sorry, I didn’t read the whole thread either. This same answer had actually been posted by Soma two posts ago.
  27. 2 points
    Sounds very interesting! Thx for the updates Ryan 🙂 I understand "we" -> "i" but don't understand the pointed 2.. are they wrong or is my english too bad? 🙂
  28. 2 points
    OK, I figured it out. I transformed the values of the inputfields inside the sleepValue function to a json array and now the values will be stored in the database. public function sleepValue(Page $page, Field $field, $value) { // throw error if value is not of the right type if (!$value instanceof OpeningHours) { throw new \Exception($this->_('Expecting an instance of OpeningHours')); } $content = json_encode($value->data['hours']); $sleepValue = ['hours' => $content]; return $sleepValue; } So the responsible lines are $content = json_encode($value->data['hours']); $sleepValue = ['hours' => $content]; The problem of the storage was that the column 'hours' was not defined in the sleepValue method. This was the important part because without it the system doesnt know where to store the value.
  29. 2 points
    Check this out: https://processwire.com/blog/posts/pw-3.0.137/#on-demand-mirroring-of-remote-web-server-files-to-your-dev-environment
  30. 2 points
    Hello @ all! I want to share a simple fieldtype and inputfield to store address data with you. I have created this inputfield for learning purposes and it has no fancy functionality. It is simply for storing address data such as street, number, postalcode and so on in one table. As an addition you can store latitude and longitude too, so you can use them in maps. Here is a screenshot of what it looks like: You can select which fields are mandatory and you can choose if the inputs for longitude and latitude should be displayed. These settings can be configured in the field configuration. If you find this inputfield useful you can download it at https://github.com/juergenweb/FieldtypeSimpleAddress There you will find a detailed explanation. If you have an idea of an usefull feature that can be added or you have detected a bug, please report it in my github account.
  31. 1 point
    The API for this is kinda ugly but it works. Where "3" is the number (in order) of the subfield / column you are looking for.
  32. 1 point
    @jploch Does this help? On the Template -> Edit -> Files tab, you could nominate an alternative template/path for your PageTable template.
  33. 1 point
    Cross referencing a similar topic with, possibly, an answer to your question @Lutz
  34. 1 point
    I can replicate here. There must be something wrong with the image, as it opens in FF and other deskop image viewers on windows. I recreated webp from your original jpg (using online jpg to webp converter) and this time Chronme shows the image. I have no idea what could be wrong, possible Chrome issue?
  35. 1 point
    This put me on the right track 🙂 I had a Pages::saved hook that set output formatting to true (when it shouldn't). Thank you!
  36. 1 point
    Yeah, you are right. Now I see what you want to do 😄 inject the CSS directly
  37. 1 point
    But this would create a <link> element right?
  38. 1 point
    In your template, you should be able to do echo $page->your_file_field->name; This is a good cheatsheet regarding the file field as well: https://cheatsheet.processwire.com/files/file-properties/ I apologize, I kinda missed that you were not going through a file field but were uploading to a directory. This might be a bit better approach for you then:
  39. 1 point
    Hi @elabx, thanks for the suggestion. I finally came up with a solution combining the hook and special names in the template flags. So with my example from above, I add a tag with the name allowedonly4-basic-b to the template that should be allowed only in the sub directory of basic-b, but not in other sub directories. $wire->addHookAfter('ProcessPageAdd::getAllowedTemplates', function($event) { $pages = wire()->pages; $parent = 'sub' == $pages->get(wire('input')->get->parent_id)->template->name ? $pages->get(wire('input')->get->parent_id)->parent() : new NullPage(); if(0 == $parent->id) return; $templates = $event->return; foreach($event->return as $template) { foreach(explode(' ', $template->tags) as $tag) { if('allowedonly4-' == substr($tag, 0, 13) && substr($tag, 13) != $parent->template->name && isset($templates[$template->id])) { unset($templates[$template->id]); } } } $event->return = $templates; }); With this combination I now can create templates allowed only for one or two defined sub directories.
  40. 1 point
    I found the culprit. It wasn't Tracy. It was another module I probably should have suspected since it has never really worked out that well for me - despite being a good idea. Thanks for your effort in trying to help me debug this - coincidences and panic are a lousy combination.
  41. 1 point
    Hi @gornycreative this could work, yes. But I guess there would be 1000 edge cases that could make that setup fragile. I've used Kickstart on almost all installations for the last couple of months years 😲 and had no problems, but for example on my new live server I get a 500 (though the installation works afterwards, but I have to clean some files manually... likely a permissions issue). What I'd really like to have is some kind of simple and solid kickstart that installs RockMigrations and can then do whatever you want. Yeah, you always get the latest versions of PW and all the modules and you stay flexible in the setup (just comment out unneeded modules).
  42. 1 point
    Hello fellow ProcessWire people! I published an article explaining how I migrated three years worth of running data from Garmin to ProcessWire: https://francescoschwarz.com/articles/running-on-my-own/ Have a great day! Cheers.
  43. 1 point
    But that's kind of the similar case as many of the Modules, with their last updates being years old. Are they no longer being updated because the developer lost interest in updating it? OR they work perfectly well still, with no need to update? lol, it could go both ways. It's a pleasant surprise when I test out an old module and it works perfectly with the later version of PW ❤️
  44. 1 point
    Thx @psy It depends on how you have used it I guess. But you can definitely run both beside each other. That's why they have separate names so that I can use them together in my projects without the need of refactoring existing code. In the long run RockFinder3 should be the only module to stay. Meaning that if you build a new project you should definitely use RF3 only and if you find anything that does not work yet with RF3 just give me a ping and I'll implement it. Most of that should be easy and maybe just copy&pasting methods from RF2 to RF3 and doing some refactoring. So I guess the answer is: It is a completely different module 🙂
  45. 1 point
    While working on the comments form of my blog, I thought to add an honeypot field in comments form to reduce spam. 🤨 Honeypot is simply an hidden field added to a form. Honeypot field can have any name and is made invisible normally with a css directive {display: none}. Web users, being unable to see the field, will not fill it, while spam bots majority will not detect its invisibility and will populate the field. Once the form is submitted with a not-empty honeypot field it is very likely we are dealing with spam. 😜 In this post you can find more details about honeypot technique. While studying FieldtypeComments module and in particular CommentForm.php, to my great surprise 👀 I realized that PW already supports honeypot for Comments Form. 🍾🍾 This feature has been introduced with PW 3.0.40. Normally this honeypot field is disabled, so it was enough to understand how to enable it! And as often is the case with PW ... it is super easy. 😎 If in your profile you are directly working with CommentArray, you will just have to enable honeypot passing it as an option to the renderForm() function of CommentArray class, example below: $comments->renderForm(['requireHoneypotField' => 'email2']); And .. we are done! 🍾🍾 If you will look at the html of your Comment Form you will see an additional line CommentFormHP, that's the hidden honeypot field. In case you are using the Uikit 3 Site/Blog Profile, the renderForm() function is called in _uikit.php, ukCommentForm() function. If you wish that honeypot field is applied to every comment form of your site, just add the requireHoneypotField option to the list at the function start: ... 'errorMessage' => __('Your comment was not saved due to one or more errors.') . ' ' . __('Please check that you have completed all fields before submitting again.'), requireHoneypotField' => 'email2', // >>>>> ADD THIS LINE ); ... Otherwise if you wish to add honeypot in comment form on selected templates only, do not modify ukCommentForm(), but pass the option requireHoneypotField when calling the function in your template: ukCommentForm($comments, ['requireHoneypotField' => 'email2']); Now that we enabled it, let test if honeypot works. 🧐 In the browser development section let's select the honeypot field and disable css {display:none} to show it. A new field will appear: If the spam bot is going to fill the field with a value and submit the form, an error is returned and comment will not be submitted 😎 That approach is great as spam comments will not be even saved inside the table field_comments. 🤪 I hope this can be of help if somebody needs to enable this PW comments feature.
  46. 1 point
    Hi @Lance O., yes. This is how I did it; I used the LoginRegister module of Ryan on a Page with a "PageUserProfile" template: // Code on the template PageUserProfile $input->get->profile = 1; $loginRegister = $modules->get('LoginRegister'); $user->of(false); echo $loginRegister->execute(); Then I use my own module and inside the init() function, I add two hooks: <?php /** * © ICF Church – <web@icf.ch> */ namespace ProcessWire; class TemplateUser extends WireData implements Module { protected $template = 'user'; public function init() { // handle profile images $this->addHookBefore('Page(template=PageUserProfile)::render', $this, 'profileImageUpload', ['priority' => 6]); $this->addHookAfter('Page(template=PageUserProfile)::render', $this, 'profileImageRemove', ['priority' => 99]); } /** * getModuleInfo is a module required by all modules to tell ProcessWire about them. * * @return array */ public static function getModuleInfo() { return [ 'title' => 'Template User Controller', 'version' => '0.0.1', 'summary' => 'Helps with profile image', 'href' => '', 'singular' => true, 'autoload' => true, 'author' => 'Noël Bossart', 'icon' => 'unlock', ]; } /** * Hock to add profile image to user object. * * @param HookEvent $event */ public function profileImageUpload(HookEvent $event) { $user = wire('user'); $input = wire('input'); if ($input->post->profile_submit) { $upload_path = $user->filesManager->getTempPath(); // name of the inputfield from the LoginRegister Module: $f = new WireUpload('profile_image'); $f->setMaxFiles(1); //$f->setMaxFileSize(1 * 1024 * 1024); $f->setOverwrite(true); $f->setOverwriteFilename('userimage'); $f->setDestinationPath($upload_path); $f->setValidExtensions(['jpg', 'jpeg', 'png', 'gif']); // remove image… if (strpos(implode(array_keys($_POST)), 'delete_profile_image_') !== false) { $user->of(false); $user->image->removeAll(); $user->save(); } $files = $f->execute(); if ($f->getErrors()) { foreach ($files as $filename) { @unlink($upload_path.$filename); } foreach ($f->getErrors() as $e) { echo $e; } } elseif (is_array($files) && count($files)) { $user->of(false); $user->image->removeAll(); // wirearray (line added by @horst: explanation is three posts beneath) foreach ($files as $file) { $user->image->add($upload_path.$file); } $user->save(); foreach ($files as $file) { @unlink($upload_path.$file); } } } } /** * Hock to remove profile image from user * * @param HookEvent $event */ public function profileImageRemove(HookEvent $event) { // remove image… if (strpos(implode(array_keys($_POST)), 'delete_profile_image_') !== false) { $this->user->of(false); $this->user->image->removeAll(); } } }
  47. 1 point
    Welcome to the forums @mjut You need to use $config->scripts->add() for JS and $config->styles->add() for CSS .These need to be added before the controller.php $config->scripts->add($config->urls->templates . "scripts/admin.js"); $config->styles->add($config->urls->templates . "styles/admin.css"); // this comes last require($config->paths->adminTemplates . 'controller.php');
  48. 1 point
    I have found my better checkerbox background image again: in use as BG it looks like this:
  49. 1 point
    It's not only the same error in the log, but you're also doing the same thing wrong. You can't use $pages in functions because of variable scope. ProcessWire makes these object automatically available for the scope of the templatefiles, but inside a function is a new scope, so you need to either redefine the variable itself or use a function to get the object. // Template scope $id = 15; // simple variable, defined by you $pages = $pages; // the pages object, defined automatically by processwire function something(){ // this is now a new variable scope // neither $pages nor $id are available here. // the api variables are not a special global variable $pages = wire('pages'); $pages->find(""); // OR just wire('pages')->find(""); } It's the same reason, why you can't use $pages in modules. $page/$pages and the other variables are just convenient to use in templates. Everywhere else you need to call them differently.
  50. 1 point
    The problem is that page $f doesn't know the repeater item changed. Those repeater items are individual pages themselves. You can solve this by saving the repeater item ($g) rather than the owning page ($f): 1. Replace your $f->save(); with $g->save(); 2. Also replace your $f->of(false); with $g->of(false); 3. Lastly, I also suggest replacing your first line with this: $found = $pages->find("parent=/invitations/$user->name, guests.guest_name=" . $sanitizer->selectorValue($form['oldname'])); Adding that selectorValue() sanitizer in there will prevent problems from occurring if the guest_name happens to contain characters like commas or quotes.
  • Create New...