Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 05/14/2017 in all areas

  1. I tested this with your scripts and body ID and the correct order was maintained, so not sure why that's not the case for you. If you prepend a series of elements then the order will be reversed (because they will each be added as the first child element of the parent, one by one in order from top to bottom). But appending elements should maintain the order. My preference is to use <region> (or <pw-region>) tags for scripts and stylesheets. The idea being you place empty <region></region> elements in your _main.php at the place where you may want to add extra scripts or stylesheets from your templates. Then you can add multiple scripts in a single replacement without an unneeded container div appearing in your final markup.
    3 points
  2. I created a simple playlist
    2 points
  3. After this tutorial you'll have learned how to: Build a Process module Make an AJAX request to backend Serve JSON as response Let's say you want to display the latest orders in a dashboard that you can access from admin panel. And you want it to refresh its content with a button click. Most straightforward and proper way (that I know of) is to create a Process module, as they're built for this purpose. First, create a directory under /site/modules/, call it ProcessDashboard, and create a file named ProcessDashboard.module under that directory. Following is about the least amount of code you need to create a Process module. <?php namespace ProcessWire; class ProcessDashboard extends Process { public static function getModuleInfo() { return [ 'title' => 'Orders Dashboard', 'summary' => 'Shows latest orders', 'version' => '0.0.1', 'author' => 'abdus', 'autoload' => true, // to automatically create process page 'page' => [ 'name' => 'order-dashboard', 'title' => 'Orders', 'template' => 'admin' ] ]; } public function ___execute() { return 'hello'; } } Once you refresh module cache from Modules > Refresh, you'll see your module. Install it. It will create an admin page under admin (/processwire/) and will show up as a new item in top menu, and when you click on it, it will show the markup we've built in execute() function. All right, now let's make it do something useful. Let's add create a data list to display latest orders. We'll change execute() function to render a data table. public function ___execute() { /* @var $table MarkupAdminDataTable */ $table = $this->modules->MarkupAdminDataTable; $table->setID($this->className . 'Table'); // "#ProcessDashboardTable" $table->headerRow([ 'Product', 'Date', 'Total' ]); // fill the table foreach ($this->getLatest(10) as $order) { $table->row([ $order['title'], $order['date'], $order['total'] ]); } // to refresh items $refreshButton = $this->modules->InputfieldSubmit; $refreshButton->name = 'refresh'; $refreshButton->id = $this->className . 'Refresh'; // "#ProcessDashboardRefresh" $refreshButton->value = 'Refresh'; // label of the button return $table->render() . $refreshButton->render(); } where getLatest() function finds and returns the latest orders (with only title, date and total fields) protected function getLatest($limit = 5, $start = 0) { // find last $limit orders, starting from $start $orders = $this->pages->find("template=order, sort=-created, limit=$limit, start=$start"); // Only return what's necessary return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total ]; }); } When you refresh the page, you should see a table like this Now we'll make that Refresh button work. When the button is clicked, it will make an AJAX request to ./latest endpoint, which will return a JSON of latest orders. We need some JS to make AJAX request and render new values. Create a JS file ./assets/dashboard.js inside the module directory. window.addEventListener('DOMContentLoaded', function () { let refresh = document.querySelector('#ProcessDashboardRefresh'); let table = document.querySelector('#ProcessDashboardTable'); refresh.addEventListener('click', function (e) { // https://developer.mozilla.org/en/docs/Web/API/Event/preventDefault e.preventDefault(); // Send a GET request to ./latest // http://api.jquery.com/jquery.getjson/ $.getJSON('./latest', { limit: 10 }, function (data) { // check if data is how we want it // if (data.length) {} etc // it's good to go, update the table updateTable(data); }); }); function renderRow(row) { return `<tr> <td>${row.title}</td> <td>${row.date}</td> <td>${row.total}</td> </tr>`; } function updateTable(rows) { table.tBodies[0].innerHTML = rows.map(renderRow).join(''); } }); And we'll add this to list of JS that runs on backend inside init() function public function init() { $scriptUrl = $this->urls->$this . 'assets/dashboard.js'; $this->config->scripts->add($scriptUrl); } Requests to ./latest will be handled by ___executeLatest() function inside the module, just creating the function is enough, PW will do the routing. Here you should notice how we're getting query parameters that are sent with the request. // handles ./latest endpoint public function ___executeLatest() { // get limit from request, if not provided, default to 10 $limit = $this->sanitizer->int($this->input->get->limit) ?? 10; return json_encode($this->getRandom($limit)); } Here getRandom() returns random orders to make it look like there's new orders coming in. protected function getRandom($limit = 5) { $orders = $this->pages->find("template=order, sort=random, limit=$limit"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total ]; }); } And we're done. When refresh button is clicked, the table is refreshed with new data. Here it is in action: 2017-04-29_19-01-40.mp4 (227KB MP4, 0m4sec) Here's the source code: https://gist.github.com/abdusco/2bb649cd2fc181734a132b0e660f64a2 [Enhancement] Converting page titles to edit links If we checkout the source of MarkupAdminDataTable module, we can see we actually have several options on how columns are built. /** * Add a row to the table * * @param array $a Array of columns that will each be a `<td>`, where each element may be one of the following: * - `string`: converts to `<td>string</td>` * - `array('label' => 'url')`: converts to `<td><a href='url'>label</a></td>` * - `array('label', 'class')`: converts to `<td class='class'>label</td>` * @param array $options Optionally specify any one of the following: * - separator (bool): specify true to show a stronger visual separator above the column * - class (string): specify one or more class names to apply to the `<tr>` * - attrs (array): array of attr => value for attributes to add to the `<tr>` * @return $this * */ public function row(array $a, array $options = array()) {} This means, we can convert a column to link or add CSS classes to it. // (ProcessDashboard.module, inside ___execute() method) // fill the table foreach ($this->getLatest(10) as $order) { $table->row([ $order['title'] => $order['editUrl'], // associative -> becomes link $order['date'], // simple -> becomes text [$order['total'], 'some-class'] // array -> class is added ]); } Now, we need to get page edit urls. By changing getLatest() and getRandom() methods to return edit links in addition to previous fields protected function getLatest($limit = 5, $start = 0) { // find last $limit orders, starting from $offset $orders = $this->pages->find("template=order, sort=-created, limit=$limit, start=$start"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total, 'editUrl' => $order->editUrl ]; }); } protected function getRandom($limit = 5) { $orders = $this->pages->find("template=order, sort=random, limit=$limit"); return $orders->explode(function ($order) { return [ 'title' => $order->title, 'date' => date('Y-m-d h:i:s', $order->created), 'total' => $order->total, 'editUrl' => $order->editUrl ]; }); } and tweaking JS file to render first column as links function renderRow(row) { return `<tr> <td><a href="${row.editUrl}">${row.title}</a></td> <td>${row.date}</td> <td>${row.total}</td> </tr>`; } we get a much more practical dashboard.
    2 points
  4. hi abdus, thanks for your effort on helping others maybe i can suggest you to take a tool that lots of people are using here for creating micro-screencasts as animated gifs: http://www.cockos.com/licecap/ very easy, very helpful
    2 points
  5. Yes, ask them to install the locales for all languages that you'll need.
    2 points
  6. Is the Turkish locale installed on the system? You need to install it if not.
    2 points
  7. Thank you guys for the additional information. What I ended up is posting data like this: private drop(event: DragEvent): void { let payload = JSON.parse(event.dataTransfer.getData("application/json")); let widget = this.$root as any; let data = new URLSearchParams(); // ProcessWire CSRF protection does not recognize JSON data per default data.append(widget.tokenName, widget.tokenValue); data.append('foo', payload.foo); fetch("/admin/page/foo/bar/", { headers: { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", "X-Requested-With": "XMLHttpRequest" // Required for ProcessWire to identify as ajax request }, method: "post", credentials: "same-origin", body: data }) .then(response => response.text()) .then(text => console.log(text)) .catch(error => console.error(error)); } This request is perfectly recognized by ProcessWire the way you would expect and CSRF protection is working. It shouldn't be tedious to make use of security critical features like CSRF protection. I love ProcessWire because it saves me huge amounts of time. If I have to mess around with something to make it work that costs me time. It doesn't matter if you consider it to be an easy task or if it's the developer's responsibility. It takes time and there's room for improvements. I'm just giving feedback. Take it or leave it.
    2 points
  8. The workflow is as follows: Enter Client ID and Secret Enter Page Name OR Page ID: If you enter the Page Name, make sure to copy it from the Facebook page url similar to `https://www.facebook.com/NameWithoutSpaces` => NameWithoutSpaces would be the Page Name Add `$events = $modules->get('FacebookEvents')->getEvents();` to a template and open the belonging page (frontend) Go back to module settings and check (reload before) if a page id has been added. If not, your page name could not be resolved. Now you should be able to receive events: $events = $modules->get('FacebookEvents')->getEvents(); echo "<ul>"; foreach ($events as $event) { echo "<li>{$event['name']}</li>"; } echo "</ul>"; $events is an array, so you have to use the following syntax: `$event['propertyName']`. I found a little bug and updated the module, so please make sure to get the latest version.
    2 points
  9. On the "Details" tab of your PageTable field settings:
    2 points
  10. Hi, I have created a site profile that shows how to integrate ProcessWire 3.0 with Vue 2.0. See repository here. How this site profile works This ProcessWire site profile is loosely based on the REST API tutorial by @gebeer. Here are the most important steps to reproduce it: Admin settings Create an api template with default settings and just the title field assigned to it. Refer to @gebeer tutorial for further details Create a pages and nav templates with just the title field, for both template tick “Allow URL Segments” in the “URLs” tab (see attachment) Create a home template, this is going to be the single php file that will load your Vue SPA. Assign this template to the root of your website Any other template you create should have the “Alternate Template Filename” field in the “Files” tab set as home (see attachment), in this way if a user enter the website from any url that is not the root, ProcessWire will always redirect to the home template, Vue router will handle the url and call the right data through the REST API Under the root, create an api page and assign the api template to it (you can set “hidden” to this page so doesn't show up in the menu) Under the api page, create the pages nav and pages (see attachment), and assign the templates nav and pages to them. Now you have the www.sitename.com/api/pages and www.sitename.com/api/nav urls that you can use to fetch the JSON data PHP template setup In the templates folder, create home.php file and leave it empty, the HTML will be generated by webpack Now create pages.php and nav.php files. On these files is where we return the JSON data, based on the right url segment. Again, refer to @gebeer tutorial for further details on this matter. Note that I wrote a PageFields class that I use on these templates to fetch ProcessWire fields. The fields that are supported are text, textarea, repeater, img. Other fields may work but I haven't tested them. See the REST API setup for further details about how to use the PageFields class REST API setup You can decide what fields are included and what fields are excluded by passing a configuration array to the PageFields class. You can find here a list of the available configuration settings. See examples below. Show only selected core fields: $pageFields = new PageFields($p, [ 'fld_core_included' => ['url', 'httpUrl', 'template'] ]); Show no global fields, and only selected custom fields: $pageFields = new PageFields($p, [ 'fld_core_included' => [], 'fld_include_all' => false, 'fld_included' => ['title', 'gallery'], ]); On a gallery image field, hide breakpoint listing and show only httpUrl field: $pageFields = new PageFields($p, [ 'img_fld_overrides' => [ 'gallery' => [ 'fields' => ['httpUrl'], 'bp_list' => false ] ], ]); Webpack setup The most important file of all Webpack setup is config/index.js. On line 33 you need to provide your domain name so that Webpack can proxy the ProcessWire REST API to the Webpack dev server. Without this you wouldn't be able to take advandage of the Webpack hot module replacement feature which allows you to reload a vue module without refreshing the page, it also allows you to keep the state of the app. Notes My REST API may have bugs, this is just an example of an integration with ProcessWire, I suggest you either build your own REST API or use the awesome GraphQL module by @Nurguly Ashyrov. Todo Replace REST API with the GraphQL module. This requires vue-apollo, the Apollo/GraphQL integration with Vue, and vue-supply for integration with Vuex.
    1 point
  11. While a relatively quiet week due to travel, the 3.0.62 version has been merged from dev to master, plus a few other small updates in this week's blog post: https://processwire.com/blog/posts/pw-3.0.62-master/
    1 point
  12. I've been using ShareX for the screencasts, but it can't handle high DPI screens very well (might be ffmpeg's fault, though), and cursor is offset a bit. Just tried LICEcap, it seems to work on high DPI screen just fine. I'll use this one from now on. Thank you very much for the suggestion @bernhard!
    1 point
  13. If I remember correctly, there were a couple of mods that weren't enabled by default in the initial apache/php install that needed to be set. I don't remember which ones specifically, so here is a listing of mine you can use to compare... access_compat.load alias.conf alias.load auth_basic.load authn_core.load authn_file.load authz_core.load authz_host.load authz_user.load autoindex.conf autoindex.load cgi.load deflate.conf deflate.load dir.conf dir.load env.load filter.load headers.load mime.conf mime.load mpm_prefork.conf mpm_prefork.load negotiation.conf negotiation.load php7.0.conf php7.0.load rewrite.load setenvif.conf setenvif.load status.conf status.load
    1 point
  14. @kixe thank you for clarifying. I had already suspected the repeater to be the cause.
    1 point
  15. I fixed the typo, thanks for the hint. The translation string has been changed, some may need to translate it again! It started pretty simple (for private use only). Then I did the "mistake" to put it on Github . But if you don't need something special, I think/hope it's still pretty easy.
    1 point
  16. Thanks a lot I found a solution to find insatalled LC files with following code: print_r(ResourceBundle::getLocales('')); it lists all installed LC files names. Then i found "tr_TR" and additionaly try to add ".utf" and it works
    1 point
  17. The hook doesn't create a page. The hook is called if a page is created. If you add a page with a repeater field in its template the hook is called twice, because 2 pages have been created. If you have 2 repeater fields Pages::added will be triggered 3 times and so on. This is not a bug.
    1 point
  18. I tested 1), 2) and 3). They work all. If you want to use file_get_contents, you need to pass an URL returning the content. Otherwise the code won't be interpreted. Stackoverflow:
    1 point
  19. What did the trick for me was editing a core file like @abdus suggested:
    1 point
  20. The error you saw just after the upgrade was regarding Set Locale and not the language files themselves. So, not worry about them (they are located at /assets/files/*language id*/file.json See this thread:
    1 point
  21. Did you change the extension of the language file to .UTF8 instead of the original .json? If yes, change it back. And delete the assets/cache folder and try again.
    1 point
  22. Hi #1 Do not use the email message textarea in module settings, write your own using php and access the title as you 're used to. 0) $emailMessage = file_get_contens('https://url/to/message.php'); // or include partial etc. $modules->get('SimpleContactForm')->render($array('emailMessage' => $emailMessage)); #2 Just pass an action parameter as option: $modules->get('SimpleContactForm')->render($array('action' => './#anchor'));
    1 point
  23. Unfortunately full language support doesn't works properly since the core function LanguageSupportPageNames::getPagePath() isn't hookable, whereas Page::path() is. I posted a request to change this. Support welcome. Thanks in advance. https://github.com/processwire/processwire-requests/issues/94
    1 point
  24. @maxf5 Please have a look at this post. Furthermore turning caching on could be complicated because the value of the hidden date field gets cached and this value is used for spam protection...
    1 point
  25. hi codecoffeecode, you can try to use something like my solution for your import if you are only doing the import once (and not need your users to be able to import anything). it's very easy to import all kinds of data like this. adrian will be offline for some weeks... hope that helps
    1 point
  26. @theo, the solution proposed in this thread involves creating a dedicated parent for all PageTable pages under the Admin branch of the page tree. Would this not solve your issue? You cannot modify the methods in ProcessPageListRenderJSON.php in a module or in any other way that means the changes will not be overwritten in a future update because the methods in this class are not hookable. Well, not apart from the construct() method anyway, which can't help what you are trying to do.
    1 point
  27. i found the solution in related template setup:
    1 point
  28. just wanted to let you know that i found a very nice jsdiff library today that is ideal for this usecase (line-based diff): https://github.com/kpdecker/jsdiff/ i'm not sure any more if a module like this would make sense, though. i built a module that makes adding and removing fields and templates very easy: public function ___upgrade($from, $to) { // define variables $fat = $this->wire->modules->get('FieldsAndTemplatesHelper'); $pages = $this->wire->pages; // upgrade if($from < $to) { // 001 if($from < 1 AND $to >= 1) { $this->message("upgrade v001"); // create templates and fields $t_clients = $fat->createTemplate('clients'); $t_client = $fat->createTemplate('client'); $f_domain = $fat->createField('domain', 'text'); $f_nossl = $fat->createField('nossl', 'checkbox'); // early exit if anything went wrong if(!$t_clients) return; if(!$t_client) return; if(!$f_domain) return; if(!$f_nossl) return; // apply settings $t_clients->noParents = -1; $t_clients->childTemplates = [$t_client->id]; $t_clients->childNameFormat = 'title'; $t_clients->save(); $t_client->noChildren = 1; $t_client->parentTemplates = [$t_clients->id]; $t_client->save(); $t_client->fieldgroup ->add($f_domain) ->add($f_nossl) ->save(); // create page $p = new Page(); $p->template = $t_clients; $p->parent = 1; $p->addStatus(Page::statusHidden); $p->addStatus(Page::statusLocked); $p->name = $p->title = 'MyClients'; $p->save(); } // 002 if($from < 2 AND $to >= 2) { $this->message("upgrade v002"); } [...] in many occasions it would not make much sense to have the diff-tool because you would have to change the code anyway, eg: $t_clients->childTemplates = [$t_client->id]; and all the correct names of the options can easily be inspected via code inspector: so... i guess i will not take this further or are there other opinions?
    1 point
  29. Forked this module a few days ago and ended up in a complete rewrite. Still under development and testing but working properly here. What is different? Do not worry about page naming. The root page is defined via ID. Full multilanguage support * take in account $template->slashUrl settings take in account LanguageSupportPagenames::useHomeSegment settings Fallback to default 404 page if custom 404 is not set Throws exception in case of misconfiguration Access of pages residing in a MultisitePageTree for other domains other than the corresponding root page is disallowed except for superuser Pages inside a MultisitePageTree accessible only via the modified url if called from the corresponding domain (throws 404 instead of unexpected redirects) Page paths are set via hook in Page::path() instead of modifying the return of Page::render() Crosslink support via the RTE (CK Editor) Link plugin Page view links in admin with correctly modified urls * under conditions: see next post! Feedback welcome. Have a try: https://github.com/kixe/Multisite/tree/kixe
    1 point
  30. Thanks @thomasaull that would be great! There is an amazing new tutorial https://auth0.com/blog/vuejs2-authentication-tutorial/ if you don't mind having the users saved on Auth0. After all, Auth0 is the company behind the JWT website so they should implement it properly and you can obviosuly still keep all users in sync with ProcessWire db. But regarding Rest API I'd rather use GraphQL instead because that seems more flexible.
    1 point
  31. Ah it's because the URL in the JS was search to url: rootURL + 'search/', (rootURL being declared as var rootURL = config.urls.root;) but I have multi languages set up on the site so the URL was wrong.
    1 point
  32. You can stuff the values into $input->post or you can set the HTTP headers, so you can work around it.
    1 point
  33. Maybe you can create separate template for participants, wich will contain all needed field for it. In your event template add page field (Autocomplete or InputfieldChosenSelect) and allow a creation of new pages through this field. So, if your participant is registered you would select it, but if not, you can just enter a name of this participant and add it to the list, but for the next event it will be already in the list of your participants ( will contain only name ). In that way, all of the partisipant will have some unique identifier and it will be quite easy to count them https://processwire.com/api/ref/wire-array/unique/
    1 point
  34. When there is the need to check a new participant if he matches one of the ones enrolled previously, I think you can't avoid having an identifier for every participant. Edit: Assuming every participant is a page (the PW way ), the page's ID would be the natural unique identifer for your purpose.
    1 point
  35. Thanks @thomasaull ATM I am waiting for GraphQL module support for RepeaterFields before I integrate it in processvue. I'll implement the JWT login later on because I haven't used it before so I need to learn how it works, but it is definitely on my list of things to do. I don't know how JWT should integrate with GraphQL too, I'll probably ask @Nurguly Ashyrov if he has any idea of how I should integrate it. There's also the problem that GraphQL allows you way too much freedom on what kind of query you can perform on the client, to prevent that I've got to implement persisted queries.
    1 point
  36. I am glad that this project is helping ProcessWire getting more devs on board :). I just want to say that I wouldn't have been able to finish ProcessVue if it wasn't for the amazing ProcessWire community. I believe that the community truly is the biggest selling point for new users (like me). Before trying ProcessWire I used OctoberCMS for a while but when I was stuck I got 0 support from the forums, so...althought the CMS is based on the amazing Laravel framework, I just left! I think that ProcessWire is extremely powerful and flexible and with time will become the tool of choice for frontend developers, the new GraphQL module will also help on this direction. Droves of frontend developers are looking for a CMS like this, they just don't know it exists! The usual keywords they use when looking for a SPAs CMS is "Decoupled CMS" or "Headless CMS", and I believe that that's exactly what ProcessWire is for! Some frontend developers prefer to use NodeJS, but the learning curve is huge if you need it for a non trivial project, and the worst thing of all is that after two weeks ANY js tool you may have used is outdated. See for example how Angular has been replaced with React or Vue, and Gulp with Webpack. That doesn't mean that I am against improvements in this regard, I just feel that it's just too much for us poor frontend devs to cope with! ProcessWire is stable, easy to use and won't change API every week. BTW, after that I migrate ProcessVue to GraphQL I am also planning to add Auth0 login integration with JWT, as I think that login/signup is a common feature in SPAs. I am sure I'll have to annoy @Nurguly Ashyrov and the rest of ProcessWire community for getting it in sync with ProcessWire users, but the result should be quite useful
    1 point
  37. Hey, @microcipcip! I actually do not know if it is possible to do with the profile exporter via setting . Though I am sure it should be. Removing node_modules should be easy - you can do it before the export (or temporary prefix the folder with a dot ). Adding .config type files is probably only possible the way you suggest. But as I said I think all those things should be eventually possible to do with the module options, as it is very powerful way to share one's work in PW. Please list the issues you cannot overcome in the module support board or even better on github. Edit: Seems like a .file issue is already present. I am going to add my voice to it. Please do too if you think it deserves it.
    1 point
×
×
  • Create New...