Custom concrete5 User Reports
concrete5's built-in user reporting functionality (found in the Dashboard -> Users and Groups) is pretty powerful. You can search by email address, group, user name, date created and any custom user attribute you've setup for inclusion in advanced search. You can sort by these custom attributes as well. Eventually, the users section of the dashboard will also get the saved search and column reordering functionality found in the file manager.
This works great for many administrators, but sometimes you need to be able to output these reports programmatically, in order to include them on the front-end of a website, within a custom administrative area, or just group information together in different ways. When that's the case, you need to get a little deeper and actually start writing some concrete5 code. Fortunately, we try and provide a nice API for doing just that.
Example
Let's say we wanted a custom report within a user's website for track visitors. This would be a custom single page that we'd lock down with permissions, so only administrators could get to it. You'd create a "custom_user_report.php" single page, so it could be found at http://www.your-website.com/index.php/custom_user_report. (Head over here if you need more information about creating and working with single pages.)
Our user report would need to list users who visited a particular page, group them by the most visits, and sort them by that visit amount in descending order. The search form for this could look like:
After choosing a particular page, and the start/end dates for our report, we'd submit the form, and get a list of users to that page over that period of time, along with the total number of visits.
The UserList Object
concrete5 provides a UserList class for filtering and sorting a result set of users. It's easy to get an array of UserInfo objects back from this class. In this example, we actually need more than just a class that filters and displays users. We need a class that can group and sort users based on how many times they've visited a particular page. That's why we define a custom UserVisitedPageList class that extends our UserList class, and put it in the models/ directory in the root of the website. It can then be loaded with
Loader::model('user_visited_page_list');
Here are the contents of the class:
page = $c;
$this->autoSortColumns[] = 'totalVisits';
}
public function setBaseQuery() {
$this->setQuery('select u.uID, count(ps.pstID) as totalVisits From Users u left join PageStatistics ps on ps.uID = u.uID');
$this->filter('cID', $this->page->getCollectionID());
$this->filter('ps.uID', 0, '>');
$this->groupBy('uID');
$this->sortBy('totalVisits', 'desc');
}
public function get($itemsToGet = 100, $offset = 0) {
$userInfos = array();
$this->createQuery();
$r = DatabaseItemList::get( $itemsToGet, intval($offset));
foreach($r as $row) {
$ui = UserInfo::getByID($row['uID']);
$ui->totalVisits = $row['totalVisits'];
$ui->timestamp = $row['timestamp'];
$ui->pageID = $row['cID'];
$userInfos[] = $ui;
}
return $userInfos;
}
}
Let's break this class down, component by component:
1. The class extends UserList, which in turn extends DatabaseItemList. Understanding the [DatabaseItemList][7] class will help this make quite a bit more sense.
2. The __construct() method, automatically run when the class is instantiated, sets the page that we're limiting results to as the protected $page variable, and adds to the $autoSortColumns array. This array is defined in the DatabaseItemList class, and determines which columns in the classes query can be sorted through a $_GET request.
3. The setBaseQuery command completely overrides the regular UserList setBaseQuery command. It groups and retrieves user visit on a page by page basis, filtering by logged in users, and logically grouping and sorting.
4. Finally, the get() function is overridden. This is a function that is automatically run by DatabaseItemList::getPage(), and run any time you want to run the query and get the results. Here, we rewrite the regular UserList class so that it still returns an array of UserInfo objects, but we set some additional object properties on these objects. This is key: these properties are not found in the regular UserInfo object. Instead, we set them for this class, and when we pass this array of result objects into the page, they'll be available and we can show them in our data grid.
## The Search Form
In the screenshot above, you can see that we don't run the search function when the page is first loaded. Instead, we force the user to submit the form. That form submits to the "search_users" action. Here is the search form in our custom single page:
$ph = Loader::helper('form/page_selector');
$dt = Loader::helper('form/date_time');
$form = Loader::helper('form');
?>