Jump to content

flydev

Members
  • Posts

    1,327
  • Joined

  • Last visited

  • Days Won

    47

Everything posted by flydev

  1. A note for myself, and people interested on how an install can scale : ProcessWire 3.0.175 adds new database scalability options https://processwire.com/blog/posts/pw-3.0.175/
  2. Check with count($page->hero_items))instead of $page->hero_items as it return the field if it exist, not what's inside.
  3. That's because header are already sent before, I mean, there is already output. Move for testing the block, on the top of the _main.php file: <?php namespace ProcessWire; [...] if($config->ajax && $input->post->bookmark){ [...] echo json_encode($json); return; [...] and adjust : `$.post('<?= $page->url?>` to `$.post('<?= $pages->get('/')->url ?>`
  4. We can see that first, request (XHR) is fired with the right data. You need to show us what is inside the "preview" tab to see what's the answer returned (the payload is what you send, the preview is the answer you get). About the code in your last post, (I didn't saw it before), should go in inside there: <?php namespace ProcessWire; [...] if($config->ajax && $input->post->bookmark){ [...] echo json_encode($json); return; [...] This chunk of code is what handle the actions, then put the logic inside it. if($config->ajax && $input->post->bookmark){ bd($input->post->bookmark); // debug post with tracy, lets see whats inside // if something was submited, but the user is not logged in, nohting will be done and we show an error if(!$user->isLoggedin()) { $action = ''; // if user not loggedin, let go to default case. } // user is logged-in else { $bookmarkid = $sanitizer->int($input->post->bookmark); // id of the bookmark $action = $sanitizer->name($input->post->action); // action that need to be done $bookmark = $pages->get($bookmarkid); // instead of $posts, get the bookmark from submitted id to work with bd($bookmark, "bm to work with"); $bookmarks = $user->getUnformatted('bookmarks'); // if($action === 'remove') { // save bookmarks //$user->setAndSave('bookmarks', $bookmarks); //$this->halt(); } switch($action) { // which action ? case 'save': // save logic if(!$bookmarks->has($bookmark)) { // add bookmark $bookmarks->add($bookmark); $user->setAndSave('bookmarks', $bookmark); $message = 'Borat saved bookmark "'. $bookmark->id .'" ?'; } else { $message = 'Nothing done'; } $success = true; break; case 'remove': // remove logic if($bookmarks->has($bookmark)) { $bookmarks->remove($bookmark); $user->setAndSave('bookmarks', $bookmarks); $message = 'Borat removed bookmark "'. $bookmark->id .'" ❌'; } else { $message = 'Nothing done'; } $success = true; break; default: // Default will throw an error $message = 'error borat'; $success = false; } // build the response data array that will be returned in `data` from `.done(function(data) {` $json = array( 'id' => $bookmarkid, 'action' => $action, 'message' => $message, 'success' => $success ); // convert data and send as JSON header('Content-Type: text/json; charset=utf-8'); echo json_encode($json); return; } The issue can be that the template is "include" in the _main.php. I look into it later, show us first the request answer. Your are not far from ✅
  5. it is not to be excluded that I misunderstood your previous question. I thought you were writing the function `if($config->ajax && $input->post->bookmark) {` in a loop. The template is fine ? The page url should be the page where you write the block which handle your request and return the data: <?php namespace ProcessWire; [...] if($config->ajax && $input->post->bookmark){ [...] echo json_encode($json); return; [...] So as the whole code seem to be in the same template, and the template name is `/all/`, then it should be fine. You can debug and see what/where the request go by looking at your dev. console (in my case, it point to root `/`) :
  6. Get rid of writing the $action = $input->post->option('bookmark', [ 'save', 'remove'] ); block in a loop, you do not need that. If you look at my example (made some corrections for consistency), you are capturing the action made by the user in the `$input->bookmark->action` and the id in `$input->bookmark->id` (form data built in the $.post() ajax jquery code): (I don't know what is the `$input->post->option('a', ['b', 'c'])`) thing, but we dont care) bd($input->post); // debug with tracy /** contain: * action: 'save' * bookmark: '1024' */ Then just handle the request : <?php /** threat ajax (pw) => https://processwire.com/api/ref/config/#pwapi-methods-runtime * Better to write a dedicated template which will receive the request. * That is a good candidate and exercise to try the module AppApi ? */ if($config->ajax && $input->post->bookmark) { // bd($input->post->bookmark); // debug with tracy, uncomment if you have it /** contain: * action: 'save' or 'remove' * bookmark: '1024' */ $bookmarkid = $sanitizer->int($input->post->bookmark); $action = $sanitizer->name($input->post->action); switch($action) { // which action ? case 'save': // save logic $message = 'Borat saved bookmark "'. $bookmarkid .'" ?'; $success = true; break; case 'remove': // remove logic $message = 'Borat removed bookmark "'. $bookmarkid .'" ❌'; $success = true; break; default: $message = 'error'; $success = false; } // build the response data array that will be returned in `data` from `.done(function(data) {` $json = array( 'id' => $bookmarkid, 'action' => $action, 'message' => $message, 'success' => $success ); // convert data and send as JSON header('Content-Type: text/json; charset=utf-8'); // forgot this line echo json_encode($json); return; } ?> <!-- the only one loop --> <!-- you dont really need a form --> <!-- <form action='<?=$page->url?>' method='post'> --> <?php // dummy bookmarks loop logic for the show $bookmarks = $pages->find("template=dummypage, limit=5, sort=sort"); foreach ($bookmarks as $bookmark): ?> <?php if($user->isLoggedin()): ?> <!-- show button to save and remove this page to bookmarks --> <!-- of course, make your own logic to show one or other button --> <!-- add a data attribut to each button --> <button class="button bookmark" name='bookmark' value='save' data-id='<?= $bookmark->id ?>'>Save <?= $bookmark->id ?></button> <button class="button bookmark" name='bookmark' value='remove' data-id='<?= $bookmark->id ?>'>Remove <?= $bookmark->id ?></button> <?php endif; ?> <?php endforeach; ?> <!-- </form> --> <script> jQuery(document).ready(function($) { // when clicked, send ajax request to server $('button.bookmark').on('click', function(e /* add event arg */) { var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked'); if (!btn) { console.warn(`⚠️ Bookmark already saved to profil`); return false; } $.post('<?= $page->url /* send to the page which have the logic and return json answer */ ?>', { action: $(this).val(), bookmark: btn.data('id') }) .done(function(data) { console.log(data, "? response from ajax req"); // $(this).removeClass('clicked'); // remove `clicked` class, can be added on the foreach loop if the bookmark is already saved and then locked, or whatever.. console.info(`✅ Bookmark id => ${data.id} ${data.action} `); }); //e.preventDefault(); // you didn't need a <form>, and if you really want it, add this line to prevent default behavior and stop the redirection }); }); </script> I will make you an example to save the page, but if I were you, I would just save a "page (ID)" in a page-reference field in the profile.
  7. I feel like a oldschool nerds ?‍♂️ listen bits in loop for motivation (https://m.pouet.net/ - http://www.bitfellas.org/) start coding in js and you end up writing asm ?
  8. Yes, you have to make some test on your side to understand better, but that is not very complicated once you get the "flow". On your example, you can paste my code chunk - if ($config->ajax)... - on your template that refer to the page /all/ and then in the click $.post call, just put the url from $pages->get('/all/')->url, it should work out of the box. PS: If it's a "fun" project, and could be open-source, do not hesitate to export the site-profile to be available to us, it rocks really lol
  9. I don't really get what is your issue, the click that still redirect ? If yes, just add `e.preventDefault()` to your js script to prevent default behavior as your are submiting a form. <?php /** threat ajax (pw) => https://processwire.com/api/ref/config/#pwapi-methods-runtime * Better to write a dedicated template which will receive the request. * That is a good candidate and exercise to try the module AppApi ? */ if($config->ajax && $input->post) { bd($input->post->bookmark); // debug with tracy /** contain: * action: 'add' * bookmark: '1024' */ /** * you can write some logic here, eg., to grab the post data sent by the ajax request */ $bookmarkid = $sanitizer->int($input->post->bookmark); // eg. you can get a page from that $bookmarkedPage = $pages->get($bookmarkid); // send response data back $message = 'Borat added bookmark "'. $bookmarkedPage->title .'" ?'; $success = true; // build the response data array $json = array( 'message' => $message, 'success' => $success ); // convert data and send as JSON header('Content-Type: text/json; charset=utf-8'); // forgot this line echo json_encode($json); return; } ?> <?php // dummy bookmarks loop logic for the show // foreach($bookmarks as $bookmark) : $bookmark = $pages->get(1024); // forgot this line ?> <!-- you dont really need a form --> <!-- <form action='<?=$page->url?>' method='post'> --> <?php if($user->isLoggedin()): ?> <!-- show button to add this page to bookmarks --> <!-- add a data attribut to each button --> <button class="button bookmark" name='bookmark' value='add' data-id='<?= $bookmark->id ?>'>Save bm #1</button> <?php endif; ?> <?php //endforeach; ?> <!-- </form> --> <script> jQuery(document).ready(function($) { // when clicked, send ajax request to server $('button.bookmark').on('click', function(e /* add event arg */) { var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked'); if (!btn) { console.warn(`⚠️ Bookmark already saved to profil`); return false; } $.post('<?= $page->url /* send to the page which have the logic and return json answer */ ?>', { action: $(this).val(), bookmark: btn.data('id') }) .done(function(data) { console.log(data, "? response from ajax req"); // $(this).removeClass('clicked'); // remove `clicked` class, can be added on the foreach loop if the bookmark is already saved and then locked, or whatever.. console.info(`✅ Bookmark id => ${btn.data('id')} saved to user profil`); }); //e.preventDefault(); // you didn't need a <form>, and if you really want it, uncomment this line to prevent default behavior and stop the redirection }); }); </script>
  10. 1 to 10 ? => 1 ??? Please, make a backup, that is your most important step to do/follow/complete with success before going further. And just in case: https://processwire.com/docs/start/install/upgrade/ https://processwire.com/modules/process-wire-upgrade/
  11. @orchardheightsdental the version is on the bottom-left corner once in the admin page. And the module is available, if i'am correct since the version 3.0.68. 1. Click on the Modules link and follow my screenshot
  12. You can find the module in Modules > Install > ProcessPagesExportImport
  13. I understand, and my point to suggest you to ask @ryan directly if his pro module can fit your needs, is because ryan is used to give more flexibility instead of locking the user to a finished functionality. I mean, there is maybe some hooks that can be used to link things to an user. Without speaking about that you benefit ryan's code and insight ? Anyway, I will might buy it for a project and could give more feedbacks. Stay tuned.
  14. I forgot about this thread ? I made some corrections and published it to github, and yes, you can try it. As always, make a DB backup before installing it. See: https://github.com/flydev-fr/UserLikes To install it ?
  15. You can also use the core module `ProcessPagesExportImport` to obtain a JSON config of the export, and then from wordpress, you can just make a script to create the pages you want. A simple google search give `plugins/json-content-importer` as a result, by the title it seem it can do the job by mapping field and value, but in reality, I do not have damn clue.
  16. Another example, clients receipts generated by the terminal are sent on real time to the "receipt app" which can be seen there: https://facture.kingspark.fr/ You can see something more than ~4M pages. This following example, is a dashboard for accouting things, a database of 10gb and more than 11M pages: (traduced by on-browser-google-trad)
  17. This. And you can have a read on a example I posted recently, I will add you two or three more screenshots of the page tree created by our softwares. Edit: Others examples added to the thread, I will make one day a showcase... but you just have to know that no page are created from the admin, but by API calls. The module used is the one linked by @MarkE and made by @Sebi . Anyway, it let you use the workflow you want, I mean freedom. And also, you can note that it scale pretty well (even if my apps are not so quite optimized, just saying).
  18. ? An issue was reported on github about the FTP functionality which was not working on PHP-8.1, which is fixed in the dev branch (Duplicator v1.4.24). As I didn't tested to backup a thing on GoogleDrive and Amazon S3, if you spot other issues, please feel free to report it in this same thread or fill a github issue. It might also be the time to implement something related to GDPR (package encryption) which I think was already discussed somewhere. Thanks you.
  19. It depend on how you configured the field. You can make it work by calling $page->images->first()->url The doc say: For more informations: https://processwire.com/docs/fields/images/
  20. Confirming that your snippet works fine here with LoginRegisterPro. But @Liam88 you have an issue, and if you are trying to debug/navigate to the url given by your $u->id link when there is no urlSegments, then you will end up on wrong user id, and then certainly on a random page ? // no urlSegment1 present // display links for user profiles? foreach($users->get('roles=login-register, sort=name') as $u) { echo "<li><a href='$u->id/'>$u->name</a></li>"; } $users->get() return a Page or null, so you are only getting iteration on the page's properties. You should use $users->find() to get the right $u page and his real id. Try with the correction and report back. And just a note: This method do not exist, it's isLoggedin(), with `Logged` and `in` in lowercase. Just in case to avoid future hassle ?
  21. But role is a thing, and permissions is another and this last is what you are looking for. Look there - https://processwire.com/docs/user-access/permissions/ - and try to play with them, then give a look at those modules : https://processwire.com/modules/admin-restrict-page-tree/ https://processwire.com/modules/custom-admin-menus/ For example, I have a role "accountant" and he do not have access to the page tree or any other features than the "accounting" module.
  22. Hi, you could use PageSum for simplicity. Just install the module and then for example: $selector = "parent=/products/, seller=$item"; // your selector $cnt = $pages->count($selector); // number of pages that match $selector if(!$cnt) echo 'No rated product found'; $sum = $pages->sum($selector, "rating"); // get sum of all matched products from "rating" field with "PageSum" $avg = $sum / $cnt; // math echo "Average rating of {$item->title} product (x$cnt) is ". number_format($avg, 2); // format number as needed
  23. Look at this example. Its made with the old version of RestAPI first made by @thomasaull years ago. To get the context, you can navigate to https://kingspark.fr and https://valideur.mykingspark.fr . I am speaking about a system which give the possibility to some of our client use their mobile or a barcode scanner device to give "free of charge parking" of their customer. Its composed with custom hardware, software and devices or mobile apps (you see it on GooglePlay). Image #1: List of Parkings // Image #2: The custom made SAGAS Terminal // Image 3: A configured User / Device available for registration and use. On the first picture, you can see a list of "Parkings", and some can have the "Valideur" functionality. We can configure attached devices and some users with specific role to get access for registration. And then, imagine the following. A customer land on the parking, grab a ticket and go to their rendezvous At the end, the guy in the office use his "Barcode Scanner" to "validate" the ticket of the customer; This mean that the customer will not pay anything when landing on the terminal to exit the parking, and when he will present the ticket on the barcode-reader installed on the terminal (the "black hole" you can see on the center in the picture), it will be recognized by another software and thought a call on ProcessWire Rest API backend. I made you two screencasts, the first is the software which once installed and registered, get the quota (number of validation available) from the user assigned to the license-code, and the second is my mobile which get notification sent from ProcessWire (by this module) from a monitoring system to get real-time information on registration. If you have any question, do not hesitate. There a more example, but you should get the whole idea ?
  24. What I would do in your scenario is: 1) Catch any \Exception and return a valid JSON with for example: $result = ['success' => false, 'error_msg' => 'Description of the error']; and log the exception original message in your logs 2) To "catch" a notice or warning, do that from a script that analyze a custom Apache ErrorLog and send you a mail on a pattern you would like to receive the mails. Just do not show "errors, mean warning, notice..." but log them into a custom Apache ErrorLog. 3) <- should be the first step, write Test Cases, test your code, and more over, do not upgrade a production code directly, as the most notice and warning you will get once your code is working is from deprecated code (PHP version, ProcessWire, Modules..). And do not forget that you can handle many scenario, eg., you can catch WireException, CustomException, \Exception. You could read some Ryan's code by opening files that are in the wire\core folder to get some example.
  25. I will take the time to answer your question a bit later @bernhard with some real example I use at work. But to get a general idea, when you go on the AppStore or GoolePlay, every app you see need to "discuss" with an (generally "Rest") API. Obviously there are many constraints to take into account when choosing the backend that will provide the API. For example, at work, I have servers that have to support quite heavy loads and that are written in Pascal. You can take a look there: https://synopse.info/files/html/Synopse mORMot Framework SAD 1.18.html#SOURCE (⚠️ it can hurt your head for the day ?) and read the general purpose and concept. I also have three ProcessWire backends (that need to be merged) that serve more user-oriented needs, such as apps delivered to customers or our technical maintenance group.
×
×
  • Create New...