1<?php
2# $Id$
3
4class AdminHandler extends PFAHandler {
5    protected $db_table = 'admin';
6    protected $id_field = 'username';
7
8    protected function validate_new_id() {
9        $email_check = check_email($this->id);
10
11        if ($email_check == '') {
12            return true;
13        } else {
14            $this->errormsg[] = $email_check;
15            $this->errormsg[$this->id_field] = Config::lang('pAdminCreate_admin_username_text_error1');
16            return false;
17        }
18    }
19
20    protected function no_domain_field() {
21        # PFAHandler die()s if domain field is not set. Disable this behaviour for AdminHandler.
22    }
23
24    # init $this->struct, $this->db_table and $this->id_field
25    protected function initStruct() {
26        # NOTE: There are dependencies between domains and domain_count
27        # NOTE: If you disable "display in list" for domain_count, the SQL query for domains might break.
28        # NOTE: (Disabling both shouldn't be a problem.)
29
30        # TODO: move to a db_group_concat() function?
31        if (db_pgsql()) {
32            $domains_grouped = "array_to_string(array_agg(domain), ',')";
33        } else { # mysql
34            $domains_grouped = 'group_concat(domain)';
35        }
36
37        $passwordReset = (int) Config::bool('forgotten_admin_password_reset');
38
39        $reset_by_sms = 0;
40        if ($passwordReset && Config::read('sms_send_function')) {
41            $reset_by_sms = 1;
42        }
43
44        $this->struct=array(
45            # field name                allow       display in...   type    $PALANG label          $PALANG description   default / options / ...
46            #                           editing?    form    list
47            'username'         => pacol($this->new, 1,      1,      'text', 'admin'              , 'email_address'     , '', array(),
48                array('linkto' => 'list.php?table=domain&username=%s') ),
49            'password'         => pacol(1,          1,      0,      'pass', 'password'           , ''                  ),
50            'password2'        => pacol(1,          1,      0,      'pass', 'password_again'     , ''                  , '', array(),
51                /*not_in_db*/ 0,
52                /*dont_write_to_db*/ 1,
53                /*select*/ 'password as password2'
54            ),
55
56            'superadmin'       => pacol(1,          1,      0,      'bool', 'super_admin'        , 'super_admin_desc'  , 0
57# TODO: (finally) replace the ALL domain with a column in the admin table
58# TODO: current status: 'superadmin' column exists and is written when storing an admin with AdminHandler,
59# TODO: but the superadmin status is still (additionally) stored in the domain_admins table ("ALL" dummy domain)
60# TODO: to keep the database backwards-compatible with 2.3.x.
61# TODO: Note: superadmins created with 2.3.x after running upgrade_1284() will not work until you re-run upgrade_1284()
62# TODO: Create them with the trunk version to avoid this problem.
63            ),
64
65            'domains'          => pacol(1,          1,      0,      'list', 'domain'             , ''                  , array(), list_domains(),
66               /*not_in_db*/ 0,
67               /*dont_write_to_db*/ 1,
68               /*select*/ "coalesce(domains,'') as domains"
69               /*extrafrom set in domain_count*/
70            ),
71
72            'domain_count'     => pacol(0,          0,      1,      'vnum', 'pAdminList_admin_count', ''               , '', array(),
73               /*not_in_db*/ 0,
74               /*dont_write_to_db*/ 1,
75               /*select*/ 'coalesce(__domain_count,0) as domain_count',
76               /*extrafrom*/ 'LEFT JOIN ( ' .
77                                ' SELECT count(*) AS __domain_count, ' . $domains_grouped . ' AS domains, username AS __domain_username ' .
78                                ' FROM ' . table_by_key('domain_admins') .
79                                " WHERE domain != 'ALL' GROUP BY username " .
80                             ' ) AS __domain on username = __domain_username'),
81
82            'active'           => pacol(1,          1,      1,      'bool', 'active'             , ''                  , 1     ),
83            'phone'            => pacol(1,  $reset_by_sms,  0,      'text', 'pCreate_mailbox_phone', 'pCreate_mailbox_phone_desc', ''),
84            'email_other'      => pacol(1,  $passwordReset, 0,      'mail', 'pCreate_mailbox_email', 'pCreate_mailbox_email_desc', ''),
85            'token'            => pacol(1,          0,      0,      'text', ''                   , ''                  ),
86            'token_validity'   => pacol(1,          0,      0,      'ts',   ''                   , '', date("Y-m-d H:i:s",time())),
87            'created'          => pacol(0,          0,      0,      'ts',   'created'            , ''                  ),
88            'modified'         => pacol(0,          0,      1,      'ts',   'last_modified'      , ''                  ),
89        );
90    }
91
92    protected function initMsg() {
93        $this->msg['error_already_exists'] = 'admin_already_exists';
94        $this->msg['error_does_not_exist'] = 'admin_does_not_exist';
95        $this->msg['confirm_delete'] = 'confirm_delete_admin';
96
97        if ($this->new) {
98            $this->msg['logname'] = 'create_admin';
99            $this->msg['store_error'] = 'pAdminCreate_admin_result_error';
100            $this->msg['successmessage'] = 'pAdminCreate_admin_result_success';
101        } else {
102            $this->msg['logname'] = 'edit_admin';
103            $this->msg['store_error'] = 'pAdminEdit_admin_result_error';
104            $this->msg['successmessage'] = 'pAdminEdit_admin_result_success';
105        }
106    }
107
108    public function webformConfig() {
109        return array(
110            # $PALANG labels
111            'formtitle_create' => 'pAdminCreate_admin_welcome',
112            'formtitle_edit' => 'pAdminEdit_admin_welcome',
113            'create_button' => 'pAdminCreate_admin_button',
114
115            # various settings
116            'required_role' => 'global-admin',
117            'listview' => 'list.php?table=admin',
118            'early_init' => 0,
119        );
120    }
121
122    /**
123     * called by $this->store() after storing $this->values in the database
124     * can be used to update additional tables, call scripts etc.
125     */
126    protected function postSave() : bool {
127        # store list of allowed domains in the domain_admins table
128        if (isset($this->values['domains'])) {
129            if (is_array($this->values['domains'])) {
130                $domains = $this->values['domains'];
131            } elseif ($this->values['domains'] == '') {
132                $domains = array();
133            } else {
134                $domains = explode(',', $this->values['domains']);
135            }
136
137            db_delete('domain_admins', 'username', $this->id, "AND domain != 'ALL'");
138
139            foreach ($domains as $domain) {
140                $values = array(
141                    'username'  => $this->id,
142                    'domain'    => $domain,
143                );
144                db_insert('domain_admins', $values, array('created'));
145                # TODO: check for errors
146            }
147        }
148
149        # Temporary workaround to keep the database compatible with 2.3.x
150        if (isset($this->values['superadmin'])) {
151            if ($this->values['superadmin'] == 1) {
152                $values = array(
153                    'username'  => $this->id,
154                    'domain'    => 'ALL',
155                );
156                $where = db_where_clause(array('username' => $this->id, 'domain' => 'ALL'), $this->struct);
157                $result = db_query_one("SELECT username from " . table_by_key('domain_admins') . " " . $where);
158                if (empty($result)) {
159                    db_insert('domain_admins', $values, array('created'));
160                    # TODO: check for errors
161                }
162            } else {
163                db_delete('domain_admins', 'username', $this->id, "AND domain = 'ALL'");
164            }
165        }
166
167        return true; # TODO: don't hardcode
168    }
169
170    protected function read_from_db_postprocess($db_result) {
171        foreach ($db_result as $key => $row) {
172            # convert 'domains' field to an array
173            if ($row['domains'] == '') {
174                $db_result[$key]['domains'] = array();
175            } else {
176                $db_result[$key]['domains'] = explode(',', $row['domains']);
177            }
178            if ($row['superadmin']) {
179                $db_result[$key]['domain_count'] = Config::lang('super_admin');
180            }
181        }
182        return $db_result;
183    }
184
185    /**
186     *  @return bool
187     */
188    public function delete() {
189        if (! $this->view()) {
190            $this->errormsg[] = Config::Lang($this->msg['error_does_not_exist']);
191            return false;
192        }
193
194        db_delete('domain_admins', $this->id_field, $this->id);
195        db_delete($this->db_table, $this->id_field, $this->id);
196
197        db_log('admin', 'delete_admin', $this->id); # TODO delete_admin is not a valid db_log keyword yet, and 'admin' is not displayed in viewlog.php
198        $this->infomsg[] = Config::Lang_f('pDelete_delete_success', $this->id);
199        return true;
200    }
201
202
203    # TODO: generate password if $new, no password specified and $CONF['generate_password'] is set
204    # TODO: except if $this->admin_username == setup.php --- this exception should be handled directly in setup.php ("if $values['password'] == '' error_out")
205
206    /**
207     * compare password / password2 field
208     * error message will be displayed at the password2 field
209     */
210    protected function _validate_password2($field, $val) {
211        return $this->compare_password_fields('password', 'password2');
212    }
213}
214
215/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
216