1<?php
2/**
3 * Federated Login for DokuWiki - provider list class
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @link       http://www.dokuwiki.org/plugin:fedauth
7 * @author     Aoi Karasu <aoikarasu@gmail.com>
8 */
9
10if(!defined('FA_PROVLIST_INC')) define('FA_PROVLIST_INC', __FILE__);
11
12define('PROV_LARGE', 2);
13define('PROV_SMALL', 1);
14
15/**
16 * The provider list class stores all the authorization providers information
17 * and the configuration of display and usage.
18 *
19 * REMARKS:
20 * Most of the methods in this class assumes that the arrays responsible for
21 * the providers display order are normalized. Any outside denormalization will
22 * cause an unexpected behaviour and possible unhandled exceptions.
23 *
24 * On-load normalization is planned in the nearest future.
25 *
26 * @author Aoi Karasu <aoikarasu@gmail.com>
27 */
28class fa_providerlist {
29
30    /**
31     * All configured providers.
32     *
33     * @var array
34     * @access private
35     */
36    var $providers = array();
37
38    /**
39     * Display order of the large provider buttons.
40     *
41     * @var array
42     * @access private
43     */
44    var $orderLarge = array();
45
46    /**
47     * Display order of the small provider buttons.
48     *
49     * @var array
50     * @access private
51     */
52    var $orderSmall = array();
53
54    /**
55     * Name of the configuration file.
56     *
57     * @var string
58     * @access private
59     */
60    var $cfgFile = '';
61
62    /**
63     * Creates an instance of fa_providerlist class using specified configuration file.
64     *
65     * @param string $cfg name of the configuration file
66     * @return object fa_providerlist class instance
67     */
68    public static function create($cfg) {
69        $instance = new self();
70        $instance->loadFrom($cfg);
71        return $instance;
72    }
73
74   /**
75     * Loads and parses a configuration file to build the list of athorization providers.
76     *
77     * @param string $cfg name of the configuration file
78     */
79    protected function loadFrom($cfg) {
80        $this->cfgFile = $cfg;
81
82        if (file_exists($this->cfgFile)) {
83            $fa_providers = array();
84            $fa_order_large = array();
85            $fa_order_small = array();
86
87            include($this->cfgFile);
88            $this->order_large = $fa_order_large;
89            $this->order_small = $fa_order_small;
90            // TODO: normalize the order arrays (most of the methods assumes they are)
91            foreach ($fa_providers as $id => $data) {
92                $provider = fa_provider::create($id, $data);
93                $this->providers[$id] = $provider;
94                // if a provider entry is not in any of sort arrays, add it to small
95                if (!in_array($id, $this->order_large) && !in_array($id, $this->order_small)) {
96                    array_push($this->order_small, $id);
97                }
98            }
99        }
100    }
101
102    /**
103     * Adds a custom authorization provider to the provider list.
104     *
105     * Note: provider id must be unique or else addition fails.
106     *
107     * @param string $id provider identifier
108     * @param string $name name of the provider
109     * @param string $url URL to the provider's authorization service
110     * @param string $large wikilink to large image
111     * @param string $small wikilink to small image
112     */
113    public function addCustom($id, $name, $url, $large, $small) {
114        if (array_key_exists($id, $this->providers)) return 0;
115        $provider = fa_provider::createCustom($id, $name, $url, $large, $small);
116        $this->providers[$id] = $provider;
117        array_push($this->order_small, $id);
118        return $this->providers[$id];
119    }
120
121    /**
122     * Returns the number of all configured providers.
123     *
124     * @return int number of providers
125     */
126    public function count() {
127        return count($this->providers);
128    }
129
130    /**
131     * Returns the number of all providers set to be displayed as large buttons.
132     *
133     * @return int number of providers
134     */
135    public function countLarge() {
136        return count($this->order_large);
137    }
138
139    /**
140     * Returns the number of all providers set to be displayed as small buttons.
141     *
142     * @return int number of providers
143     */
144    public function countSmall() {
145        return count($this->order_small);
146    }
147
148    /**
149     * Returns the requested authorization provider object.
150     *
151     * @param string $id provider identifier
152     * @return object configured authorization provider
153     */
154    public function get($id) {
155        return $this->providers[$id];
156    }
157
158    /**
159     * Returns an array of all configured authorization providers.
160     *
161     * @return array all configured providers
162     */
163    public function getAll() {
164        // NOTE: consider returning normalized array
165        return $this->providers;
166    }
167
168    /**
169     * Returns an array of the providers set to be displayed as large buttons.
170     *
171     * @return array providers to be shown as large buttons
172     */
173    public function getLarge() {
174        $ret = array();
175        foreach($this->order_large as $key => $val) {
176            $ret[$val] = $this->providers[$val];
177        }
178        return $ret;
179    }
180
181    /**
182     * Returns an array of the providers set to be displayed as small buttons.
183     *
184     * @return array providers to be shown as small buttons
185     */
186    public function getSmall() {
187        $ret = array();
188        foreach($this->order_small as $key => $val) {
189            $ret[$val] = $this->providers[$val];
190        }
191        return $ret;
192    }
193
194    /**
195     * Returns the XHTML of provider image choosing automatically
196     * whether it should be small or large.
197     *
198     * @param string $id provider identifier
199     * @param string $class (optional) a CSS class to use
200     * @return string rendered image XHTML
201     */
202    public function getImageXHTML($id, $class='') {
203        $size = in_array($id, $this->order_large) ? PROV_LARGE : PROV_SMALL;
204        return $this->providers[$id]->getImageXHTML($size, $class);
205    }
206
207    /**
208     * Indicates whether a provider is first on its order list.
209     *
210     * @param string $id provider identifier
211     * @throws ErrorException provider not found in any of the order arrays
212     * @return bool true, if the provider is the first one
213     */
214    public function isFirst($id) {
215        $src =& $this->_getOrderFor($id);
216        if (src == null) throw new ErrorException("isFirst() - item not found in any of the order arrays");
217        $first = reset($src);
218        return $first == $id;
219    }
220
221    /**
222     * Indicates whether a provider is the last one on its order list.
223     *
224     * @param string $id provider identifier
225     * @throws ErrorException provider not found in any of the order arrays
226     * @return bool true, if the provider is the last one
227     */
228    public function isLast($id) {
229        $src =& $this->_getOrderFor($id);
230        if (src == null) throw new ErrorException("isLast() - item not found in any of the order arrays");
231        $last = end($src);
232        return $last == $id;
233    }
234
235    /**
236     * Moves a provider one step up in its order array.
237     *
238     * @return true, if move succeded
239     */
240    public function moveDown($id) {
241        $src =& $this->_getOrderFor($id);
242        if (src == null) return false;
243
244        // get the position (key is integer), assumed normalized array
245        $index = array_search($id, $src);
246
247        // do movedown
248        if(count($src) > $index) {
249            array_splice($src, $index+2, 0, $src[$index]);
250            array_splice($src, $index, 1);
251            return true;
252        }
253        return false;
254    }
255
256    /**
257     * Moves a provider one step down in its order array.
258     *
259     * @return true, if move succeded
260     */
261    public function moveUp($id) {
262        $src =& $this->_getOrderFor($id);
263        if (src == null) return false;
264
265        // get the position (key is integer), assumed normalized array
266        $index = array_search($id, $src);
267
268        // do moveup
269        if ((count($src) > $index) && ($index>0)) {
270            array_splice($src, $index-1, 0, $src[$index]);
271            array_splice($src, $index+1, 1);
272            return true;
273        }
274        return false;
275    }
276
277    /**
278     * Removes a custom authorization provider from the provider list.
279     *
280     * @param string $id provider identifier
281     */
282    public function removeCustom($id) {
283        if (!array_key_exists($id, $this->providers)) return false;
284        if (!$this->providers[$id]->isRemovable()) return false;
285
286        unset($this->providers[$id]);
287
288        // remove entry from order array
289        $ord =& $this->_getOrderFor($id);
290        if ($ord != null) {
291            $index = array_search($id, $ord);
292            array_splice($ord, $index, 1);
293        }
294        return 1;
295    }
296
297    /**
298     * Save the authorization providers configuration back to the file it was loaded from
299     * or to a new file, if fname is supplied.
300     *
301     * @param string $user username of the user performing the save
302     */
303    public function save($user) {
304        // create header
305        $date = date('r');
306        $cfg = '<?php' . "\n/**\n * Federated Login Plugin - configuration file\n"
307             . " * This is an automatically generated file. Do not edit.\n"
308             . " * Run for user: $user\n * Date: $date\n */\n\n";
309        // serialize order
310        if ($this->countLarge()) {
311            $cfg .= "\$fa_order_large = array('" . implode("','", $this->order_large) . "');\n";
312        }
313        if ($this->countSmall()) {
314            $cfg .= "\$fa_order_small = array('" . implode("','", $this->order_small) . "');\n";
315        }
316        $cfg .= "\n";
317        // serialize providers
318        foreach ($this->providers as $id => $provider) {
319            $cfg .= $provider->getSerialized();
320        }
321        file_put_contents($this->cfgFile, $cfg);
322    }
323
324    /**
325     * Changes the target configuration file the configuration is beeing saved to.
326     *
327     * @param string fname name of the new config file
328     */
329    public function setConfigFile($fname) {
330       $this->cfgFile = $fname;
331    }
332
333    /**
334     * Toggles the size of a provider button (moves a provider id between order arrays).
335     *
336     * @param string $id provider identifier
337     * @return mixed provider button size code or false on failure
338     */
339    public function toggleSize($id) {
340        if (in_array($id, $this->order_large)) {
341            $index = array_search($id, $this->order_large);
342            array_splice($this->order_large, $index, 1);
343            array_push($this->order_small, $id);
344            return PROV_SMALL;
345        }
346        if (in_array($id, $this->order_small)) {
347            $index = array_search($id, $this->order_small);
348            array_splice($this->order_small, $index, 1);
349            array_push($this->order_large, $id);
350            return PROV_LARGE;
351        }
352        return false;
353    }
354
355    /**
356     * Null reference helper.
357     */
358    var $nullGuard = null;
359
360    /**
361     * Returns the reference to an order array that contains specified provider id.
362     *
363     * @param string $id provider identifier
364     * @return arrayref reference to one of the order arrays or null
365     */
366    private function &_getOrderFor($id) {
367        if (in_array($id, $this->order_small)) {
368            return $this->order_small;
369        }
370        if (in_array($id, $this->order_large)) {
371            return $this->order_large;
372        }
373        return $this->nullGuard;
374    }
375
376} /* fa_providerlist */
377
378/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
379