Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/21/2019 in all areas

  1. In ProcessWire the wisdom usually is to avoid selecting much data at all. That's the sole reasoning for e.g. the nesting you described. It won't help at all if you want to aggregate over e.g. the last 5 years of weather data. The biggest question still open in this topic is "what for?". Without knowing the patterns of how you intend to access the stored data and which timeframes of aggregations of this data are appropriate it's not really possible to tell what you need. If you're fine with reports taking a hot minute to aggregate you're in a whole different ballpark than if you need huge aggregations to be live and instantly available in some web dashboard. Especially if you plan to hit the latter case I'd also suggest looking at proper databases for time series data, especially if the number of entries is meant to grow beyond the ~500k–1kk mark. I'd look at influxdb or postgresql with timescale plugin. Using pages in processwire might make sense for a mvp, but if things should scale it'll be a lot of manual querying even in processwire, so I'd opt for the proper solution from the start. Given the volume of data I doubt you can avoid getting more intimate with databases, as you just need to aggregate data directly on the db side, which processwire doesn't support to begin with.
    5 points
  2. Hi @mr-fan, you are dealing with a very interesting project! Wow… it brings me back to my previous lives in Microcontrollers and Nand Flash storage... If your MCU timer is sampling analog data every 10 minutes, and your objective is to store them for 5 years at this granularity level (10 minutes), as you have calculated, it means 2.6*10^5 records. That's not trivial at all. I love PW page-storage capabilities, but, on my personal opinion, resulting storage solution would be less efficient and performant than directly storing data into a MySQL table. I would use MySQL table for data storage and PW pages for data post-processing and reporting. A lot would depend from which kind of post-processing you are going to need, but as you are building a simple (but big!) data logger, another option for storage could be even to create a simple flat file to which you are appending a new record every 10 minutes. However MySQL, in the long run, will give much better flexibility, versatility, and performance. If you wish to look for a simple example of using MySQL from inside PW I suggest you to explore at FieldtypeComments core module. Comments in PW are not made using PW page-storage, but as a MySQL table (field_comments) so you can get some useful hints in dealing with MySQL schemas from within PW. Of course you do not need to create a separated MySQL database for your logger, it can be simply a new table inside your existing PW database, exactly as PW does to manage comments. Please also note that PW library provides an embedded support for dealing with MySQL (I did not use it so far, but .. now I feel I need to make a trial soon…) in $database API variable. By the way, if you need any help to create a MySQL data logger table inside your PW database, just give me the data specs, I would be glad to help you in setting up a trace. Wish you a nice week-end.
    4 points
  3. Thanks for all input again. Again i was not to clear - single project just for me and just for information no public hits no further usecase for the system. Yes LostKobraKai i think i did'nt plan on this one...until now i didn't have such a usecase or wasn't deep into database performace (what is not really needed for normal websites if you use PW as a base...it scale for this cases and i know that well) So may i am a little bit to naive on this project - and my feeling said "Hey try to ask the dev guys in the forum for hints" ...;) I would first go the hard way and make my hands dirty and will try how far PW could go on this one.... Yes nested page setup like metioned wont scale on things like "live aggregating" all saved data, but i think i would be happy if i could get things like average of year/month/week and store them in separate fields on the dedicated template - so like netcarver wrote if i am modest in granularity it will run fine. But a important Tipp is the FieldtypeEvents module from Teppo, i forgot this one - it would work great for the week dataset so many pages are spared. What i will try with dummy data is now: templates /station/year/month/week station (id, title) year (id, title, temp, humidity, pressure, rain, light) - the average - average from 12 childpages should be no problem month (id, title, temp, humidity, pressure, rain, light) - the average - this will end in 52 childpages for a join for the average week (id, title, records - modified eventtype field for the intraday records) - this will end in 672 entries (if i take a record every 15 minute) in one week field Problems on this one would be selecting from date to date but i would be fine with averages of weeks or months...and maybe the amount of fieldtypeevent tables (52 per year)? I don't have the time to investigate in other DB systems, since this year i've got one more little maid that take over my speartime ? But with your interesting input i can think i get in the right direction, and don't have wrong views on aggregation and reporting of the collected data. Since alle of you pointed out that the collection wouln't be the problem with PW or MySql....but all the other stuff. And on these points i could make compromise since this is only a hobby project. I will report (even if it took a while). Even on such rare and offbeat questions in this forum you get helpfull and friendly answers! You all made my weekend! - I love this forum, it is a hidden island in the www
    3 points
  4. Absolutely agree with this – use case for the data matters a lot! In my experience MySQL queries (with decent indexes) tend to be pretty fast until you reach the scale of millions of rows, but if this is going to be a public-facing service that gets a lot of hits and needs to generate all sorts of reports real-time then definitely consider alternatives right from the get-go. Might be a good idea to look into them anyway, but if it's a one-off project and you're likely to stay in 200-300k record range, you're probably not going to get a major benefit out of them. That being said, if you already know what your data is going to look like, you can take some guesswork out of the equation by starting from a simple proof of concept: create a database table for your data, add a script that generates some 200-300k rows of random mock data based on your actual expected data format, and build a proof of concept graph to display said data. If the database concept doesn't pan out, i.e. it's too slow or something like that, you can just swap that to something more performant while keeping other parts of the application. Either way it's often a good idea to build your product in layers, so that if a specific layer – graph library, database, or something in-between – needs to be swapped for something else, other layers remain more or less the same ?
    2 points
  5. Not sure how familiar everyone here is with the inner workings of ProcessWire, so just to expand on this a little bit: FieldtypeComments is a ProcessWire Fieldtype module, and Fieldtype modules can define their own database schema. The FieldtypeEvents module was built as an example for custom Fieldtype modules, and if you take a closer look at FieldtypeEvents::getDatabaseSchema() (and other methods in that class), you should get a pretty good idea of how this stuff works. On the other hand if you want to store loads of data and don't really need/want said data to be stored in an actual field (accessible via the API and editable in the Admin), you can also define a custom database table, just as Edison pointed out above. You can find some examples of working with custom database tables from the ProcessChangelog module. Of course you don't have to wrap the table creation etc. into a module – not unless you expect to set this thing up on multiple occasions ? One last point on naming custom tables: if you create a truly custom database table, you'll want to steer away from any native table names. This includes field_*, since that prefix is reserved for field data.
    2 points
  6. Try: $wire->addHookAfter('Field::getInputfield', function(HookEvent $event) { $page = $event->arguments(0); $inputfield = $event->return; // Only for non-superusers if($event->wire('user')->isSuperuser()) return; // Only for a particular Repeater page ID if($page->id !== 1553) return; // Set collapsed to Inputfield::collapsedNoLocked or Inputfield::collapsedHidden as suits $inputfield->collapsed = Inputfield::collapsedNoLocked; });
    2 points
  7. Hello, I thought it might be useful to post a CSP I've recently deployed using this module. Every site is different - there's no prescriptive policy and that's the main caveat here. This is for a site with an embedded Shopify store, an Issuu embed, a Google Tour embed and Google Maps implementation (JS API). It also uses Font Awesome 5 from their CDN, jQuery from CDNJS, and some Google Fonts. It also has TextformatterVideoEmbed installed alongside its extended options module. default-src 'none'; script-src 'self' cdnjs.cloudflare.com *.google.com *.gstatic.com *.googleapis.com www.google-analytics.com www.googletagmanager.com e.issuu.com sdks.shopifycdn.com; style-src 'self' 'unsafe-inline' cdnjs.cloudflare.com *.googleapis.com use.fontawesome.com; img-src 'self' data: *.google.com *.googleapis.com *.gstatic.com *.ggpht.com www.google-analytics.com www.googletagmanager.com brand.nbcommunication.com *.shopify.com sdks.shopifycdn.com; connect-src 'self' www.google-analytics.com ocean-kinetics.myshopify.com; font-src 'self' fonts.gstatic.com use.fontawesome.com; object-src 'self'; media-src 'self' data:; manifest-src 'self'; frame-src www.google.com www.youtube.com www.youtube-nocookie.com player.vimeo.com w.soundcloud.com e.issuu.com; form-action 'self'; base-uri 'self' The Shopify embed script and Google Analytics initialisation have been moved into script files so there are no inline scripts at all. The script-src 'unsafe-inline' directive is an obstacle to getting that A+ on Observatory. Google Analytics is also a bit of an impediment to getting a top-drawer score, as its script doesn't use SRI. However, there is a reason for that as I understand it - it is a script that just loads other scripts so SRI implementation would just be token, it wouldn't actually be improving security. Still, it is possible to get A+ without dealing with this. It would be great to get some discussion going on CSP implementation - I'm only a few weeks in myself, so have much to learn! Cheers, Chris NB
    2 points
  8. Hey folks! Took a couple of late nights, but managed to turn this old gist of mine into a proper module. The name is SearchEngine, and currently it provides support for indexing page contents (into a hidden textarea field created automatically), and also includes a helper feature ("Finder") for querying said contents. No fancy features like stemming here yet, but something along those lines might be added later if it seems useful (and if I find a decent implementation to integrate). Though the API and selector engine make it really easy to create site search pages, I pretty much always end up duplicating the same features from site to site. Also – since it takes a bit of extra time – it's tempting to skip over some accessibility related things, and leave features like text highlighting out. Overall I think it makes sense to bundle all that into a module, which can then be reused over and over again ? Note: markup generation is not yet built into the module, which is why the examples below use PageArray::render() method to produce a simple list of results. This will be added later on, as a part of the same module or a separate Markup module. There's also no fancy JS API or anything like that (yet). This is an early release, so be kind – I got the find feature working last night (or perhaps this morning), and some final tweaks and updates were made just an hour ago ? GitHub repository: https://github.com/teppokoivula/SearchEngine Modules directory: https://modules.processwire.com/modules/search-engine/ Demo: https://wireframe-framework.com/search/ Usage Install SearchEngine module. Note: the module will automatically create an index field install time, so be sure to define a custom field (via site config) before installation if you don't want it to be called "search_index". You can change the field name later as well, but you'll have to update the "index_field" option in site config or module settings (in Admin) after renaming it. Add the site search index field to templates you want to make searchable. Use selectors to query values in site search index. Note: you can use any operator for your selectors, you will likely find the '=' and '%=' operators most useful here. You can read more about selector operators from ProcessWire's documentation. Options By default the module will create a search index field called 'search_index' and store values from Page fields title, headline, summary, and body to said index field when a page is saved. You can modify this behaviour (field name and/or indexed page fields) either via the Module config screen in the PocessWire Admin, or by defining $config->SearchEngine array in your site config file or other applicable location: $config->SearchEngine = [ 'index_field' => 'search_index', 'indexed_fields' => [ 'title', 'headline', 'summary', 'body', ], 'prefixes' => [ 'link' => 'link:', ], 'find_args' => [ 'limit' => 25, 'sort' => 'sort', 'operator' => '%=', 'query_param' => null, 'selector_extra' => '', ], ]; You can access the search index field just like any other ProcessWire field with selectors: if ($q = $sanitizer->selectorValue($input->get->q)) { $results = $pages->find('search_index%=' . $query_string . ', limit=25'); echo $results->render(); echo $results->renderPager(); } Alternatively you can delegate the find operation to the SearchEngine module: $query = $modules->get('SearchEngine')->find($input->get->q); echo $query->resultsString; // alias for $query->results->render() echo $query->pager; // alias for $query->results->renderPager() Requirements ProcessWire >= 3.0.112 PHP >= 7.1.0 Note: later versions of the module may require Composer, or alternatively some additional features may require installing via Composer. This is still under consideration – so far there's nothing here that would really depend on it, but advanced features like stemming most likely would. Installing It's the usual thing: download or clone the SearchEngine directory into your /site/modules/ directory and install via Admin. Alternatively you can install SearchEngine with Composer by executing composer require teppokoivula/search-engine in your site directory.
    1 point
  9. Example implementation: Info: So this is very much an ALPHA version of what I hope turns into a swiss armory knife payment module. Currently you are able to take payments with the payments form or cross browser payments button (Apple Pay etc). Please read implementation details here and consider contributing https://github.com/benbyford/PaymentStripeIntents TODO: Add subscription functionality Add customer functionality More testing and code clean up More usage examples anything else?
    1 point
  10. When your blog content is growing to some thousands of pages and you have comments scattered here and there, sometimes you start wondering if placing comments below each post is the best approach. Of course this totally depends on your project and contents. In my case I came to the conclusion I could provide a better user experience switching to a Forum-like solution, where discussions were organized by macro subjects and not below each individual post. Of course there are many products and services providing fully featured forum solutions, but getting only few comments per day I was reluctant to setup a dedicated forum and to deal with users' profiles. A nice thing of the comments system is that users do not need to create account and password, but simply use their email address. And here we are ... why not to setup a very basic Forum-like solution using PW Comments? But please do not get too excited… we will just use comments in a way that looks-like-a-forum. I think this solution makes sense if you have a limited number of comments per day spread on some topics. If you need to deal with high discussions traffic with several users ... do it with a fully featured forum solution !! To implement this Forum-like solution we will take benefits of (1) PW pages/templates flexibility (why you should be here otherwise…), (2) core module FieldtypeComments, (3) Uikit 3 Site/Blog Profile, (4) some of the tutorials I recently released about PW Comments (listed at the end). OK, let's start. Login into the Admin panel, select Setup, and then Fields. Let's create a checkbox field named "sticky" that we can use to keep some forum subjects on top of the communities. Now let's move to Setup, Template. We will create a new template with name "communities'. In addition to "title" field, we will add also "body" and "sidebarRight" fields (already available in the profile): Then we create a template, that we will use for individual communities, with name "community". In addition to "title" field, we will add "date", "body", "images", "comments" fields (already available in the profile) and the "sticky" field we have just created. If you wish, as I did, you can also associate some category fields. If you use Multi-Language is a good idea to use the field "date", as ,differently from createdStr, it will automatically return a locale-formatted date as you defined in the field. Now that we have created the templates, we need to customize them. Reopen the "communities" template. In the Family tab: allow the template to have children, allow only one page with this template, select "community" as only allowed template for CHILDREN. Then in the URLs tab: select allow page numbers. This will return a paginated list of communities. We reopen the "community" template. In the Family tab: do not allow the template to have children, allow to create new pages, select "communities" as only allowed template for PARENT. Now in the URLs tab: select allow page numbers. This will be necessary to create paginated comments. Let's go to the page-tree, under Home we will create a new page named "Community" using the templates "communities" (note the final s), this will be the page listing all the communities. Under the Community page, we will create the individual communities. Give the pages the name you wish and use the template "community" (note w/o the final s). It may be good creating one with the name "New Forum Requests" checking its "sticky" field so that it remains on top. As to implement this Forum-like solution, the comments pagination is a must, at this stage you will have to implement it as described in this tutorial: Now we are ready for coding (very little!) the php templates. We need to associate php files to the two templates we have created. In site/templates create two new php files communities.php and community.php. Open the template blog.php and copy its content to communities.php, then repeat the same operation copying the content of template blog-post.php to community.php. Great! We are few steps away from our Forum-like solution. Modify communities.php as follows: ... echo ukHeading1(page()->title, 'divider'); echo page()->body . '<br><hr>'; //>>>>> ADD THIS LINE //$posts = page()->children('limit=10'); //echo ukBlogPosts($posts); $posts = page()->children('sort=-sticky, sort=title, limit=10'); //>>>>> ADD THIS LINE echo ukBlogPosts($posts, ['moreText' => '', 'moreIcon' => '']); //>>>>> ADD THIS LINE ... Then modify community.php as follows: // blog post content echo ukBlogPost(page()); // comments if($page->hasField('comments')) { //>>>>> ADD THIS LINE $comments = page()->comments; // comment list if(count($comments)) { //echo ukHeading3("Comments", "icon=comments"); echo ukComments($comments->find("sort=-created"), ['paginate' => true]); //>>>>> ADD THIS LINE - READ PAGINATION TUTORIAL (NEEDED) echo '<hr>'; } // comment form $gdpr = __('I agree with the processing of my personal data. I have read and accept the Privacy Policy.'); //>>>>> ADD THIS LINE - READ GDPR TUTORIAL (OPTIONAL) echo ukHeading3(__('Join the discussion'), "icon=comment"); echo ukCommentForm($comments, ['labels' => ['gdpr' => $gdpr]]); //>>>>> ADD THIS LINE - READ GDPR TUTORIAL (OPTIONAL) } //>>>>> ADD THIS LINE Very good (hope so...). Now let's see the result. When browsing to the Community page we will get the list of the communities. In my example I decided to keep sticky the one where users can submit suggestions for new forum subjects. Selecting one of the communities we have created, we are going to see (1) a description of the community, (2) a paginated list of comments, (3) and the comment form at the bottom. Please note that in this picture pagination is kept with a limit of three items just to fit the image … It makes sense to have 10-15-20 comments per page depending on your preferences. And we are done, here it is. If you wish you can implement some additional features as described in the tutorials below. Not mandatory but probably makes good sense to enable the honeypot field as described in this tutorial as anti-spam measure: If you have the "pleasure" to deal with GDPR matters, here is described how to add a privacy checkbox to your comment form: and finally if you wish your Forum-like solution to be Multi-Language here it is what to do: I hope you enjoyed and wish you a nice week-end ! PS: it's a couple of tutorials I miss the emoj … hope they will come back in this forum soon !!
    1 point
  11. Wondering how to get that A+ rating on Mozilla Observatory? Now you can with ⭐⭐⭐MarkupContentSecurityPolicy⭐⭐⭐ Of course, MarkupContentSecurityPolicy does not guarantee an A+ rating, but it does help you implement a Content Security Policy for your ProcessWire website. Markup Content Security Policy Configure and implement a Content Security Policy for all front-end HTML pages. This module should only be used in production once it has been fully tested in development. Implementing a Content Security Policy on a site without testing will almost certainly break something! Overview Website Security Auditing Tools such as Mozilla Observatory will only return a high score if a Content Security Policy is implemented. It is therefore desirable to implement one. A common way of adding the Content-Security-Policy header would be to add it to the .htaccess file in the site's root directory. However, this means the policy would also cover the ProcessWire admin, and this limits the level of security policy you can add. The solution is to use the <meta> element to configure a policy, for example: <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">. MarkupContentSecurityPolicy places this element with your configured policy at the beginning of the <head> element on each HTML page of your site. There are some limitations to using the <meta> element: Not all directives are allowed. These include frame-ancestors, report-uri, and sandbox. The Content-Security-Policy-Report-Only header is not supported, so is not available for use by this module. Configuration To configure this module, go to Modules > Configure > MarkupContentSecurityPolicy. Directives The most commonly used directives are listed, with a field for each. The placeholder values given are examples, not suggestions, but they may provide a useful starting point. You will almost certainly need to use 'unsafe-inline' in the style-src directive as this is required by some modules (e.g. TextformatterVideoEmbed) or frameworks such as UIkit. Should you wish to add any other directives not listed, you can do so by adding them in Any other directives. Please refer to these links for more information on how to configure your policy: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy https://scotthelme.co.uk/content-security-policy-an-introduction/ https://developers.google.com/web/fundamentals/security/csp/ Violation Reporting Because the report-uri directive is not available, when Violation Reporting is enabled a script is added to the <head>which listens for a SecurityPolicyViolationEvent. This script is based on https://developer.mozilla.org/en-US/docs/Web/API/SecurityPolicyViolationEvent and POSTs the generated report to ?csp-violations=1. The module then logs the violation report to csp-violations. Unfortunately, most of the violations that are reported are false positives, and not actual attempts to violate the policy. These are most likely from browser extensions and are not easy to determine and filter. For this reason, there is no option for the report to be emailed when a policy is violated. Instead, you can specify an endpoint for the report to be sent to. This allows you to handle additional reporting in a way that meets your needs. For example, you may want to log all reports in a central location and send out an email once a day to an administrator notifying them of all sites with violations since the last email. Retrieving the Report To retrieve the report at your endpoint, the following can be used: $report = file_get_contents("php://input"); if(!empty($report)) { $report = json_decode($report, 1); if(isset($report) && is_array($report) && isset($report["documentURI"])) { // Do something } } Debug Mode When this is enabled, a range of information is logged to markup-content-security-policy. This is probably most useful when debugging a reporting endpoint. Additional .htaccess Rules To get an A+ score on Mozilla Observatory, besides using HTTPS and enabling the HSTS header, you can also place the following prior to ProcessWire's htaccess directives: Header set Content-Security-Policy "frame-ancestors 'self'" Header set Referrer-Policy "no-referrer-when-downgrade" Installation Download the zip file at Github or clone the repo into your site/modules directory. If you downloaded the zip file, extract it in your sites/modules directory. In your admin, go to Modules > Refresh, then Modules > New, then click on the Install button for this module. ProcessWire >= 3.0.123 is required to use this module.
    1 point
  12. Personally when I install a CMS I prefer to set it inside a subdirectory rather than the root folder. There are different reasons for this choice. For example you may wish to have different CMS installed in different subdirectories; or multiple copies or versions of Processwire in different subdirectories; keep separated directories for production and development; and so on. Sometimes this choice is taken also to obfuscate your CMS contents. In this forum I found some helpful hints, but I could not find a turn-key tutorial explaining the detailed steps for redirection and how to hide subfolder in urls' segments. It's pretty easy. Just let's do it step by step. We are going to create a new .htaccess file in the root folder (please note it is a new one, we will leave the .htaccess file in Processwire subfolder unchanged), where we will add the section described below. Just replace yoursite.com and yoursubfolder with ... your ones! # .htaccess in root directory # Redirect to Processwire subdirectory <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTP_HOST} ^(www.)?yoursite.com$ RewriteCond %{REQUEST_URI} !^/yoursubfolder/ RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ /yoursubfolder/$1 RewriteCond %{HTTP_HOST} ^(www.)?yoursite.com$ RewriteRule ^(/)?$ yoursubfolder/index.php [L] </IfModule> With this approach we can protect root files and subdirectories from redirection. Are we done ? Yes and no. After these modifications pages are redirected correctly, but in the browser link you will note that subfolder's name is still showing. This is not good, we want to hide it. We are just one step away. Let's open our site/config.php and add the following line at the end: /** * Set urls root to hide Processwire folder * This must be combined with .htaccess in root directory */ $config->urls->root = '/'; Do not forget to lock site/config.php after modifying it. If you prefer, instead of modifying config.php, you can place the above line in _init.php. And here it is! I hope this simple tutorial can be of help.
    1 point
  13. DEPRECATED! I'll release a commercial module "RockGrid" in 2023/2024. If you are interested please write me a PM ? ---------------------------------------- This module is (or will be) the successor of RockGrid. It uses http://tabulator.info/ as grid library that is completely open sourced (MIT, no commercial extensions) and as far as I've seen so far at least as powerful as ag-grid, which RockGrid is using. GOALS: Support various data sources (RockFinder, JS, PHP, SQL) Supported only in PW Backend + Uikit Easy setup of new fields DOCS: Currently the readme is terrible, sorry for that. But you get lots of examples after installing the module! From simple tabulators to more complex ones. INSTALLATION: Just install the main module. RockMarkup is a required dependency. Please also have a look at the RockMarkup examples before jumping into RockTabulator! STATUS: The module is early alpha and can be downloaded from github: https://github.com/BernhardBaumrock/RockTabulator It took me two weeks of rewriting RockMarkup/RockTabulator to get where I am now and I think I'm quite happy with the setup. There might be some breaking changes though in the near future. At the moment the main features that should be working properly are: Data sources: SQL, PHP, JS Sandbox for easy setup Multilang This means basically everything that is possible with Tabulator is also possible with RockTabulator. Though, there are no custom helpers/plugins yet. That's the next step and whoever is willing to help, please give me a shout ? If you have any questions or ideas please let me know ?
    1 point
  14. Please don't use this module any more. I think in the end it just adds more complexity (and dependencies) than benefits. See this tutorial how simple it is to create a custom runtime-only Inputfield: WHY? I've started building this module because the existing solutions by @kongondo and @kixe (https://modules.processwire.com/modules/fieldtype-runtime-markup/ and https://github.com/kixe/FieldtypeMarkup) did not exactly fit my needs. Actually this module is aimed to be a base module that can easily be extended by other modules. It takes care of the heavy lifting that has to be done when working with custom fieldtypes in ProcessWire (injecting scripts and styles, handling JS events, doing translations). See RockTabulator as an example. I'm quite sure more will follow (eg ChartJS)... WHAT? This module helps you injecting ANY php/html/js/css into any PW backend form (either on a page or in custom process modules). It also comes with a sandbox process module that helps you setup your fields and provides handy shortcuts that integrate with TracyDebugger and your IDE: WHERE ...to get it? At the moment the module is released as early alpha and available only on github: https://github.com/BernhardBaumrock/RockMarkup2 If you have any questions or ideas please let me know ? PS: This module shows how easy it is to extend this module for your very own needs. All you need to do is providing the module's info arrays and then overwrite any methods that you have to modify (eg the InputField's render() method): https://github.com/BernhardBaumrock/RockMarkupExtensionExample
    1 point
  15. When I installed Processwire for the first time, I noticed that Admin sessions were not lasting for a long time and it was very common to be logged out. Reading this forum I found useful information, but I could not solve this issue. I tried to modify site/config.php adding $config->sessionExpireSeconds with different values, but without success (for your awareness sessionExpireSeconds default value is 86400 seconds, ie 1 day .. so quite enough!). OK, I had no time to go deeper, so I kept doing what I had to do ... being happily logged out … time to time. Few weeks later while studying Session.php in wire/core/ I got the hint (please read the commented text) which helped me to understand how to solve it: if(ini_get('session.save_handler') == 'files') { if(ini_get('session.gc_probability') == 0) { // Some debian distros replace PHP's gc without fully implementing it, // which results in broken garbage collection if the save_path is set. // As a result, we avoid setting the save_path when this is detected. } else { ini_set("session.save_path", rtrim($this->config->paths->sessions, '/')); } } Uh! Yes, my VPS uses Debian 9 ! So I quickly did a test: echo ini_get('session.gc_probability'); echo ini_get('session.gc_divisor'); echo ini_get('session.gc_maxlifetime'); The session.gc_maxlifetime was rightly set to 86400, but session.gc_probability was 0 ! As you can easily get from Session.php routine, if probability is 0 bye bye PHP's session garbage collection and of course PW will not set the save_path. But why probability is set to 0 ?? This is due to the fact Debian/Ubuntu Linux overrides standard PHP’s session garbage collection by setting session.gc_probability to 0. As a consequence PHP’s standard session garbage collection will never run. Instead Debian/Ubuntu sets a specific Cron job for garbage collection with a duration of 1440 seconds, ie 24 minutes. This determines the max session duration. If you try to change session.gc_maxlifetime through $config->sessionExpireSeconds=86400 or ini_set('session.gc_maxlifetime', 86400) it will have no effect and sessions will be deleted at intervals of 24 minutes (or within 54 minutes). The Cron job garbage collection duration is defined at php.ini level, and runtime changes to session.gc_maxlifetime will not affect the Cron job timeout. Uh! Of course the simplest solution would be to change session.gc_maxlifetime in php.ini. However sometimes this is not possible (shared hosting). Another point to consider is that this change may affect multiple applications running on your server. Instead of modifying php.ini, personally I preferred to enable PHP's session garbage collection locally for Processwire only. Majority of the code is already written in PW Session.php, we have just to take benefit of it. Let's go step by step. Let's open site/config.php and do the following modifications (I choose to implement 14400 seconds session time, ie 4 hours): /** * Reduce inactivity timeout (original 86400 = 1 day) * How many seconds of inactivity before session expires */ $config->sessionExpireSeconds = 14400; /** * Enable Session Garbage Collection * Garbage Collection is disabled in Debian as default (probability set to zero) * Enable session garbage collection with a 1% chance of running on each session_start(); * Chance calculated as gc_probability/gc_divisor * Session path is defined inside wire/core/Session.php and points to site/assets/sessions * Thanks to this modification session now takes into account gc_maxlifetime set in config */ ini_set('session.gc_probability', 1); ini_set('session.gc_divisor', 100); Do not forget to lock site/config.php after modifying it. Here it is! We are done! After we have set gc_maxlifetime (through sessionExpireSeconds), enabled the garbage collection (gc_probability=1), and set its intervention probability (gc_divisor=100 ie 1% chance), we can rely on Session.php where the a session path (session_save_path()) is defined, and session is started (session_start()). In the development session of our browser let's look for our session cookie: if you go to site/assets/sessions you will find your corresponding session: Please note that during development phase, likely having little traffic, the number of sessions may grow and they will not be deleted. This depends from the 1% chance of deletion. If you want to test garbage collection is properly working or just to make sure sessions files are regularly cleaned during development, you can decrease gc_divisor to a lower rate(10=>10% ... 2=>50%). Do not forget to reestablish gc_divisor = 100 when moving to production. I hope some can find that tutorial useful in the future ... and may save several log outs!
    1 point
  16. This week I was catching up with client work after traveling last week, but some of that work overlapped with a focus on WireMail modules. As a result, this week I’ve released two new WireMail modules, and also have information in this blog post on how you can configure two existing WireMail modules (WireMailSmtp and WireMailPHPMailer) to use Gmail as the SMTP sender— https://processwire.com/blog/posts/wiremail-modules-and-gmail/
    1 point
  17. Chaining (piping usually means something different) in OOP doesn't mean returning $this. It means return the object, which you want to execute the next method call on. Where you get the object to return from is up to you. But I'm really wondering what the use case behind this is. Generally I'd tend to avoid classes knowing of each other and rather opting for composing their functionality with code outside of them.
    1 point
  18. @Robin S It works. Thanks a lot!
    1 point
  19. When you do this you load all the children of the of the root parent into memory as a PageArray, and then you get just one of those pages. It's more efficient to directly get the single page you need: $page->rootParent()->child('template=menu_submenus, include=hidden');
    1 point
  20. v0.1.4 released. This version adds support for some new features added in Repeater Matrix v0.0.5 - you can disable the settings for matrix types (so items cannot change their matrix type), and when the limit for a type is reached it becomes unavailable for selection in the type dropdown of other matrix items. The module now requires Repeater Matrix >= v0.0.5. If you are using Repeater Matrix v0.0.4 or older then there is no need to upgrade Restrict Repeater Matrix as the new features in v0.1.4 are only applicable to Repeater Matrix v0.0.5.
    1 point
  21. Here is another snippet that I use to get rid of unwanted table properties: // Remove unwanted attributes from tables CKEDITOR.on('dialogDefinition', function(ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; if (dialogName == 'table') { var info = dialogDefinition.getContents('info'); info.remove('txtWidth'); info.remove('txtHeight'); info.remove('txtBorder'); info.remove('txtCellPad'); info.remove('txtSummary'); info.remove('txtCellSpace'); info.remove('cmbAlign'); var advanced = dialogDefinition.getContents('advanced'); advanced.remove('advStyles'); advanced.remove('advId'); //Id attribute advanced.remove('advLangDir'); // writing direction advanced.get('advCSSClasses')['default'] = 'uk-table'; //set default class for table } Put this code inside your custom config.js Best regards
    1 point
  22. Here's a little text formatter module that redirects all external links found in a textarea (e.g. for hit counting) to a given URL. TextformatterExternalRedirect GitHub repo As TextFormatters do, this module modifies the contents of the textarea (HTML) at rendering time. It prepends the given URL to the link address for external links (http and https), optionally makes sure that the link is opened in a new window and allows you to add css classes to such links. All options can be configured in the module's configuration settings. Usage: Download the zip archive and extract it to site/modules, the rename the top folder to TextformatterExternalRedirect Go to the backend and select "Modules" -> "Refresh", then click "Install" for "External Link Redirect" Click "Settings" and configure the module Go into the field configuration for the field(s) you want this formatter to apply to and add it from the dropdown Done, now any external links in the configured fields will have the configured settings applied Feel free to leave feedback and post and questions you have here.
    1 point
  23. Hi @gebeer Just stumbled across this (I know my reply is a little late,) but it is totally possible to prevent your rest API endpoints from starting sessions by using the $config->sessionAllow; variable. If you define it to be a function that returns bool true or false, then it will be evaluated and the return value determines if the Session class constructor is allowed to start a new session. There's an example of it in the default wire/config.php file at line 245. Reproduced here... $config->sessionAllow = function($session) { // if there is a session cookie, a session is likely already in use so keep it going if($session->hasCookie()) return true; // if URL is an admin URL, allow session if(strpos($_SERVER['REQUEST_URI'], $session->config->urls->admin) === 0) return true; // otherwise disallow session return false; }; You just need to rewrite the function so it returns false for your API endpoint path. Make that change and add it to your site/config.php file, and I think that anything hitting your API endpoint directly will not have a session created for the connection. Hope that helps!
    1 point
  24. I've been in Minnesota all week for a family reunion and only a few minutes at the computer every day, so I don't have a core update or anything worthwhile to write about this week. But now I'm headed back home so will be back to a regular schedule next week. Hope that you have a great weekend!
    1 point
  25. A sneak preview of a new page builder concept that I'm close to completing. I'll write more about this in coming weeks, but this video demonstrates a lot of unique things going on:
    1 point
  26. Dashboard Tasks MD COLLABS, MaxDev Collaboration & Sharing Tool is a simple management task module; a plugin runs on Processwire CMF Framework. Available here at https://github.com/sjahrazad/DashboardTasks This module allows you to track interactions in your team through your Email application; furthermore, you can collaborate and share your task instantly from the web site. This module modified from Kongondo Dashboard Notes at https://processwire.com/talk/topic/20980-dashboard-notes/ Install 1. Place the module files in /site/modules/DashboardTasks/ 2. In your admin, click Modules > Check for new modules 3. There are two modules, the main module is DashboardTasks, and the other one DashboardDocs is an optional module in case you want the users to attach files to their task. 4. Install DashboardDocs to use both modules, or install DashboardTasks only without the document attachment. User Roles There are three roles for this module, task-user (co-worker), task-owner (task creator) and task-manager. Demo Users The installer created five demo users, task-manager, task-owner and three task-coworkers. For the first time log-in user, before using the module, a task manager role required to configure the Global Settings. user name: task-manager, password: task-manager, email: task-manager@yourdomainname user name: task-owner password: task-owner, email: task-owner@yourdomainname user name: task-coworker1 password: task-coworker1, email: task-coworker1@yourdomainname user name: task-coworker2 password: task-coworker2, email: task-coworker2@yourdomainname user name: task-coworker3 password: task-coworker3, email: task-coworker3@yourdomainname Change those emails with your test emails. Task Types There are three types available for the new task creation. First, is the Notes type. Use this type if you want to share your notes or collaborate the ideas to your members. The second type is the core of this module, a Task Scheduler type with start date and due date of the task. If you click the Required Job Acceptance from your task settings, then this task type will become an enquiry or job offer task type. The third type is the Reports or Private Notes type. Use this type for your private record or might be you share it to the selected members in your team. Usage Scenario You can use this module for numerous tasks management purposes, from just a shared idea up to enquiry request type. Email or SMS is the primary interface for the user in this module. The user can decide either respond to that email request and log in to the system or just read it in their mailbox. If there is no action required from the recipient, then it is not compulsory to log in to the system as well. Responses to the submitted task also will be sent out to the task creator email as well. For example, your company use a Call Centre service to capture your customer's enquiries, where the messages will be sent out to your contractors or service providers. In this case, your role here is the Task Manager, the call centre agent's role become the Task Owner, someone who created the task and your contractors or service providers are the Task Users. Once a contractor received the request and accepted the enquiry's offer, this contractor assigned as the job provider for that task. The other scenario usage, for example, you're a property manager who will process your tenant request for fault enquiry to your tradesperson group. Or, a lecturer who wants to arrange a research group with some students.. the list goes on ... CSS CSS Grid doesn't work in some old browsers. You need to adjust the CSS under Less folder if you want to use this module for old browsers. <details> tag is not working in IE or Edge browser. SMS If you want the output sent to SMS and Email, you have to add your SMS Provider API to the ProcessDashboardTasks.module. And add your mobile users' array to the SaveTask and SaveTaskReply function.
    1 point
×
×
  • Create New...