3 * A class to handle reading, writing, viewing, editing and validating
7 * @author Andrew McMillan <andrew@mcmillan.net.nz>
8 * @copyright Catalyst IT Ltd, Morphoss Ltd <http://www.morphoss.com/>
9 * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
11 require_once("AWLUtilities.php");
14 * We need to access some session information.
16 require_once("Session.php");
19 * We use the DataEntry class for data display and updating
21 require_once("DataEntry.php");
24 * We use the DataUpdate class and inherit from DBRecord
26 require_once("DataUpdate.php");
29 * A class to handle reading, writing, viewing, editing and validating
34 class User
extends DBRecord
{
39 * A unique user number that is auto assigned on creation and invariant thereafter
45 * Something to prefix all field names with before rendering them.
53 * The constructor initialises a new record, potentially reading it from the database.
54 * @param int $id The user_no, or 0 if we are creating a new one
55 * @param string $prefix The prefix for entry fields
57 function User( $id , $prefix = "") {
60 // Call the parent constructor
63 $this->prefix
= $prefix;
71 $keys['user_no'] = $id;
75 // Initialise the record, possibly from the file.
76 $this->Initialise('usr',$keys);
80 $this->EditMode
= ( (isset($_GET['edit']) && $_GET['edit'] && $this->AllowedTo($this->WriteType
))
81 ||
(0 == $this->user_no
&& $this->AllowedTo("insert") ) );
83 if ( $this->user_no
== 0 ) {
84 dbg_error_log("User", "Initialising new user values");
86 // Initialise to standard default values
94 * Can the user do this?
95 * @param string $whatever What the user wants to do
96 * @return boolean Whether they are allowed to.
98 function AllowedTo ( $whatever )
105 * First we globally short-circuit the 'admin can do anything'
107 if ( $session->AllowedTo("Admin") ) {
109 dbg_error_log("User",":AllowedTo: Admin is always allowed to %s", $whatever );
113 switch( strtolower($whatever) ) {
116 $rc = ( $this->user_no
> 0 && $session->user_no
== $this->user_no
);
120 $rc = ( $this->user_no
> 0 && $session->user_no
== $this->user_no
);
123 case 'changepassword':
124 $rc = ( ($this->user_no
> 0 && $session->user_no
== $this->user_no
)
125 ||
("insert" == $this->WriteType
) );
128 case 'changeusername': // Administrator only
129 case 'changeactive': // Administrator only
139 $rc = ( isset($session->roles
[$whatever]) && $session->roles
[$whatever] );
141 dbg_error_log("User",":AllowedTo: %s is%s allowed to %s", (isset($this->username
)?
$this->username
:null), ($rc?
"":" not"), $whatever );
147 * Get the group memberships for the user
149 function GetRoles () {
150 $this->roles
= array();
151 $qry = new AwlQuery( 'SELECT role_name FROM role_member JOIN roles USING (role_no) WHERE user_no = ? ', $this->user_no
);
152 if ( $qry->Exec("User") && $qry->rows() > 0 ) {
153 while( $role = $qry->Fetch() ) {
154 $this->roles
[$role->role_name
] = 't';
161 * Render the form / viewer as HTML to show the user
162 * @return string An HTML fragment to display in the page.
166 dbg_error_log("User", ":Render: type=$this->WriteType, edit_mode=$this->EditMode" );
168 $ef = new EntryForm( $REQUEST_URI, $this->Values
, $this->EditMode
);
169 $ef->NoHelp(); // Prefer this style, for the moment
171 if ( $ef->EditMode
) {
172 $html .= $ef->StartForm( array("autocomplete" => "off" ) );
173 if ( $this->user_no
> 0 ) $html .= $ef->HiddenField( "user_no", $this->user_no
);
176 $html .= "<table width=\"100%\" class=\"data\" cellspacing=\"0\" cellpadding=\"0\">\n";
178 $html .= $this->RenderFields($ef);
179 $html .= $this->RenderRoles($ef);
181 $html .= "</table>\n";
182 if ( $ef->EditMode
) {
183 $html .= '<div id="footer">';
184 $html .= $ef->SubmitButton( "submit", (("insert" == $this->WriteType
) ?
translate("Create") : translate("Update")) );
186 $html .= $ef->EndForm();
193 * Render the core details to show to the user
194 * @param object $ef The entry form.
195 * @param string $title The title to display above the entry fields.
196 * @return string An HTML fragment to display in the page.
198 function RenderFields($ef , $title = null ) {
201 if ( $title == null ) $title = i18n("User Details");
202 $html = ( $title == "" ?
"" : $ef->BreakLine(translate($title)) );
204 if ( $this->AllowedTo('ChangeUsername') ) {
205 $html .= $ef->DataEntryLine( translate("User Name"), "%s", "text", "username",
206 array( "size" => 20, "title" => translate("The name this user can log into the system with.")), $this->prefix
);
209 $html .= $ef->DataEntryLine( translate("User Name"), $this->Get('username') );
211 if ( $ef->EditMode
&& $this->AllowedTo('ChangePassword') ) {
212 $this->Set('new_password','******');
213 unset($_POST['new_password']);
214 $html .= $ef->DataEntryLine( translate("New Password"), "%s", "password", "new_password",
215 array( "size" => 20, "title" => translate("The user's password for logging in.")), $this->prefix
);
216 $this->Set('confirm_password', '******');
217 unset($_POST['confirm_password']);
218 $html .= $ef->DataEntryLine( translate("Confirm"), "%s", "password", "confirm_password",
219 array( "size" => 20, "title" => translate("Confirm the new password.")), $this->prefix
);
222 $html .= $ef->DataEntryLine( translate("Full Name"), "%s", "text", "fullname",
223 array( "size" => 50, "title" => translate("The user's full name.")), $this->prefix
);
225 $html .= $ef->DataEntryLine( translate("EMail"), "%s", "text", "email",
226 array( "size" => 50, "title" => translate("The user's e-mail address.")), $this->prefix
);
228 if ( $this->AllowedTo('ChangeActive') ) {
229 $html .= $ef->DataEntryLine( translate("Active"), ($this->Get('active') == 't'?
translate('Yes') : translate('No')), "checkbox", "active",
230 array( "_label" => translate("User is active"),
231 "title" => translate("Is this user active?")), $this->prefix
);
234 $html .= $ef->DataEntryLine( translate("Active"), ($this->Get('active') == 't'?
translate('Yes') : translate('No')) );
237 $html .= $ef->DataEntryLine( translate("Date Style"), ($this->Get('date_format_type') == 'E' ?
'European' : ($this->Get('date_format_type') == 'U' ?
'US of A' : 'ISO 8861')),
238 "select", "date_format_type",
239 array( "title" => translate("The style of dates used for this person."),
240 "_E" => translate("European (d/m/y)"), "_U" => translate("United States of America (m/d/y)"), "_I" => translate("ISO Format (YYYY-MM-DD)") ),
243 if ( isset($c->default_locale
) ) {
244 if ( $this->Get('locale') == '' ) {
245 $this->Set('locale',$c->default_locale
);
247 $html .= $ef->DataEntryLine( translate("Language"), "%s", "lookup", "locale",
248 array( "title" => translate("The preferred language for this person."),
249 "_sql" => "SELECT locale, locale_name_locale FROM supported_locales ORDER BY locale ASC;" ),
253 $html .= $ef->DataEntryLine( translate("EMail OK"), $session->FormattedDate($this->Get('email_ok'),'timestamp'), "timestamp", "email_ok",
254 array( "title" => translate("When the user's e-mail account was validated.")), $this->prefix
);
256 $html .= $ef->DataEntryLine( translate("Joined"), $session->FormattedDate($this->Get('joined'),'timestamp') );
257 $html .= $ef->DataEntryLine( translate("Updated"), $session->FormattedDate($this->Get('updated'),'timestamp') );
258 $html .= $ef->DataEntryLine( translate("Last used"), $session->FormattedDate($this->Get('last_used'),'timestamp') );
265 * Render the user's administrative roles
267 * @return string The string of html to be output
269 function RenderRoles( $ef, $title = null ) {
273 if ( $title == null ) $title = i18n("User Roles");
274 $html = ( $title == "" ?
"" : $ef->BreakLine(translate($title)) );
276 $html .= '<tr><th class="prompt">'.translate("User Roles").'</th><td class="entry">';
277 if ( $ef->EditMode
) {
278 $sql = "SELECT role_name FROM roles ";
279 if ( ! ($session->AllowedTo('Admin') ) ) {
280 $sql .= "NATURAL JOIN role_member WHERE user_no=$session->user_no ";
282 $sql .= "ORDER BY roles.role_no";
284 $ef->record
->roles
= array();
286 // Select the records
287 $q = new AwlQuery($sql);
288 if ( $q && $q->Exec("User") && $q->rows() ) {
290 while( $row = $q->Fetch() ) {
291 @dbg_error_log
("User", ":RenderRoles: Is a member of '%s': %s", $row->role_name
, $this->roles
[$row->role_name
] );
292 $ef->record
->roles
[$row->role_name
] = ( isset($this->roles
[$row->role_name
]) ?
$this->roles
[$row->role_name
] : 'f');
293 $html .= $ef->DataEntryField( "", "checkbox", "roles[$row->role_name]",
294 array("title" => translate("Does the user have the right to perform this role?"),
295 "_label" => translate($row->role_name
) ) );
301 foreach( $this->roles
AS $k => $v ) {
302 if ( $i++
> 0 ) $html .= ", ";
306 $html .= '</td></tr>'."\n";
312 * Validate the information the user submitted
313 * @return boolean Whether the form data validated OK.
315 function Validate( ) {
317 dbg_error_log("User", ":Validate: Validating user");
321 if ( $this->Get('fullname') == "" ) {
322 $c->messages
[] = i18n('ERROR: The full name may not be blank.');
326 // Password changing is a little special...
327 unset($_POST['password']);
328 if ( $_POST['new_password'] != "******" && $_POST['new_password'] != "" ) {
329 if ( $_POST['new_password'] == $_POST['confirm_password'] ) {
330 $this->Set('password',$_POST['new_password']);
333 $c->messages
[] = i18n('ERROR: The new password must match the confirmed password.');
338 $this->Undefine('password');
341 dbg_error_log("User", ":Validate: User %s validation", ($valid ?
"passed" : "failed"));
346 * Write the User record.
351 if ( parent
::Write() ) {
352 $c->messages
[] = i18n('User record written.');
353 if ( $this->WriteType
== 'insert' ) {
354 $qry = new AwlQuery( "SELECT currval('usr_user_no_seq');" );
355 $qry->Exec("User::Write");
356 $sequence_value = $qry->Fetch(true); // Fetch as an array
357 $this->user_no
= $sequence_value[0];
360 if ( $this->user_no
== $session->user_no
&& $this->Get("date_format_type") != $session->date_format_type
) {
361 // Ensure we match the date style setting
362 $session->date_format_type
= $this->Get("date_format_type");
363 unset($_POST['email_ok']);
364 $qry = new AwlQuery( "SET DATESTYLE TO ?;", ($this->Get("date_format_type") == 'E' ?
'European,ISO' : ($this->Get("date_format_type") == 'U' ?
'US,ISO' : 'ISO')) );
368 return $this->WriteRoles();
374 * Write the roles associated with the user
377 function WriteRoles() {
380 if ( isset($_POST['roles']) && is_array($_POST['roles']) ) {
383 foreach( $_POST['roles'] AS $k => $v ) {
384 if ( $v && $v != "off" ) {
385 $roles .= ( $roles == '' ?
'' : ', ' );
386 $roles .= AwlQuery
::quote($k);
389 $qry = new AwlQuery();
391 $succeeded = $qry->QDo('DELETE FROM role_member WHERE user_no = '.$this->user_no
);
393 $succeeded = $qry->Begin();
394 $sql = 'DELETE FROM role_member WHERE user_no = '.$this->user_no
;
395 $sql .= ' AND role_no NOT IN (SELECT role_no FROM roles WHERE role_name IN ('.$roles.') )';
396 if ( $succeeded ) $succeeded = $qry->QDo($sql);
397 $sql = 'INSERT INTO role_member (role_no, user_no)';
398 $sql .= ' SELECT role_no, '.$this->user_no
.' FROM roles WHERE role_name IN ('.$roles.')';
399 $sql .= ' EXCEPT SELECT role_no, user_no FROM role_member';
400 if ( $succeeded ) $succeeded = $qry->QDo($sql);
406 if ( ! $succeeded ) {
407 $c->messages
[] = i18n('ERROR: There was a database error writing the roles information!');
408 $c->messages
[] = i18n('Please note the time and advise the administrator of your system.');