source: companies/celepar/phpgwapi/doc/vfs/vfs.sgml @ 763

Revision 763, 20.7 KB checked in by niltonneto, 15 years ago (diff)

Importação inicial do Expresso da Celepar

Line 
1<!doctype linuxdoc system>
2
3<article>
4<!-- LyX 1.1 created this file. For more info see http://www.lyx.org/ -->
5<title>
6phpgwapi - VFS Class
7</title>
8<author>
9Jason Wies
10</author>
11<date>
12June 2001, February 2002
13</date>
14<abstract>
15The VFS, or Virtual File System, handles all file system activity
16 for eGoupWare.
17</abstract>
18<sect>
19Introduction and Purpose<label id="sec:introduction" >
20<p>
21The latest version of the VFS for eGoupWare combines actual
22 file system manipulation with fully integrated database support.
23 It features nearly transparent handling of files and directories,
24 as well as files inside and outside the virtual root. This document
25 is intended to provide API and application developers with a guide
26 to incorporating the VFS into their work.
27</p>
28<sect>
29Basics<label id="sec:basics" >
30<sect1>
31Prerequisites<label id="sec:prerequisites" >
32<p>
33You must explicitly enable the VFS class. To do this, set 'enable_vfs_class'
34 to True in &dollar;GLOBALS&lsqb;'phpgw_info'&rsqb;&lsqb;'flags'&rsqb;.
35 An example:
36</p>
37<p>
38<verb>
39&dollar;GLOBALS&lsqb;'phpgw_info'&rsqb;&lsqb;'flags'&rsqb; = array(
40     'currentapp' =&gt; 'phpwebhosting',
41     'noheader' =&gt; False,
42     'noappheader' =&gt; False,
43     'enable_vfs_class' =&gt; True,
44     'enable_browser_class' =&gt; True
45);
46</verb>
47</p><sect1>
48Concepts<label id="sec:concepts" >
49<p>
50The VFS in located in phpgwapi/inc/class.vfs_sql.inc.php. You
51 can look over it, but I don't suggest trying to understand how it
52 works. It isn't necessary to know its internals to use it, but you
53 may find the inline comments helpful. The basic things to keep in
54 mind:
55</p>
56<p>
57<itemize>
58 <item>
59Files and directories are synonymous in almost all cases
60</itemize>
61<p>
62<verb>
63&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;mv (array(
64     'from' =&gt; 'file1',
65     'to' =&gt; 'dir/file2'
66));
67
68&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;mv (array(
69     'from' =&gt; 'dir1',
70     'to' =&gt; 'dir/dir1'
71));
72
73&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;rm (array(
74     'string' =&gt; 'file'
75));
76
77&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;rm (array(
78     'string' =&gt; 'dir'
79));
80</verb>
81</p><p>
82All work as you would except them to. The major exception is:
83</p>
84<p>
85<verb>
86&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;touch (array(
87     'string' =&gt; 'file'
88));
89</verb>
90</p><p>
91vs.
92</p>
93<p>
94<verb>
95&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;mkdir (array(
96     'string' =&gt; 'dir'
97));
98
99</verb>
100<p>
101<itemize>
102 <item>
103Users and groups are synonymous
104</itemize>
105</p><p>
106As far as the actual paths are concerned, users and groups are
107 the same. /home/username works the same as /home/groupname.
108</p>
109<p>
110<itemize>
111 <item>
112You should never have to know the real paths of files
113</itemize>
114</p><p>
115One of the VFS's responsibilities is to translate paths for you.
116 While you certainly <em>can</em> operate using full paths, it is much simpler
117 to use the virtual paths. For example, instead of using:
118</p>
119<p>
120<verb>
121&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;cp (array(
122     'from' =&gt; '/var/www/egroupware/files/home/user/file1',
123     'to' =&gt; '/var/www/egroupware/files/home/user/file2',
124     'relatives' =&gt; array(
125          RELATIVE_NONE|VFS_REAL,
126          RELATIVE_NONE|VFS_REAL
127     )
128));
129</verb>
130</p><p>
131you might use
132</p>
133<p>
134<verb>
135&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;cp (array(
136     'from' =&gt; '/home/user/file1',
137     'to' =&gt; '/home/user/file2',
138     'relatives' =&gt; array(
139          RELATIVE_NONE,
140          RELATIVE_NONE
141     )
142));
143</verb>
144</p><p>
145(We'll get to the RELATIVE's in a minute.)
146</p>
147<p>
148Site administrators should be able to move their files dir around
149 on their system and know that everything will continue to work smoothly.
150</p>
151<p>
152<itemize>
153 <item>
154Relativity is <em>vital</em>
155</itemize>
156</p><p>
157Relativity is a new feature in the VFS, and its importance cannot
158 be stressed enough. It will make your life much easier, especially
159 for file system intensive applications, but it will take some getting
160 used to. If something doesn't work right the first time, chances
161 are great it has to do with incorrect relativity settings. We will
162 deal with relativity in depth in the Relativity section.
163</p>
164<sect>
165Basic Functions<label id="sec:basic_functions" >
166<p>
167These are two functions you'll need to know before we get into
168 relativity.
169</p>
170<sect1>
171path_parts ()<label id="sec:path_parts" >
172<p>
173The job of path_parts () is to translate any given file location
174 into its many component parts for any relativity. The values passed
175 to path_parts () are:
176</p>
177<p>
178<verb>
179string
180relatives
181object
182</verb>
183</p><p>
184'string' is the path you want to translate, 'relatives' is the
185 standard relativity array, and 'object' specifies how you would like
186 the return value: if 'object' is True, an object will be returned;
187 if 'object' is False, an array will be returned. I think you'll find
188 the object easier to deal with, and we'll be using it throughout
189 this document. The most important returned values (but not all) for
190 path_parts () are:
191</p>
192<p>
193<verb>
194fake_full_path
195fake_leading_dirs
196fake_extra_path
197fake_name
198real_full_path
199real_leading_dirs
200real_extra_path
201real_name
202</verb>
203</p><p>
204Just like you would think, fake_full_path contains the full virtual
205 path of 'string', and real_full_path contains the full real path
206 of 'string'. The fake_name and real_name variables should always
207 be the same, and contain the final file or directory name. The leading_dirs
208 contain everything except the name, and the extra_path is everything
209 from the / before "home" to the end of the leading_dirs. To better
210 illustrate, here is an example:
211</p>
212<p>
213<verb>
214&dollar;p = &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;path_parts (array(
215     'string' =&gt; '/home/jason/dir/file',
216     'relatives' =&gt; array(
217         RELATIVE_NONE
218     )
219));
220</verb>
221<p>
222<itemize>
223 <item>
224&dollar;p-&gt;fake_full_path - /home/jason/dir/file
225 <item>
226&dollar;p-&gt;fake_leading_dirs - /home/jason/dir
227 <item>
228&dollar;p-&gt;fake_extra_path - home/jason/dir
229 <item>
230&dollar;p-&gt;fake_name - file
231 <item>
232&dollar;p-&gt;real_full_path - /var/www/egroupware/files/home/jason/dir/file
233 <item>
234&dollar;p-&gt;real_leading_dirs - /var/www/egroupware/files/home/jason/dir
235 
236 <item>
237&dollar;p-&gt;real_extra_path - home/jason/dir
238 <item>
239&dollar;p-&gt;real_name - file
240</itemize>
241</p><p>
242As you can see, path_parts () is a very useful function and will
243 save you from doing those darn substr ()'s yourself. For those of
244 you used to the prior VFS, note that <em>getabsolutepath () is depreciated</em>.
245 getabsolutepath () still exists (albeit in a much different form),
246 and is responsible for some of the path translation, but it is an
247 <em>internal</em> function only. Applications should only use path_parts ().
248 We have shown you how to use path_parts () so you can experiment
249 with it using different paths and relativities as we explore relativity.
250</p>
251<sect1>
252cd ()<label id="sec:cd" >
253<p>
254Part of the overall goal for the VFS in eGoupWare is to give
255 the user a seamless experience during their session. For example,
256 if they upload a file using a file manager to the directory /home/my_group/project1,
257 and then go to download an email attachment, the default directory
258 will be /home/my_group/project1. This is accomplished using the cd
259 () function. Examples:
260</p>
261<p>
262<verb>
263/* cd to their home directory */
264&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;cd (array(
265     'string' =&gt; '/'
266));
267
268/* cd to /home/jason/dir */
269&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;cd (array(
270     'string' =&gt; '/home/jason/dir',
271     'relative' =&gt; False,
272     'relatives' =&gt; array(
273          RELATIVE_NONE
274     )
275));
276
277/* When following the above, cd's to /home/jason/dir/dir2 */
278&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;cd (array(
279     'string' =&gt; 'dir2',
280     'relative' =&gt; True
281));
282</verb>
283</p><p>
284If 'relative' is True, the 'string' is simply appended to the
285 current path. If you want to know what the current path is, use &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;pwd
286 ().
287</p>
288<p>
289Now you're ready for relativity.
290</p>
291<sect>
292Relativity<label id="sec:relativity" >
293<p>
294Ok, just one last thing before we get into relativity. You will
295 notice throughout the examples the use of &dollar;fakebase. &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;fakebase
296 is by default '/home'. The old VFS was hard-coded to use '/home',
297 but the naming choice for this is now up to administrators. See the
298 <ref id="sec:fakebase" name="Fakebase directory (changing /home)" > section for more information. Throughout the rest of this document,
299 you will see &dollar;fakebase used in calls to the VFS, and /home
300 used in actual paths. <em>You should always use &dollar;fakebase when
301 making applications. </em>I suggest doing &dollar;fakebase = &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;fakebase;
302 right off the bat to keep things neater.
303</p>
304<sect1>
305What is it and how does it work?
306<p>
307One of the design challenges for a Virtual File System is to
308 try to figure out whether the calling application is referring to
309 a file inside or outside the virtual root, and if inside, exactly
310 where. To solve this problem, the eGoupWare VFS uses RELATIVE
311 defines that are used in bitmasks passed to each function. The result
312 is that any set of different relativities can be used in combination
313 with each other. Let's look at a few examples. Say you want to move
314 'logo.png' from the user's home directory to the current directory.
315 
316</p>
317<p>
318<verb>
319&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;mv (array(
320    'from' =&gt; 'logo.png',
321    'to' =&gt; 'logo.png',
322    'relatives' =&gt; array(
323          RELATIVE_USER,
324          RELATIVE_ALL
325     )
326));
327</verb>
328</p><p>
329RELATIVE_USER means relative to the user's home directory. RELATIVE_ALL
330 means relative to the current directory, as set by cd () and as reported
331 by pwd (). So if the current directory was "&dollar;fakebase/my_group/project1",
332 the call to mv () would be processed as:
333</p>
334<p>
335<verb>
336MOVE "&dollar;fakebase/jason/logo.png" TO "&dollar;fakebase/my_group/project1/logo.png"
337</verb>
338</p><p>
339and the actual file system call would be:
340</p>
341<p>
342<verb>
343rename ('/var/www/egroupware/files/home/jason/logo.php', '/var/www/egroupware/files/home/my_group/project1/logo.png');
344</verb>
345</p><p>
346Those used to the old VFS will note that you do not have to translate
347 the path beforehand. Let's look at another example. Suppose you were
348 moving an email attachment stored in eGoupWare's temporary directory
349 to the 'attachments' directory within the user's home directory (we're
350 assuming the attachments directory exists). Note that the temporary
351 directory is <em>outside</em> the virtual root.
352</p>
353<p>
354<verb>
355&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;mv (array(
356     'from' =&gt; &dollar;GLOBALS&lsqb;'phpgw_info'&rsqb;&lsqb;'server'&rsqb;&lsqb;'temp_dir'&rsqb; . '/' . &dollar;randomdir . '/' . &dollar;randomfile,
357     'to' =&gt; 'attachments/actual_name.ext',
358     'relatives' =&gt; array(
359          RELATIVE_NONE|VFS_REAL,
360          RELATIVE_USER
361     )
362));
363</verb>
364</p><p>
365&dollar;randomdir and &dollar;randomfile are what the directory
366 and file might be called before they are given a proper name by the
367 user, which is actual_name.ext in this example. RELATIVE_NONE is
368 the define for using full path names. However, RELATIVE_NONE is still
369 relative to the virtual root, so we pass along VFS_REAL as well,
370 to say that the file is <em>outside</em> the virtual root, somewhere else
371 in the file system. Once again, RELATIVE_USER means relative to the
372 user's home directory. So the actual file system call might look
373 like this (keep in mind that &dollar;randomdir and &dollar;randomfile
374 are just random strings):
375</p>
376<p>
377<verb>
378rename ('/var/www/egroupware/tmp/0ak5adftgh7/jX42sC9M', '/var/www/egroupware/files/home/jason/attachments/actual_name.ext');
379</verb>
380</p><p>
381Of course you don't have to know that, nor should you be concerned
382 with it; you can take it for granted that the VFS will translate
383 the paths correctly. Let's take a look at one more example, this
384 time using the RELATIVE_USER_APP define. RELATIVE_USER_APP is used
385 to store quasi-hidden application files, similar to the Unix convention
386 of &tilde;/.appname. It simply appends .appname to the user's home
387 directory. For example, if you were making an HTML editor application
388 named 'htmledit', and wanted to keep a backup file in case something
389 goes wrong, you could use RELATIVE_USER_APP to store it:
390</p>
391<p>
392<verb>
393&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;write (array(
394     'string' =&gt; 'file.name&tilde;',
395     'relatives' =&gt; array(
396          RELATIVE_USER_APP
397     ),
398     'content' =&gt; &dollar;contents
399));
400</verb>
401</p><p>
402This assumes that &tilde;/.htmledit exists of course. The backup
403 file "file.name&tilde;" would then be written in &dollar;fakebase/jason/.htmledit/file.name&tilde;.
404 Note that storing files like this might not be as good of a solution
405 as storing them in the temporary directory or in the database. But
406 it is there in case you need it.
407</p>
408<sect1>
409Complete List<label id="sec:relatives_complete_list" >
410<p>
411Here is the complete list of RELATIVE defines, and what they
412 do:
413</p>
414<p>
415<descrip>
416 <tag>
417RELATIVE_ROOT</tag>Don't translate the path at all. Just prepends
418 a /. You'll probably want to use RELATIVE_NONE though, which handles
419 both virtual and real files.
420 <tag>
421RELATIVE_USER</tag>User's home directory
422 <tag>
423RELATIVE_CURR_USER</tag>Current user's home directory. If the
424 current directory is &dollar;fakebase/my_group/project1, this will
425 return is &dollar;fakebase/my_group
426 <tag>
427RELATIVE_USER_APP</tag>Append .appname to the user's home directory,
428 where appname is the current application's appname
429 <tag>
430RELATIVE_PATH</tag>DO NOT USE. Relative to the current directory,
431 used in RELATIVE_ALL
432 <tag>
433RELATIVE_NONE</tag>Not relative to anything. Use this with VFS_REAL
434 for files outside the virtual root. Note that using RELATIVE_NONE
435 by itself still means relative to the virtual root
436 <tag>
437RELATIVE_CURRENT</tag>An alias for the currently set RELATIVE
438 define, or RELATIVE_ALL if none is set (see the Defaults section)
439 <tag>
440VFS_REAL</tag>File is outside of the virtual root. Usually used
441 with RELATIVE_NONE
442 <tag>
443RELATIVE_ALL</tag>Relative to the current directory. Use RELATIVE_ALL<em>
444 </em>instead of RELATIVE_PATH
445</descrip>
446</p><sect1>
447Defaults<label id="sec:relatives_defaults" >
448<p>
449You might be thinking to yourself that passing along RELATIVE
450 defines with every VFS call is overkill, especially if your application
451 always uses the same relativity. The default RELATIVE define for
452 all VFS calls is RELATIVE_CURRENT. RELATIVE_CURRENT itself defaults
453 to RELATIVE_ALL (relative to the current path), <em>unless</em> your application
454 sets a specific relativity. If your application requires most of
455 the work to be done outside of the virtual root, you may wish to
456 set RELATIVE_CURRENT to RELATIVE_NONE|VFS_REAL. set_relative () is
457 the function to do this. For example:
458</p>
459<p>
460<verb>
461&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;set_relative (array(
462     'mask' =&gt; RELATIVE_NONE|VFS_REAL
463));
464
465&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;read (array(
466     'string' =&gt; '/etc/passwd'
467));
468
469&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;cp (array(
470     'from' =&gt; '/usr/include/stdio.h',
471     'to' =&gt; '/tmp/stdio.h'
472));
473
474&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;cp (array(
475     'from' =&gt; '/usr/share/pixmaps/yes.xpm',
476     'to' =&gt; 'icons/yes.xpm',
477     'relatives' =&gt; array(
478          RELATIVE_CURRENT,
479          RELATIVE_USER
480     )
481));
482</verb>
483</p><p>
484You should notice that no relativity array is needed in the other
485 calls that refer to files outside the virtual root, but one is needed
486 for calls that include files inside the virtual root. Any RELATIVE
487 define can be set as the default and works in the same fashion. To
488 retrieve the currently set define, use get_relative (). Note that
489 the relativity is reset after each page request; that is, it's good
490 only for the life of the current page loading, and is not stored
491 in session management.
492</p>
493<sect>
494Function reference<label id="sec:function_reference" >
495<p>
496To view the function reference for the VFS, use the doc/inlinedocparser.php
497 script that comes with eGoupWare, ie <url url="http://localhost/doc/inlinedocparser.php?fn=class.vfs_sql.inc.php" name="http://localhost/doc/inlinedocparser.php?fn=class.vfs_sql.inc.php">.
498</p>
499<sect>
500Notes<label id="sec:notes" >
501<sect1>
502Database<label id="sec:database" >
503<p>
504Data about the files and directories within the virtual root
505 is kept in the SQL database. Currently, this information includes:
506</p>
507<p>
508<itemize>
509 <item>
510File ID (used internally, primary key for table)
511 <item>
512Owner ID (phpGW account_id)
513 <item>
514Created by ID (phpGW account_id)
515 <item>
516Modified by ID (phpGW account_id)
517 <item>
518Created (date)
519 <item>
520Modified (date)
521 <item>
522Size (bytes)
523 <item>
524MIME type
525 <item>
526Deleteable (Y/N/Other?)
527 <item>
528Comment
529 <item>
530App (appname of application that created the file)
531 <item>
532Directory (directory the file or directory is in)
533 <item>
534Name (name of file or directory)
535 <item>
536Link directory (if the file or directory is linked, what the
537 actual directory is)
538 <item>
539Link name (if the file or directory is linked, what the actual
540 name is)
541 <item>
542Version (numeric version of the file)
543</itemize>
544</p><p>
545The internal names of these (the database column names) are stored
546 in the &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;attributes
547 array, which is useful for loops, and is guaranteed to be up-to-date.
548</p>
549<p>
550Note that no information is kept about files outside the virtual
551 root. If a file is moved outside, all records of it are deleted from
552 the database (other than the journaling records). If a file is moved
553 into the virtual root, some information, specifically MIME-type,
554 is not always stored in the database. The vital information has defaults:
555 owner is based on where the file is being stored; size is correctly
556 read; deleteable is set to Y.
557</p>
558<sect1>
559ACL support<label id="sec:acl_support" >
560<p>
561ACL support is built into the VFS. vfs-&gt;acl_check () does
562 the actual checking, and is called from all VFS functions as needed.
563 If the file or directory sent to acl_check () doesn't exist, the
564 permissions for the parent directory are used to determine access.
565 ACL checking can be overridden at any time by setting vfs-&gt;override_acl.
566 For example:
567</p>
568<p>
569<verb>
570&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;override_acl = 1;
571&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;mkdir (array(
572     'string' =&gt; &dollar;GLOBALS&lsqb;'fakebase'&rsqb;. '/' . &dollar;group_array&lsqb;'account_name'&rsqb;,
573     'relatives' =&gt; array(
574          RELATIVE_NONE
575     )
576));
577&dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;override_acl = 0;
578</verb>
579</p><sect1>
580Function aliases<label id="sec:function_aliases" >
581<p>
582You might have noticed there are some functions that just pass
583 the arguments on to other functions. These are provided in part because
584 of legacy and in part for convenience. You can use either. Here is
585 the list (alias -&gt; actual):
586</p>
587<p>
588<itemize>
589 <item>
590copy -&gt; cp
591 <item>
592move -&gt; rm
593 <item>
594delete -&gt; rm
595 <item>
596dir -&gt; ls
597</itemize>
598</p><sect1>
599Fakebase directory (changing /home)<label id="sec:fakebase" >
600<p>
601The old VFS was hard-coded to use '/home' as the fake base directory,
602 even though the user never saw it. With the new system, crafty administrators
603 may wish to change '/home' to something else, say '/users' or '/public_html'.
604 The fake base directory name is stored in &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;fakebase,
605 and changing it will transparently change it throughout the VFS and
606 all applications. However, this must be done <em>before</em> any data is in
607 the VFS database. If you wish to change it afterwords, you'll have
608 to manually update the database, replacing the old value with the
609 new value. <em>Application programmers need to recognize that /home is
610 not absolute, and use &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;fakebase
611 instead</em>. I suggest setting &dollar;fakebase = &dollar;GLOBALS&lsqb;'phpgw'&rsqb;-&gt;vfs-&gt;fakebase;
612 right off the bat to keep things neater.
613</p>
614<sect>
615About this Document
616<sect1>
617Copyright and License
618<p>
619Copyright (c) 2001, 2002 Jason Wies
620</p>
621<p>
622Permission is granted to copy, distribute and/or modify this
623 document under the terms of the GNU Free Documentation License, Version
624 1.1 or any later version published by the Free Software Foundation;
625 with no Invarient Sections, with no Front-Cover Texts, and no Back-Cover
626 Texts.
627</p>
628<p>
629A copy of the license is available at <url url="http://www.gnu.org/copyleft/fdl.html" name="http://www.gnu.org/copyleft/fdl.html">.
630</p>
631<sect1>
632History
633<p>
634Original document released in June 2001 by Jason Wies.
635</p>
636<p>
637Updated February 2002 to include arrayized parameters, single
638 quotes, and GLOBALS.
639</p>
640<sect1>
641Contributing
642<p>
643Contributions are always welcome. Please send to the current
644 maintainer, Jason Wies,
645</p>
646
647
648</article>
Note: See TracBrowser for help on using the repository browser.