Jump to content

Preview/Discussion: RockDataTables


Recommended Posts

34 minutes ago, kathep said:

I would also buy a promodule, if it allowed the ability to have frontend pages.

Can you explain your exact usecase? I'm still not sure how useful the module could be on the frontend... It's really easy to implemenet a Datatable on your own on the frontend and then you have all the flexebility that custom code provides. Not sure if it makes sense to trade flexibility against ease of use...

This is an awesome example by @Macrura http://ohmspeaker.com/speaker-filter/?length=13&width=17

That would not be possible with my module. At least it would not be easier than without using it... So I'm not sure if my module could provide what you expect :)

Link to post
Share on other sites

ok... i get closer :) 

it's definitely the best to build the tables by sql queries. so the module will get an easy sql editor with live preview like shown here:

5a35744217587_2017-12-1620_08_05-ProgramManager.thumb.png.642f481f5bf4147468a8c1b28440704d.png

you see that the query loads 10.000 rows in around 500ms :) 

another huge benefit of writing sql queries is that you can group and sum your data easily, like in this example, where i build sums of "field3" grouped by month:

5a35743b40e9b_2017-12-1620_11_11-ProgramManager.thumb.png.a8b95b0aaf3231572fb8b7c669bfee8c.png

thanks to @adrian 's idea i zipped the json response of the sql query making it shrink from 2.7mb in the first example to 121kb :)

I'll add some helpers to make multilang queries easy and to query views (for kind of a modular setup)

  • Like 3
Link to post
Share on other sites

@bernhard - I love the flexibility of pure SQL as well, but for those less familiar with it, I was wondering if you have experimented with findMany() ?

The other option might be findIDs() - could you perhaps build an SQL query from the returned IDs to get the other requested fields?

Maybe one of these options would still be performant enough and a little more user friendly? Maybe the option for SQL or a selector so users have the choice depending on their needs an skills?

Just some random thoughts that might not be useful at all :)

  • Like 2
Link to post
Share on other sites
2 hours ago, adrian said:

Just some random thoughts that might not be useful at all :)

hi adrian, totally useful thoughts as always! i agree that for simple tables it would be easier to use the pw api. and i think that this will make sense in many situations, so i will build the module to support both options. it's definitely not necessary to go the sql route when all you want to do is listing 20 or so pages :)

i did some tests on findMany and findIDs but as i need to access fields of the page it's not enough to just load the ids. and then it gets slow again...

5a359f1931bdb_2017-12-1623_31_15-testfindMany.thumb.png.42c0fa93614928202d493d1f67d0bb73.png

Link to post
Share on other sites

Hey @bernhard - glad you're going to offer both. 

On the use of findIDs()  - I don't think I explained properly. My ideas was to take the returned IDs and build up a manual SQL query using those. Here is what it might look like this. It's rough, but it's running on 5000 pages and this returns the id and the title in 47ms and all that is needed is a simple selector - in this case: parent_id=xxxx

Does this suit your needs?

image.png.4a305d05c5601c1b0f6fa15de21831e7.png

  • Like 1
Link to post
Share on other sites

sorry, I'm still not sure if I get what you mean... if you use findIDs then you have to use an sql query afterwards. not sure why that should be better than having a query like this:

select ... from pages where parent_id=12593

Maybe you are talking about more complex examples where it could be easier to use a pw selector than having several WHERE ... and ... and ... in your SQL? Not sure how often those cases would rise up... but it's no problem anyhow because i plan to offer those two options of defining table data:

  $table->data(
    $pages->find('template=item'), [
      ['col_name1', 'label of this column', function() { return $this->page->title; }],
      ['col_name2', __('multilang label'), function() { return $this->page->parent->title; }],
      ['col_name3', 'another good label', function() { return implode('<br>', $this->page->repeater->each('path')); }],
    ]);

defining the table like this would really be simple and i really like this way so thank you for bringing this up again :)

  $table->data(
    'SELECT field1, field2, field3 FROM pages WHERE templates_id=44', [
      ['col_name1', 'label of this column', 'fiel1'],
      ['col_name2', __('multilang label'), 'fiel2'],
      ['col_name3', 'another good label', 'fiel3'],
    ]);

the other option will be any sql query, so you could also create this sql query like you did in your example combining it with a $pages->findIDs()

Link to post
Share on other sites
23 minutes ago, bernhard said:

sorry, I'm still not sure if I get what you mean... if you use findIDs then you have to use an sql query afterwards. not sure why that should be better than having a query like this:


select ... from pages where parent_id=12593

If the selector is that simple, then sure SQL is easy to write. 

Maybe I am missing something, but my assumption is that currently the user will have to write an SQL query themselves to get the pages they want. Is that correct? If it is, then my thought was that using findIDs would let you take a selector (generated from InputfieldSelector) and then pass those IDs into an SQL that is generated by your module. That way the user wouldn't have to code anything - all GUI via the InputfieldSelector (what is used by Lister/ListerPro etc). 

Does that make sense, or am I still just confused about how this module works?

PS - in my example above there is not actually any reason to even get the id from the pages table because you already have that (obviously). So you probably just need to get from the db tables for each of the fields that you need to present.

Link to post
Share on other sites

@adrian I'll explain more details tomorrow just a quick question: if you only define data by a selector, how would you define which columns to show (which fields, which columns headers)?

But you brought up yet another idea in my head :)

  • Like 1
Link to post
Share on other sites
Just now, bernhard said:

if you only define data by a selector, how would you define which columns to show (which fields, which columns headers)?

 

The same way Lister does - with an ASM field that lets them select fields and sort the order they will be displayed in.

image.png.4877f6311cc6a01c775bc40427d1ebe2.png

  • Like 2
Link to post
Share on other sites

Ok, thanks. I get your point now. Actually I did not think of having a GUI for my tables at all but it seems it could make a lot of sense. Taking this idea further it could also be used as a Lister replacement... I have to think about that and how to put all those usecases under one hood.

 

Link to post
Share on other sites

Hey @bernhard - I just quickly hacked together a new $pages->findArray() method that you can use like this:

$pages->findArray("parent=12593", array('title', 'integer'), 'object');

It takes 3 arguments:

  • selector
  • array of fields to return
  • return an array or object

It needs to be cleaned up (eg properly bind values etc), but as you can see in the screenshot below that it returns an array of the requested fields for 5000 pages in 66ms. I wonder whether there is justification for something like this in the core? It seems like a pretty efficient way to get just the fields you're interested in without the overhead of page objects.

You can obviously then do:

Object

foreach($pages->findArray("parent=12593", array('title', 'integer'), 'object') as $p) {
    d($p->title.':'.$p->integer);
}

OR

Array

foreach($pages->findArray("parent=12593", array('title', 'integer'), 'array') as $p) {
    d($p['title'].':'.$p['integer']);
}

This certainly isn't specific to your module - I am definitely going to be making use of this in the future for my sites when I need data for tables / charts, etc.

Here is the hook code:

Spoiler

$this->addHook("Pages::findArray", function($event) {

    $selector = $event->arguments(0);
    $fields = $event->arguments(1);
    $type = $event->arguments(2) == 'array' ? PDO::FETCH_ASSOC : PDO::FETCH_OBJ;

    $fieldsStrArr = array();
    $fieldsJoins = '';
    foreach($fields as $f) {
        $fieldsStrArr[] = "field_$f.data AS `$f`";
        $fieldsJoins .= "LEFT JOIN field_$f ON pages.id = field_$f.pages_id\n";
    }
    $fieldsStr = implode(', ', $fieldsStrArr);

    $items = $this->pages->findIDs($selector);
    $sql = "
    SELECT pages.id, $fieldsStr FROM pages
    $fieldsJoins
    WHERE id IN (" . implode(',',$items) . ")
    ";
    $results = $this->database->query($sql);
    $event->return = $results->fetchAll($type);

});

 

 

BTW - the "title" field in the results below is correct to be a number - these were 5000 API generated pages with numeric titles.

image.png.1023381203f8f8bb1eead4f733b5fa29.png

 

  • Like 4
Link to post
Share on other sites

hi adrian

On 17.12.2017 at 4:27 PM, adrian said:

I just quickly hacked together a new $pages->findArray() method that you can use like this

This is a GREAT idea :) Very helpful indeed! I think that should definitely go into the core.

I see you are using joins for your example. As mentioned here I think it's better to use subqueries in our case because it makes the query easier (to read, write and construct) and it makes it easier to query other fieldtypes (like repeaters or the like). here is my modified version of your hook. I think that could really be helpful in many situations. I would suggest using $pages->findArray() and $pages->findObject() as different methods. Easier to remember and more self-explaining :)

$this->addHook("Pages::findObject", function($event) {
  $event->return = $this->pages->findArray($event->arguments(0), $event->arguments(1), $event->arguments(2), true);
});
$this->addHook("Pages::findArray", function($event) {
  $selector = $event->arguments(0);
  $fields = $event->arguments(1);
  $fields_pages = $event->arguments(2) ?: [];
  $type = $event->arguments(3) ? \PDO::FETCH_OBJ : \PDO::FETCH_ASSOC;

  // todo: check for empty pages find operation and early exit
  // $this->pages->findIDs($selector)
  // https://processwire.com/talk/topic/18558-mysql-database-to-processwire-pages-opinions/?do=findComment&comment=162328

  // build sql string
  $sql = "SELECT\n  p.";

  // add fields of pages table
  $fields_pages[] = 'id'; // make sure we return the page id
  $fields_pages = array_unique($fields_pages);
  $sql .= implode(",\n  p.", $fields_pages);

  foreach($fields as $f) {
    $field = $this->fields->get($f);
    if(!$field) continue;
    $fieldtype = $field->type;
    
    // fielddata is always stored in the "data" column of the field's table
    // multilang fields have several data columns identified by the language id
    // we use a variable to query the current user's language, eg data1234
    $data = "data";

    switch(true) {
      // if it is a multilang field we append the language id to query the correct column
      case $fieldtype instanceof FieldtypeTextLanguage:
      case $fieldtype instanceof FieldtypeTextareaLanguage:
        if($this->user->language->name != 'default') $data .= $this->user->language->id;
        // no break here intended!

      // build sql query
      case $fieldtype instanceof FieldtypeText:
        $sql .= ",\n  (SELECT $data FROM field_$f WHERE pages_id = p.id) AS $f";
        break;
      case $fieldtype instanceof FieldtypePage:
        $sql .= ",\n  (SELECT GROUP_CONCAT($data SEPARATOR ',') FROM field_$f WHERE pages_id = p.id) AS $f";
        break;
      default:
        $sql .= ",\n  '$fieldtype not supported' AS $f";
    }
  }

  $sql .= "\nFROM\n  pages AS p";
  $sql .= "\nWHERE\n  p.id IN (" . implode(",", $this->pages->findIDs($selector)) . ")";

  $results = $this->database->query($sql);
  $event->return = $results->fetchAll($type);
});

This is really easy to extend for all other fieldtypes (and maybe we could also implement a feature to add a custom query as returned field?). I also added support for multilanguage fields :) (see field1 in the example):

5a36a63ad21ac_2017-12-1718_13_07-Console.png.b24f0cc5071105932a5f2d56c7744ec2.png

btw: querying the database via a pages->findIDs query is a very good idea because it already takes care of page status and access control :) does anybody know any limitations for the length of the query (querying 10.000 pages means having 10.000 ids in the sql statement!).

edit: I'm querying 10.000 pages again in the example :)

and this was the resulting query:

SELECT
  p.id,
  (SELECT data FROM field_title WHERE pages_id = p.id) AS title,
  (SELECT data11041 FROM field_field1 WHERE pages_id = p.id) AS field1,
  (SELECT GROUP_CONCAT(data SEPARATOR ',') FROM field_test_page WHERE pages_id = p.id) AS test_page,
  'FieldtypeRepeater not supported' AS test_repeater
FROM
  pages AS p
WHERE
  p.id IN (1016,1017,1018,1019,1020,1021...)

PS: The "FieldtypeRepeater not supported" part is just for demonstration. I think a repeater should return ids just like the "test_page" field in my example does. Maybe we should use a pipe as separator so that you can instantly use the returned value as a $pages->find() selector?

  • Like 1
Link to post
Share on other sites
28 minutes ago, bernhard said:

does anybody know any limitations for the length of the query (querying 10.000 pages means having 10.000 ids in the sql statement!)

The only limit is what is hit by the "max_allowed_packet" setting.

As for the performance, I think we're probably OK because "id" is indexed. If it wasn't, we'd be in trouble. Might need to do an extreme test to really know. I suppose an SQL query completely from scratch would be more efficient, but using findIDs certainly simplifies lots of things as you noted, and especially if the selector gets complex - groups, subfield values etc.

  • Like 1
Link to post
Share on other sites

updated the hook in my previous post to support queries of the pages table itself (like templates_id, created_users_id and so on).

5a36bea10e034_2017-12-1719_58_27-newpages-_findObjects()method.png.5a7eb17ff715359a9bb3da0cb149c658.png

edit: this solution is not the best, imho. It would be better to have one array with all fields and then split this array automatically (like this: if field part of pages table query this field directly else join field_xxx )

  • Like 1
Link to post
Share on other sites

Great work @bernhard !

The only suggestion that comes on top of my head is maybe some built-in default charts? 

Also, you could look at the features list of other commercial data table products out in the market, (not just for PHP/JS but other areas like .NET etc) and see if there's anything interesting features you would want to incorporate into yours.

Companies I can think of are : Infragistics, Telerik, DevExpress, Syncfusion etc.

https://js.devexpress.com/

https://www.telerik.com/kendo-ui/grid

https://www.infragistics.com/products/ignite-ui/grids-and-lists/data-grid

https://www.syncfusion.com/products/javascript/ejgrid

As for whether it becomes free or paid version, I'd certainly be happy to pay for it if I had a use case for it and it was within the budget constraints. It certainly could win clients over if you could show them your CMS has reporting capabilities that others may or may not have.

  • Like 1
Link to post
Share on other sites

@bernhard - I haven't looked into the best way to incorporate it yet, but before I forget, I think it would be great to be able to add mysql functions to these new $pages methods. For example, it would be great to be able to directly get the YEAR from a datetime field using YEAR(datetime) AS year. Lots of other options of course, but that should give you an idea of what I am thinking.

  • Like 1
Link to post
Share on other sites

hi francis,

thanks for your suggestions!

7 hours ago, FrancisChung said:

The only suggestion that comes on top of my head is maybe some built-in default charts? 

You mean something like "make a piechart of column x and y"? I'm already thinking of that but don't know if it's really worth the effort when you can write nice charts with some lines of chartjs code... maybe a simple example/tutorial would be of more help...

7 hours ago, FrancisChung said:

Also, you could look at the features list of other commercial data table products out in the market

Thanks, I already knew kendo and like the style very much. Though I'm sure I cannot provide all those features. But I already built my module to be extendable via plugins so everybody will be welcome to contribute. The filter plugin will for sure be one of the most important ones and your examples are nice starting points!

5a378b8c1f042_2017-12-1810_31_52-https___www.igniteui.com_grid__ga2.207905682.1428508297_1513589436-544114306_15.png.8dbac094b9032082736f3faff737384a.png and https://demos.telerik.com/kendo-ui/grid/filter-menu-customization

4 hours ago, adrian said:

@bernhard - I haven't looked into the best way to incorporate it yet, but before I forget, I think it would be great to be able to add mysql functions to these new $pages methods. For example, it would be great to be able to directly get the YEAR from a datetime field using YEAR(datetime) AS year. Lots of other options of course, but that should give you an idea of what I am thinking.

Yeah, I also thought of that option. Would be quite easy. Could be used like this:

$pages->findObjects('template=basic-page', [
  'id',
  'templates_id',
  'mydate' => 'SELECT YEAR(data) FROM field_mydate WHERE pages_id = p.id',
  'multilang_example' => 'SELECT GROUP_CONCAT(#data# separator '|') FROM field_test_page WHERE pages_id = p.id'
]);

2 things here to mention:

  1. using subqueries makes this very easy to implement
  2. multilang would be possible replacing #data# by data1234 (data + langid)

I really like where this goes so far :)

  • Like 1
Link to post
Share on other sites
1 minute ago, bernhard said:

You mean something like "make a piechart of column x and y"? I'm already thinking of that but don't know if it's really worth the effort when you can write nice charts with some lines of chartjs code... maybe a simple example/tutorial would be of more help...

Hi. Just so you know, I have been working (on the side, slow pace) on a free data visualisation module based on d3. It might or might not include a data cleanup tool (like Pivot Tables in Excel). It's a hobby project. Will this conflict with your plans for your datatables module?

  • Like 2
Link to post
Share on other sites

not at all, sounds like this could be a great companion! at the moment I think I will also release the datatables module as open source. I've never worked with d3 but the company I'm writing my thesis at does some crazy stuff with it :) maybe a dedicated "d3 preview" thread would be nice? I really enjoy the discussion and the input here :)

  • Like 3
Link to post
Share on other sites
3 hours ago, bernhard said:

'mydate' => 'SELECT YEAR(data) FROM field_mydate WHERE pages_id = p.id',

Could this be abstracted to something like this:

'YEAR(mydate)' => 'year'

This would result in a "year" key in the returned array/object that returns the YEAR from the mydate field.

Link to post
Share on other sites
29 minutes ago, adrian said:

Could this be abstracted to something like this:

Don't think that would be a good idea because if you want to query repeaters or the like you can concat strings from subqueries and so on with my route.

Maybe we could add a check wheter the statement begins with SELECT or not. If it begins with SELECT it takes the whole query, if not it creates the query for you...

'myfield' => 'YEAR(data)'
--> (SELECT YEAR(data) FROM field_myfield WHERE pages_id = p.id) AS myfield

'myfield' => 'SELECT ... FROM ... WHERE ...'
--> (SELECT ... FROM ... WHERE ...) AS myfield

Not sure how often one would need this short syntax? But also no problem to implement...

  • Like 1
Link to post
Share on other sites
5 minutes ago, bernhard said:

Not sure how often one would need this short syntax?

But you know SQL - I am trying to think of the devs that don't and are used to the PW API. Remember you not only need to know SQL, but you also need a good understanding of the PW database tables structure with the names of the tables and that content is stored in the "data" field.

To this end, I would suggest:

'myfield' => 'YEAR()'

I'd love to see that converted to:

SELECT YEAR(data) AS year FROM field_myfield WHERE pages_id = p.id AS myfield

so take the name of the function and lowercase it and return it as "year" for the key/property in the array/object.

I think this could be really useful.

  • Like 1
Link to post
Share on other sites

I get your point but I don't think this level of reduction is good. For example if you looked for a way to format your date you would google "mysql date format" and see this example:

SELECT DATE_FORMAT(BirthDate, "%W %M %e %Y") FROM Employees;

I think it's a lot easier to communicate in the docs that you have to use the "data" column in your query than changing the syntax. So your query would be

DATE_FORMAT(data, '%W %M %e %Y')

I'm still hesitant about how often that would be needed... But if needed often this could be a handy timesaver for sure.

Link to post
Share on other sites
3 minutes ago, bernhard said:

I'm still hesitant about how often that would be needed

I think it would be used quite often in a datatables type application, but I also am not sure about just how far to go in terms of getting away from SQL which is already very effective. I guess my main goal is to remove the need to understand the PW table/field structure/relationships, but you have put a lot more thought into this than I, so feel free to ignore my ramblings :)

  • Like 1
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By monollonom
      (once again I was surprised to see a work of mine pop up in the newsletter, this time without even listing the module on PW modules website 😅. Thx @teppo !)
      Github: https://github.com/romaincazier/FieldtypeQRCode
      Modules directory: https://processwire.com/modules/fieldtype-qrcode/
      This is a simple module I made so a client could quickly grab a QR Code of the page's url in the admin.
      There's not much to it for now, but if need be you can output anything using a hook:
      $wire->addHookAfter("FieldtypeQRCode::getQRText", function($event) { $event->return = "Your custom text"; }) You can also output the QR code on your front-end by calling the field:
      echo $page->qr_code_field; The module uses the PHP library QR Code Generator by Kazuhiko Arase. When looking for a way to generate a QR Code in PW I came across @ryan's integration in his TFA module. I'm not very familiar with fieldtype/inputfield module development so I blindly followed @bernhard (great) tutorial and his BaseFieldtypeRuntime. At some point I'll take a deeper look to make a module on my own.
      Some ideas for improvements :
      add the ability to choose what to ouput : page's url / editUrl / file(s) / image(s) / ... allow to output multiple QR codes ?
    • By Chris Bennett
      https://github.com/chrisbennett-Bene/AdminThemeTweaker
      Inspired by @bernhard's excellent work on the new customisable LESS CSS getting rolled into the core soon, I thought I would offer up the module for beta testing, if it is of interest to anyone.

      It takes a different approach to admin styling, basically using the Cascade part of CSS to over-ride default UiKit values.
      Values are stored in ModuleConfig Module creates a separate AdminThemeTweaker Folder at root, so it can link to AdminThemeTweaker.php as CSS AdminThemeTweaker.php reads the module values, constructs the CSS variables then includes the CSS framework Can be switched on and off with a click. Uninstall removes everything, thanks to bernhard's wonderful remove dir & contents function.
      It won't touch your core. It won't care if stuff is upgraded. You won't need to compile anything and you don't need to touch CSS unless you want to.

      It won't do much at all apart from read some values from your module config, work out the right CSS variables to use (auto contrast based on selected backgrounds) and throw it on your screen.
      You can configure a lot of stuff, leave it as it comes (dark and curvy), change two main colors (background and content background) or delve deep to configure custom margins, height of mastheads, and all manner of silly stuff I never use.

      Have been developing it for somewhere around 2 years now. It has been (and will continue to be) constantly tweaked over that time, as I click on something and find something else to do.
      That said, it is pretty solid and has been in constant use as my sole Admin styling option for all of those 2 years.

      If nothing else, it would be great if it can provide any assistance to @bernhard or other contributor's who may be looking to solve some of the quirkier UiKit behavior.
      Has (in my opinion) more robust and predictable handling of hidden Inputfields, data-colwidths and showIf wrappers.
      I am very keen to help out with that stuff in any way I can, though LESS (and any css frameworks/tools basically) are not my go.
      I love CSS variables and banging-rocks-together, no-dependency CSS you can write with notepad.



       

    • By opalepatrick
      I see old posts saying that repeaters are not the way to go in Custom Process Modules. If that is the case, when using forms (as I am trying to do) how would one tackle things like repeat contact fields where there can be multiple requirements for contact details with different parameters? (Like point of contact, director, etc) or even telephone numbers that have different uses?
      Just for background I am creating a process module that allows me to create types of financial applications in the admin area (no need to publish any of this, pure admin) that require a lot of personal or company information.
      Maybe I am thinking about this incorrectly?
    • By HMCB
      I ran across a reference to IftRunner module. The post was 6 years ago. I cant find it in available modules. Has it been pulled?
    • By tcnet
      PageViewStatistic for ProcessWire is a module to log page visits of the CMS. The records including some basic information like IP-address, browser, operating system, requested page and originate page. Please note that this module doesn't claim to be the best or most accurate.
      Advantages
      One of the biggest advantage is that this module doesn't require any external service like Google Analytics or similar. You don't have to modify your templates either. There is also no Javascript or image required.
      Disadvantages
      There is only one disadvantage. This module doesn't record visits if the browser loads the page from its browser cache. To prevent the browser from loading the page from its cache, add the following meta tags to the header of your page:
      <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" /> How to use
      The records can be accessed via the Setup-menu of the CMS backend. The first dropdown control changes the view mode.

      Detailed records
      View mode "Detailed records" shows all visits of the selected day individually with IP-address, browser, operating system, requested page and originate page. Click the update button to see new added records.

      Cached visitor records
      View modes other than "Detailed records" are cached visitor counts which will be collected on a daily basis from the detailed records. This procedure ensures a faster display even with a large number of data records. Another advantage is that the detailed records can be deleted while the cache remains. The cache can be updated manually or automatically in a specified time period. Multiple visits from the same IP address on the same day are counted as a single visitor.

      Upgrade from older versions
      Cached visitor counts is new in version 1.0.8. If you just upgraded from an older version you might expire a delay or even an error 500 if you display cached visitor counts. The reason for this is that the cache has to be created from the records. This can take longer if your database contains many records. Sometimes it might hit the maximally execution time. Don't worry about that and keep reloading the page until the cache is completely created.
      Special Feature
      PageViewStatistic for ProcessWire can record the time a visitor viewed the page. This feature is deactivated by default. To activate open the module configuration page and activate "Record view time". If activated you will find a new column "S." in the records which means the time of view in seconds. With every page request, a Javascript code is inserted directly after the <body> tag. Every time the visitor switches to another tab or closes the tab, this script reports the number of seconds the tab was visible. The initial page request is recorded only as a hyphen (-).

      Settings
      You can access the module settings by clicking the Configuration button at the bottom of the records page. The settings page is also available in the menu: Modules->Configure->ProcessPageViewStat.
      IP2Location
      This module uses the IP2Location database from: http://www.ip2location.com. This database is required to obtain the country from the IP address. IP2Location updates this database at the begin of every month. The settings of ProcessPageViewStat offers the ability to automatically download the database monthly. Please note, that automatically download will not work if your webspace doesn't allow allow_url_fopen.
      Dragscroll
      This module uses DragScroll. A JavaScript available from: http://github.com/asvd/dragscroll. Dragscroll adds the ability in view mode "Day" to drag the records horizontally with the mouse pointer.
      parseUserAgentStringClass
      This module uses the PHP class parseUserAgentStringClass available from: http://www.toms-world.org/blog/parseuseragentstring/. This class is required to filter out the browser type and operating system from the server request.
×
×
  • Create New...