diff --git a/fas/.gitignore b/fas/.gitignore deleted file mode 100644 index e89c835..0000000 --- a/fas/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -*~ -random_seed -pubring.gpg -secring.gpg -trustdb.gpg -fas.log -*.pyc -*.pyo -*.swp -*.mo -fas.egg-info -build -gnupg -locale -ssl - diff --git a/fas/COPYING b/fas/COPYING deleted file mode 100644 index 623b625..0000000 --- a/fas/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/fas/MANIFEST.in b/fas/MANIFEST.in deleted file mode 100644 index 188f1f0..0000000 --- a/fas/MANIFEST.in +++ /dev/null @@ -1,28 +0,0 @@ -include fas2.sql -include README COPYING TODO -include *.cfg *.conf -include convert.py start-fas -include client/*.conf -include fas/config/* -include fas/static/* -include fas/static/css/* -include fas/static/images/* -include fas/static/images/balloons/* -include fas/static/js/* -include fas/static/theme/* -include fas/static/theme/fas/* -include fas/static/theme/fas/css/* -include fas/static/theme/fas/images/* -include fas/templates/cla/*.html -include fas/templates/cla/*.txt -include fas/templates/group/*.html -include fas/templates/group/*.txt -include fas/templates/openid/*.html -include fas/templates/openid/*.txt -include fas/templates/user/*.html -include fas/templates/user/*.txt -include fas/templates/*.html -include fas/templates/*.txt -include po/LINGUAS -include po/*.po -include po/*.pot diff --git a/fas/README b/fas/README deleted file mode 100644 index 3c01e73..0000000 --- a/fas/README +++ /dev/null @@ -1,145 +0,0 @@ -===================== -Fedora Account System -===================== - -:Authors: Ricky Zhou - Mike McGrath - Toshio Kuratomi -:Contact: fedora-infrastructure-list@redhat.com -:Date: Friday, 29 February, 2008 -:Version: 0.1 - -The Fedora Account System holds information on Fedora Contributors to give -them access to the wonderful things that Fedora has. - -.. contents:: - -This is a TurboGears_ project. It can be started by running the start-fas.py -script. - -.. _TurboGears: http://www.turbogears.org - -------------- -Prerequisites -------------- -Before you can get started, make sure to have the following packages installed -(example being from Fedora 8 with a local postgres database server):: - - yum install git-core postgresql-plpython postgresql-server postgresql-python \ - python-TurboMail TurboGears pygpgme python-sqlalchemy python-genshi \ - python-psycopg2 pytz python-babel babel - - # Note: on RHEL5 you need postgresql-pl instead of postgresql-plpython - -At present, the database needs to be a postgres database since we use triggers -to manage some of the data (like syncing accounts with bugzilla). - -If you are unfamiliar with postgres and this is your first time installing it, -you will want to generate the database and allow users to connect. First as -root run:: - - /etc/init.d/postgresql initdb - -Then make sure the bottom of /var/lib/pgsql/data/pg_hba.conf looks like:: - - # TYPE DATABASE USER CIDR-ADDRESS METHOD - - # "local" is for Unix domain socket connections only - local all all ident sameuser - # IPv4 local connections: - #host all all 127.0.0.1/32 ident sameuser - # IPv6 local connections: - #host all all ::1/128 ident sameuser - - host all all 0.0.0.0 0.0.0.0 md5 - -Then just start the postgres database: - - /etc/init.d/postgresql start - -------- -Hacking -------- -If you want to hack on the Account System you need to checkout the module. -It's presently part of the fedora-infrastructure git repo:: - git clone git://git.fedorahosted.org/git/fedora-infrastructure - cd fedora-infrastructure/fas - -Once you are inside you're fresh checkout, you need to regenerate some files -that are created by the build script, setup.py:: - python setup.py egg_info && python setup.py build - -This will create the fas.egg-info directory which has metadata about the -program. It allows things like the identity provider to work. - -Do any configuration necessary and start up the postgres server. Then make the -plpython language available on new databases, create a postgres user to manage -the data and import the schema:: - sudo -u postgres createlang plpythonu template1 - sudo -u postgres createuser --encrypted --pwprompt fedora - sudo -u postgres psql < fas2.sql - -The last thing to do is configure the application to use your settings. -You'll need to edit dev.cfg and change the following lines:: - mail.on = False # Set to True if you want to test notification sending - mail.server = 'localhost' # Your mail server - sqlalchemy.dburi = "postgres://fedora:pass@localhost/fas2" # Fill in the - # password you gave in the createuser step and the db host if it's not - # localhost. - server.socket_port=8080 # Change if you don't want to run on port 8080 - base_url_filter.base_url = "http://localhost:8080/fas" # Change the port if - # you changed server.socket_port above. - -You may also need to change some of the directories and settings in -fas/config/app.cfg. - -You should then be able to start the server and test things out:: - ./start-fas.py - # browse to http://localhost:8080/fas/ - -The default administrative user is "admin" with password "admin" - -Another handy command for trying short snippets of code is tg-admin shell. -Make sure you're in the top level directory that start-fas.py and dev.cfg is -in, then run:: - tg-admin shell - --------------------- -Enabling Local Users --------------------- -* THIS IS EXPERIMENTAL * - -To allow local users to log in to your system, first enable fas via the -client in fas/client/fasClient.py - - ./fasClient -e - -To disable run - - ./fasClient -d - -To sync with your local install run: - - ./fasClient -i - -To test, look and see if your groups or users show up with getent. For -example: - - getent passwd - getent group - ------------- -Localization ------------- -To generate the POT file (located in the po/ subdirectory), run the -following from the top level directory: - - pybabel extract -F pybabel.conf -o po/fas.pot . - -Message merging should be done manually using msgmerge at this point. - - python setup.py build - -compiles the PO files and places them where TurboGears will look for -them. To enable a language to be available to users, it must be added -to po/LINGUAS. diff --git a/fas/TODO b/fas/TODO deleted file mode 100644 index a8e9669..0000000 --- a/fas/TODO +++ /dev/null @@ -1,14 +0,0 @@ -Things to Fix in FAS2 before declaring it done: - - * fasClient.py: Proper logging - -Nice-to-have things: - * fas/group.py: Easy searching within groups (and sponsor/admin interface) - - * fas/aliases.py: An easy interface to manage mail aliases - - * setup.py: - - Fix installing in all scenarios: bdist_egg, install, and - install --install-data='/usr/share/fas' - - Install fas.cfg - - Install start-fas to the correct directory diff --git a/fas/client/fas.conf b/fas/client/fas.conf deleted file mode 100644 index 7e18c20..0000000 --- a/fas/client/fas.conf +++ /dev/null @@ -1,66 +0,0 @@ -[global] -; url - Location to fas server -url = http://localhost:8088/accounts/ - -; temp - Location to generate files while user creation process is happening -temp = /var/db - -; login - username to contact fas -login = admin - -; password - password for login name -password = admin - -; prefix - Install db files, etc, to a prefix (like a chroot for example) -prefix = / - -[host] -; Group hierarchy is 1) groups, 2) restricted_groups 3) ssh_restricted_groups -; so if someone is in all 3, the client behaves the same as if they were just -; in 'groups' - -; groups that should have a shell account on this system. -groups = sysadmin-main - -; groups that should have a restricted account on this system. -; restricted accounts use the restricted_shell value in [users] -restricted_groups = - -; ssh_restricted_groups: groups that should be restricted by ssh key. You will -; need to disable password based logins in order for this value to have any -; security meaning. Group types can be placed here as well, for example -; @hg,@git,@svn -ssh_restricted_groups = - -; aliases_template: Gets prepended to the aliases file when it is generated by -; fasClient -aliases_template = /tmp/template.txt - -[users] -; default shell given to people in [host] groups -shell = /bin/bash - -; home - the location for fas user home dirs -home = /home/fedora - -; home_backup_dir - Location home dirs should get moved to when a user is -; deleted this location should be tmpwatched -home_backup_dir = /tmp/fedora - -; ssh_restricted_app - This is the path to the restricted shell script. It -; will not work automatically for most people though through alterations it -; is a powerfull way to restrict access to a machine. An alternative example -; could be given to people who should only have cvs access on the machine. -; setting this value to "/usr/bin/cvs server" would do this. -ssh_restricted_app = "/usr/bin/cvs server" - -; restricted_shell - The shell given to users in the ssh_restricted_groups -restricted_shell = /sbin/nologin - -; ssh_restricted_shell - The shell given to users in the ssh_restricted_groups -ssh_restricted_shell = /bin/bash - -; ssh_key_options - Options to be appended to people ssh keys. Users in the -; ssh_restricted_groups will have the keys they uploaded altered when they are -; installed on this machine, appended with the options below. -ssh_key_options = no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty diff --git a/fas/client/fasClient b/fas/client/fasClient deleted file mode 100755 index 4d36a95..0000000 --- a/fas/client/fasClient +++ /dev/null @@ -1,577 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright © 2007-2008 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, modify, -# copy, or redistribute it subject to the terms and conditions of the GNU -# General Public License v.2. This program is distributed in the hope that it -# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the -# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. You should have -# received a copy of the GNU General Public License along with this program; -# if not, write to the Free Software Foundation, Inc., 51 Franklin Street, -# Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are -# incorporated in the source code or documentation are not subject to the GNU -# General Public License and may only be used or replicated with the express -# permission of Red Hat, Inc. -# -# Red Hat Author(s): Mike McGrath -# -# TODO: put tmp files in a 700 tmp dir - -import sys -import logging -import syslog -import os -import tempfile -import codecs -import datetime -import time - -from urllib2 import URLError -from fedora.tg.client import BaseClient, AuthError, ServerError -from optparse import OptionParser -from shutil import move, rmtree, copytree -from rhpl.translate import _ - -import ConfigParser - -parser = OptionParser() - -parser.add_option('-i', '--install', - dest = 'install', - default = False, - action = 'store_true', - help = _('Download and sync most recent content')) -parser.add_option('-c', '--config', - dest = 'CONFIG_FILE', - default = '/etc/fas.conf', - metavar = 'CONFIG_FILE', - help = _('Specify config file (default "%default")')) -parser.add_option('--nogroup', - dest = 'no_group', - default = False, - action = 'store_true', - help = _('Do not sync group information')) -parser.add_option('--nopasswd', - dest = 'no_passwd', - default = False, - action = 'store_true', - help = _('Do not sync passwd information')) -parser.add_option('--noshadow', - dest = 'no_shadow', - default = False, - action = 'store_true', - help = _('Do not sync shadow information')) -parser.add_option('--nohome', - dest = 'no_home_dirs', - default = False, - action = 'store_true', - help = _('Do not create home dirs')) -parser.add_option('--nossh', - dest = 'no_ssh_keys', - default = False, - action = 'store_true', - help = _('Do not create ssh keys')) -parser.add_option('-s', '--server', - dest = 'FAS_URL', - default = None, - metavar = 'FAS_URL', - help = _('Specify URL of fas server.')) -parser.add_option('-p', '--prefix', - dest = 'prefix', - default = None, - metavar = 'prefix', - help = _('Specify install prefix. Useful for testing')) -parser.add_option('-e', '--enable', - dest = 'enable', - default = False, - action = 'store_true', - help = _('Enable FAS synced shell accounts')) -parser.add_option('-d', '--disable', - dest = 'disable', - default = False, - action = 'store_true', - help = _('Disable FAS synced shell accounts')) -parser.add_option('-a', '--aliases', - dest = 'aliases', - default = False, - action = 'store_true', - help = _('Sync mail aliases')) - - -(opts, args) = parser.parse_args() - -log = logging.getLogger('fas') - -try: - config = ConfigParser.ConfigParser() - if os.path.exists(opts.CONFIG_FILE): - config.read(opts.CONFIG_FILE) - elif os.path.exists('fas.conf'): - config.read('fas.conf') - print >> sys.stderr, "Could not open %s, defaulting to ./fas.conf" % opts.CONFIG_FILE - else: - print >> sys.stderr, "Could not open %s." % opts.CONFIG_FILE - sys.exit(5) -except ConfigParser.MissingSectionHeaderError, e: - print >> sys.stderr, "Config file does not have proper formatting - %s" % e - sys.exit(6) - -FAS_URL = config.get('global', 'url').strip('"') -if opts.prefix: - prefix = opts.prefix -else: - prefix = config.get('global', 'prefix').strip('"') - -def _chown(arg, dir_name, files): - os.chown(dir_name, arg[0], arg[1]) - for file in files: - os.chown(os.path.join(dir_name, file), arg[0], arg[1]) - -class MakeShellAccounts(BaseClient): - temp = None - groups = None - people = None - memberships = None - emails = None - group_mapping = {} - valid_groups = {} - usernames = {} - - def mk_tempdir(self): - self.temp = tempfile.mkdtemp('-tmp', 'fas-', os.path.join(prefix + config.get('global', 'temp').strip('"'))) - - def rm_tempdir(self): - rmtree(self.temp) - - - def valid_groups(self): - ''' Create a dict of valid groups, including that of group_type ''' - if not self.groups: - self.group_list() - valid_groups = {'groups':[], 'restricted_groups':[], 'ssh_restricted_groups': []} - for restriction in valid_groups: - for group in config.get('host', restriction).strip('"').split(','): - if group == '': - continue - if group == '@all': - for grp in self.groups: - if not grp['name'].startswith('cla'): - valid_groups[restriction].append(grp['name']) - elif group.startswith('@'): - for grp in self.groups: - if grp['group_type'] == group[1:]: - valid_groups[restriction].append(grp['name']) - else: - valid_groups[restriction].append(group) - self.valid_groups = valid_groups - - def valid_group(self, name, restriction=None): - ''' Determine if group is valid on the system ''' - if restriction: - return name in self.valid_groups[restriction] - else: - for restrict_key in self.valid_groups: - if name in self.valid_groups[restrict_key]: - return True - return False - - def valid_user(self, username): - ''' Is the user valid on this system ''' - if not self.valid_groups: - self.valid_groups() - if not self.group_mapping: - self.get_group_mapping() - try: - for restriction in self.valid_groups: - for group in self.valid_groups[restriction]: - if username in self.group_mapping[group]: - return True - except KeyError: - return False - return False - - def ssh_key(self, person): - ''' determine what ssh key a user should have ''' - for group in self.valid_groups['groups']: - try: - if person['username'] in self.group_mapping[group]: - return person['ssh_key'] - except KeyError: - print >> sys.stderr, '%s could not be found in fas but was in your config under "groups"!' % group - continue - for group in self.valid_groups['restricted_groups']: - try: - if person['username'] in self.group_mapping[group]: - return person['ssh_key'] - except KeyError: - print >> sys.stderr, '%s could not be found in fas but was in your config under "restricted_groups"!' % group - continue - for group in self.valid_groups['ssh_restricted_groups']: - try: - if person['username'] in self.group_mapping[group]: - command = config.get('users', 'ssh_restricted_app').strip('"') - options = config.get('users', 'ssh_key_options').strip('"') - key = 'command="%s",%s %s' % (command, options, person['ssh_key']) - return key - except TypeError: - print >> sys.stderr, '%s could not be found in fas but was in your config under "ssh_restricted_groups"!' % group - continue - return 'INVALID\n' - - def shell(self, username): - ''' Determine what shell username should have ''' - for group in self.valid_groups['groups']: - try: - if username in self.group_mapping[group]: - return config.get('users', 'shell').strip('"') - except KeyError: - print >> sys.stderr, '%s could not be found in fas but was in your config under "groups"!' % group - continue - for group in self.valid_groups['restricted_groups']: - try: - if username in self.group_mapping[group]: - return config.get('users', 'restricted_shell').strip('"') - except KeyError: - print >> sys.stderr, '%s could not be found in fas but was in your config under "restricted_groups"!' % group - continue - for group in self.valid_groups['ssh_restricted_groups']: - try: - if username in self.group_mapping[group]: - return config.get('users', 'ssh_restricted_shell').strip('"') - except KeyError: - print >> sys.stderr, '%s could not be found in fas but was in your config under "ssh_restricted_groups"!' % group - continue - - print >> sys.stderr, 'Could not determine shell for %s. Defaulting to /sbin/nologin' % username - return '/sbin/nologin' - - def install_aliases_txt(self): - move(self.temp + '/aliases', prefix + '/etc/aliases') - - def passwd_text(self, people=None): - i = 0 - passwd_file = codecs.open(self.temp + '/passwd.txt', mode='w', encoding='utf-8') - shadow_file = codecs.open(self.temp + '/shadow.txt', mode='w', encoding='utf-8') - os.chmod(self.temp + '/shadow.txt', 00400) - if not self.people: - self.people_list() - for person in self.people: - username = person['username'] - if self.valid_user(username): - uid = person['id'] - human_name = person['human_name'] - password = person['password'] - home_dir = "%s/%s" % (config.get('users', 'home').strip('"'), username) - shell = self.shell(username) - passwd_file.write("=%s %s:x:%i:%i:%s:%s:%s\n" % (uid, username, uid, uid, human_name, home_dir, shell)) - passwd_file.write("0%i %s:x:%i:%i:%s:%s:%s\n" % (i, username, uid, uid, human_name, home_dir, shell)) - passwd_file.write(".%s %s:x:%i:%i:%s:%s:%s\n" % (username, username, uid, uid, human_name, home_dir, shell)) - shadow_file.write("=%i %s:%s:99999:0:99999:7:::\n" % (uid, username, password)) - shadow_file.write("0%i %s:%s:99999:0:99999:7:::\n" % (i, username, password)) - shadow_file.write(".%s %s:%s:99999:0:99999:7:::\n" % (username, username, password)) - i = i + 1 - passwd_file.close() - shadow_file.close() - - def valid_user_group(self, person_id): - ''' Determine if person is valid on this machine as defined in the - config file. I worry that this is going to be horribly inefficient - with large numbers of users and groups.''' - for member in self.memberships: - for group in self.memberships[member]: - if group['person_id'] == person_id: - return True - return False - - def get_usernames(self): - usernames = {} - if not self.people: - self.people_list() - for person in self.people: - uid = person['id'] - if self.valid_user_group(uid): - username = person['username'] - usernames[uid] = username - self.usernames = usernames - - def get_group_mapping(self): - if not self.usernames: - self.get_usernames() - for group in self.groups: - gid = group['id'] - name = group['name'] - try: - ''' Shoot me now I know this isn't right ''' - members = [] - for member in self.memberships[name]: - members.append(self.usernames[member['person_id']]) - memberships = ','.join(members) - self.group_mapping[name] = members - except KeyError: - ''' No users exist in the group ''' - pass - - - def groups_text(self, groups=None, people=None): - i = 0 - file = open(self.temp + '/group.txt', 'w') - if not self.groups: - self.group_list() - if not self.people: - self.people_list() - if not self.usernames: - self.get_usernames() - if not self.group_mapping: - self.get_group_mapping() - ''' First create all of our users/groups combo ''' - for person in self.people: - uid = person['id'] - try: - if self.valid_user(self.usernames[uid]): - username = person['username'] - file.write("=%i %s:x:%i:\n" % (uid, username, uid)) - file.write("0%i %s:x:%i:\n" % (i, username, uid)) - file.write(".%s %s:x:%i:\n" % (username, username, uid)) - i = i + 1 - except KeyError: - continue - - for group in self.groups: - gid = group['id'] - name = group['name'] - try: - ''' Shoot me now I know this isn't right ''' - members = [] - for member in self.memberships[name]: - members.append(self.usernames[member['person_id']]) - memberships = ','.join(members) - self.group_mapping[name] = members - except KeyError: - ''' No users exist in the group ''' - pass - file.write("=%i %s:x:%i:%s\n" % (gid, name, gid, memberships)) - file.write("0%i %s:x:%i:%s\n" % (i, name, gid, memberships)) - file.write(".%s %s:x:%i:%s\n" % (name, name, gid, memberships)) - i = i + 1 - file.close() - - def group_list(self, search='*'): - params = {'search' : search} - request = self.send_request('group/list', auth=True, input=params) - self.groups = request['groups'] - memberships = {} - for group in self.groups: - memberships[group['name']] = [] - try: - for member in request['memberships'][u'%s' % group['id']]: - memberships[group['name']].append(member) - except KeyError: - pass - self.memberships = memberships - self.valid_groups() - return self.groups - - def people_list(self, search='*'): - params = {'search' : search} - self.people = self.send_request('user/list', auth=True, input=params)['people'] - - def email_list(self, search='*'): - params = {'search' : search} - self.emails = self.send_request('user/email_list', auth=True, input=params)['emails'] - return self.emails - - def make_group_db(self): - self.groups_text() - os.system('makedb -o %s/group.db %s/group.txt' % (self.temp, self.temp)) - - def make_passwd_db(self): - self.passwd_text() - os.system('makedb -o %s/passwd.db %s/passwd.txt' % (self.temp, self.temp)) - os.system('makedb -o %s/shadow.db %s/shadow.txt' % (self.temp, self.temp)) - os.chmod(self.temp + '/shadow.db', 00400) - - def install_passwd_db(self): - try: - move(self.temp + '/passwd.db', os.path.join(prefix + '/var/db/passwd.db')) - except IOError, e: - print "ERROR: Could not write passwd db - %s" % e - - def install_shadow_db(self): - try: - move(self.temp + '/shadow.db', os.path.join(prefix + '/var/db/shadow.db')) - except IOError, e: - print "ERROR: Could not write shadow db - %s" % e - - def install_group_db(self): - try: - move(self.temp + '/group.db', os.path.join(prefix + '/var/db/group.db')) - except IOError, e: - print "ERROR: Could not write group db - %s" % e - - def create_homedirs(self): - ''' Create homedirs and home base dir if they do not exist ''' - home_base = os.path.join(prefix + config.get('users', 'home').strip('"')) - if not os.path.exists(home_base): - os.makedirs(home_base, mode=0755) - for person in self.people: - home_dir = os.path.join(home_base, person['username']) - if not os.path.exists(home_dir) and self.valid_user(person['username']): - syslog.syslog('Creating homedir for %s' % person['username']) - copytree('/etc/skel/', home_dir) - os.path.walk(home_dir, _chown, [person['id'], person['id']]) - - def remove_stale_homedirs(self): - ''' Remove homedirs of users that no longer have access ''' - home_base = os.path.join(prefix + config.get('users', 'home').strip('"')) - try: - home_backup_dir = config.get('users', 'home_backup_dir').strip('"') - except ConfigParser.NoOptionError: - home_backup_dir = '/var/tmp/' - users = os.listdir(home_base) - for user in users: - if not self.valid_user(user): - if not os.path.exists(home_backup_dir): - os.makedirs(home_backup_dir) - syslog.syslog('Backed up %s to %s' % (user, home_backup_dir)) - target = '%s-%s' % (user, time.mktime(datetime.datetime.now().timetuple())) - move(os.path.join(home_base, user), os.path.join(prefix + home_backup_dir, target)) - - def create_ssh_keys(self): - ''' Create ssh keys ''' - home_base = prefix + config.get('users', 'home').strip('"') - for person in self.people: - username = person['username'] - if self.valid_user(username): - ssh_dir = os.path.join(home_base, username, '.ssh') - if person['ssh_key']: - key = self.ssh_key(person) - if not os.path.exists(ssh_dir): - os.makedirs(ssh_dir, mode=0700) - f = codecs.open(os.path.join(ssh_dir, 'authorized_keys'), mode='w', encoding='utf-8') - f.write(key + '\n') - f.close() - os.chmod(os.path.join(ssh_dir, 'authorized_keys'), 0600) - os.path.walk(ssh_dir, _chown, [person['id'], person['id']]) - - def make_aliases_txt(self): - ''' update your mail aliases file ''' - if not self.groups: - groups = self.group_list() - if not self.usernames: - self.get_usernames() - - self.emails = self.email_list() - email_file = codecs.open(self.temp + '/aliases', mode='w', encoding='utf-8') - email_template = codecs.open(config.get('host', 'aliases_template').strip('"')) - email_file.write("# Generated by fasClient\n") - for line in email_template.readlines(): - email_file.write(line) - sorted = self.emails.keys() - sorted.sort() - for person in sorted: - email_file.write("%s: %s\n" % (person, self.emails[person])) - for group in self.groups: - name = group['name'] - members = {} - members['member'] = [] - for membership in self.memberships[name]: - role_type = membership['role_type'] - person = self.usernames[membership['person_id']] - if role_type == 'user': - ''' Legacy support ''' - members['member'].append(person) - continue - members['member'].append(person) - try: - members[role_type].append(person) - except KeyError: - members[role_type] = [person] - for role in members: - email_file.write("%s-%ss: %s\n" % (name, role, ','.join(members[role]))) - email_file.close() - -def enable(): - temp = tempfile.mkdtemp('-tmp', 'fas-', config.get('global', 'temp').strip('"')) - - old = open('/etc/sysconfig/authconfig', 'r') - new = open(temp + '/authconfig', 'w') - for line in old: - if line.startswith("USEDB"): - new.write("USEDB=yes\n") - else: - new.write(line) - new.close() - old.close() - try: - move(temp + '/authconfig', '/etc/sysconfig/authconfig') - except IOError, e: - print "ERROR: Could not write /etc/sysconfig/authconfig - %s" % e - sys.exit(5) - os.system('/usr/sbin/authconfig --updateall') - rmtree(temp) - -def disable(): - temp = tempfile.mkdtemp('-tmp', 'fas-', config.get('global', 'temp').strip('"')) - old = open('/etc/sysconfig/authconfig', 'r') - new = open(temp + '/authconfig', 'w') - for line in old: - if line.startswith("USEDB"): - new.write("USEDB=no\n") - else: - new.write(line) - old.close() - new.close() - try: - move(temp + '/authconfig', '/etc/sysconfig/authconfig') - except IOError, e: - print "ERROR: Could not write /etc/sysconfig/authconfig - %s" % e - sys.exit(5) - os.system('/usr/sbin/authconfig --updateall') - rmtree(temp) - - -if __name__ == '__main__': - if opts.enable: - enable() - if opts.disable: - disable() - - if opts.install: - try: - fas = MakeShellAccounts(FAS_URL, config.get('global', 'login').strip('"'), config.get('global', 'password').strip('"'), False) - except AuthError, e: - print >> sys.stderr, e - sys.exit(1) - except URLError, e: - print >> sys.stderr, 'Could not connect to %s - %s' % (FAS_URL, e.reason[1]) - sys.exit(9) - fas.mk_tempdir() - fas.make_group_db() - fas.make_passwd_db() - if not opts.no_group: - fas.install_group_db() - if not opts.no_passwd: - fas.install_passwd_db() - if not opts.no_shadow: - fas.install_shadow_db() - if not opts.no_home_dirs: - fas.create_homedirs() - fas.remove_stale_homedirs() - if not opts.no_ssh_keys: - fas.create_ssh_keys() - fas.rm_tempdir() - if opts.aliases: - try: - fas = MakeShellAccounts(FAS_URL, config.get('global', 'login').strip('"'), config.get('global', 'password').strip('"'), False) - except AuthError, e: - print >> sys.stderr, e - sys.exit(1) - fas.mk_tempdir() - fas.make_aliases_txt() - fas.install_aliases_txt() - - if not (opts.install or opts.enable or opts.disable or opts.aliases): - parser.print_help() diff --git a/fas/client/restricted-shell b/fas/client/restricted-shell deleted file mode 100755 index 6f4fd1c..0000000 --- a/fas/client/restricted-shell +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python -tt -# This script allows people to run the commands listed in 'commands' and -# 'commands' only. Be careful though, by adding /bin/bash you've effectively -# disabled this script. Also, via some voodoo you can restrict what flags -# get passed or even completely alter what would normally happen if a command -# were envoked (see scp section below) - -# TODO: better documentation needed for how this file works - - -import sys, os - -commands = { - "git-receive-pack": "/usr/bin/git-receive-pack", - "git-upload-pack": "/usr/bin/git-upload-pack", - "bzr": "/usr/bin/run-bzr", - "hg": "/usr/bin/run-hg", - "mtn": "/usr/bin/run-mtn", - "svnserve": "/usr/bin/run-svnserve", - "scp": "/usr/bin/scp", -} - -if __name__ == '__main__': - orig_cmd = os.environ.get('SSH_ORIGINAL_COMMAND') - if not orig_cmd: - print "Need a command" - sys.exit(1) - allargs = orig_cmd.split() - try: - basecmd = os.path.basename(allargs[0]) - cmd = commands[basecmd] - except: - sys.stderr.write("Invalid command %s\n" % orig_cmd) - sys.exit(2) - - if basecmd in ('git-receive-pack', 'git-upload-pack'): - # git repositories need to be parsed specially - thearg = ' '.join(allargs[1:]) - if thearg[0] == "'" and thearg[-1] == "'": - thearg = thearg.replace("'","") - thearg = thearg.replace("\\'", "") - if thearg[:len('/git/')] != '/git/' or not os.path.isdir(thearg): - print "Invalid repository %s" % thearg - sys.exit(3) - allargs = [thearg] - elif basecmd in ('scp'): - thearg = ' '.join(allargs[1:]) - firstLetter = allargs[2][0] - secondLetter = allargs[2][1] - uploadTarget = "/srv/web/releases/%s/%s/%s/" % (firstLetter, secondLetter, allargs[2]) - if thearg.find('/') != -1: - print "scp yourfile-1.2.tar.gz scm.fedorahosted.org:$YOURPROJECT # No trailing /" - sys.exit(4) - elif not os.path.isdir(uploadTarget): - print "http://fedorahosted.org/releases/%s/%s/%s does not exist!" % (firstLetter, secondLetter, allargs[2]) - sys.exit(5) - else: - newargs = [] - newargs.append(allargs[0]) - newargs.append(allargs[1]) - newargs.append(uploadTarget) - os.execv(cmd, [cmd] + newargs[1:]) - sys.exit(1) - else: - allargs = allargs[1:] - os.execv(cmd, [cmd] + allargs) - sys.exit(1) diff --git a/fas/convert.py b/fas/convert.py deleted file mode 100644 index cd90d2a..0000000 --- a/fas/convert.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/python -import pgdb - -from turbogears.view import engines -import turbogears.view -import turbogears.util as tg_util -from turbogears import view, database, errorhandling, config -from itertools import izip -from inspect import isclass -from turbogears import update_config, start_server -import cherrypy -cherrypy.lowercase_api = True -from os.path import * -import sys -import time -import crypt -import random - -if len(sys.argv) > 1: - update_config(configfile=sys.argv[1], - modulename="fas.config") -elif exists(join(dirname(__file__), "setup.py")): - update_config(configfile="dev.cfg",modulename="fas.config") -else: - update_config(configfile="prod.cfg",modulename="fas.config") - -from sqlalchemy import * -from sqlalchemy.exceptions import * -from fas.model import * - - -def generate_salt(length=8): - chars = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - salt = '' - for i in xrange(length): - salt += random.choice(chars) - return salt - - - -db = pgdb.connect(dsn='localhost', user='fedora', password='test', database='fedorausers') - -c = db.cursor() - -c.execute('select id, username, email, human_name, gpg_keyid, ssh_key, password, comments, postal_address, telephone, affiliation, creation, approval_status, internal_comments, ircnick from person order by id;') - -print "Converting People Table" -for person in c.fetchall(): - (id, username, email, human_name, gpg_keyid, ssh_key, password, comments, postal_address, telephone, affiliation, creation, approval_status, internal_comments, ircnick) = person - print "\t%i - %s" % (id, username) - p = People() - p.id = id - p.username = username - p.human_name = human_name - p.gpg_keyid = gpg_keyid - p.ssh_key = ssh_key - p.password = crypt.crypt(password, "$1$%s" % generate_salt(8)) - p.comments = comments - p.postal_address = postal_address - p.telephone = telephone - p.creation = creation - p.internal_comments = internal_comments - p.ircnick = ircnick - p.status = 'active' - p.email = email - try: - session.flush() - except IntegrityError, e: - print "\tERROR - Could not create %s - %s" % (username, e) - session.close() - continue - -c.execute('select id, name, owner_id, group_type, needs_sponsor, user_can_remove, prerequisite_id, joinmsg from project_group;') -bool_dict = {0 : False, 1 : True} -print "Creating Groups..." -admin = People.by_username('admin') -admin_id = admin.id -for group in c.fetchall(): - (id, name, owner_id, group_type, needs_sponsor, user_can_remove, prerequisite_id, joinmsg) = group - print "%i - %s" % (id, name) - try: - group = Groups() - group.id = id - group.name = name - group.display_name = name - if owner_id == 100001: - ''' Update to new admin id ''' - owner_id = admin_id - group.owner_id = owner_id - group.group_type = group_type - group.needs_sponsor = bool(bool_dict[needs_sponsor]) - group.user_can_remove = bool(bool_dict[user_can_remove]) -# if prerequisite_id: -# prerequisite = Groups.by_id(prerequisite_id) -# group.prerequisite = prerequisite - group.joinmsg = joinmsg - # Log here - session.flush() - except IntegrityError, e: - print "\tERROR - The group: '%s' (%i) could not be created - %s" % (name, id, e) - except FlushError, e: - print "\tERROR - The group: '%s' (%i) could not be created - %s" % (name, id, e) - except InvalidRequestError, e: - print "\tERROR - The group: '%s' (%i) could not be created - %s" % (name, id, e) - - session.close() - -c.execute('select person_id, project_group_id, role_type, role_domain, role_status, internal_comments, sponsor_id, creation, approval from role order by person_id;') -print "Creating Role Maps..." -for role in c.fetchall(): - (person_id, project_group_id, role_type, role_domain, role_status, internal_comments, sponsor_id, creation, approval) = role - print "%s - %s" % (person_id, project_group_id) - try: - role = PersonRoles() - if len(role_status) > 10: - role_status = 'approved' - if role_status == 'declined': - ''' No longer exists ''' - continue - role.role_status = role_status - role.role_type = role_type - role.member = People.by_id(person_id) - role.group = Groups.by_id(project_group_id) - session.flush() - except ProgrammingError, e: - print "\tERROR - The role %s -> %s could not be created - %s" % (person_id, project_group_id, e) - session.close() - except IntegrityError, e: - if e.message.find('dupilcate key'): - print "\tERROR - The role %s -> %s already exists! Skipping" % (person_id, project_group_id) - session.close() - continue - print "\tERROR - The role %s -> %s could not be created - %s" % (person_id, project_group_id, e) - session.close() diff --git a/fas/fas.cfg b/fas/fas.cfg deleted file mode 100644 index 9e1b649..0000000 --- a/fas/fas.cfg +++ /dev/null @@ -1,131 +0,0 @@ -[global] - -theme = 'fas' -# TODO: better namespacing (maybe a [fas] section) -admingroup = 'accounts' - -accounts_email = "nobody@fedoraproject.org" -#accounts_email = "accounts@fedoraproject.org" -legal_cla_email = "nobody@fedoraproject.org" -#legal_cla_email = "legal-cla-archive@fedoraproject.org" - -email_host = "fedoraproject.org" # as in, web-members@email_host - -gpgexec = "/usr/bin/gpg" -gpghome = "/home/ricky/work/fedora/fedora-infrastructure/fas/gnupg" -gpg_fingerprint = "C199 1E25 D00A D200 2D2E 54D1 BF7F 1647 C54E 8410" -gpg_passphrase = "m00!s@ysth3c0w" -gpg_keyserver = "hkp://subkeys.pgp.net" - -cla_done_group = "cla_done" -cla_fedora_group = "cla_fedora" - -privileged_view_groups = "(^fas-.*)" -username_blacklist = "(.*-members)|(.*-sponsors)|(.*-administrators)|(root)|(webmaster)" - -openidstore = "/var/tmp/fas/openid" - -openssl_digest = "md5" -openssl_expire = 31536000 # 60*60*24*365 = 1 year -openssl_ca_file = "/srv/fedora-infrastructure/fas/ssl/ca-Upload" -openssl_c = "US" -openssl_st = "North Carolina" -openssl_l = "Raleigh" -openssl_o = "Fedora Project" -openssl_ou = "Upload Files" - -# Groups that automatically grant membership to other groups -# Format: 'group1:a,b,c|group2:d,e,f' -auto_approve_groups = 'cvsextras:fedorabugs|cla_fedora:cla_done|cla_redhat:cla_done|cla_dell:cla_done|cla_ibm:cla_done' - -# This is where all of your settings go for your development environment # Settings that are the same for both development and production -# (such as template engine, encodings, etc.) all go in -# fas/config/app.cfg - -mail.on = True -mail.server = 'localhost' -#mail.testmode = True -mail.debug = False -mail.encoding = 'utf-8' - -# DATABASE - -# pick the form for your database -# sqlobject.dburi="postgres://username@hostname/databasename" -# sqlobject.dburi="mysql://username:password@hostname:port/databasename" -# sqlobject.dburi="sqlite:///file_name_and_path" - -# If you have sqlite, here's a simple default to get you started -# in development -sqlalchemy.dburi="postgres://fedora:test@localhost/fas2" -#sqlalchemy.echo=True - -# if you are using a database or table type without transactions -# (MySQL default, for example), you should turn off transactions -# by prepending notrans_ on the uri -# sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename" - -# for Windows users, sqlite URIs look like: -# sqlobject.dburi="sqlite:///drive_letter:/path/to/file" - -# SERVER - -# Some server parameters that you may want to tweak -server.socket_port=8088 - -# Enable the debug output at the end on pages. -# log_debug_info_filter.on = False - -server.environment="development" -autoreload.package="fas" - -# session_filter.on = True - -# Set to True if you'd like to abort execution if a controller gets an -# unexpected parameter. False by default -tg.strict_parameters = True - -server.webpath='/accounts' -base_url_filter.on = True -base_url_filter.use_x_forwarded_host = True -base_url_filter.base_url = "http://localhost:8088/accounts" - -# Make the session cookie only return to the host over an SSL link -# Disabled for testing. -#visit.cookie.secure = True - -# LOGGING -# Logging configuration generally follows the style of the standard -# Python logging module configuration. Note that when specifying -# log format messages, you need to use *() for formatting variables. -# Deployment independent log configuration is in fas/config/log.cfg -[logging] - -[[loggers]] -[[[fas]]] -level='DEBUG' -qualname='fas' -handlers=['debug_out'] - -[[[allinfo]]] -level='INFO' -handlers=['debug_out'] - -[[[access]]] -level='INFO' -qualname='turbogears.access' -handlers=['access_out'] -propagate=0 - -[[[identity]]] -level='INFO' -qualname='turbogears.identity' -handlers=['access_out'] -propagate=0 - -[[[database]]] -# Set to INFO to make SQLAlchemy display SQL commands -level='ERROR' -qualname='sqlalchemy.engine' -handlers=['debug_out'] -propagate=0 diff --git a/fas/fas.spec b/fas/fas.spec deleted file mode 100644 index 3481176..0000000 --- a/fas/fas.spec +++ /dev/null @@ -1,115 +0,0 @@ -%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} - -Name: fas -Version: 0.8.1 -Release: 1%{?dist} -Summary: Fedora Account System - -Group: Development/Languages -License: GPLv2 -URL: https://fedorahosted.org/fas2/ -Source0: https://fedorahosted.org/releases/f/e/fedora-infrastructure/%{name}-%{version}.tar.gz -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) - -BuildArch: noarch -BuildRequires: python-devel -BuildRequires: python-setuptools-devel -BuildRequires: TurboGears -BuildRequires: gettext -Requires: TurboGears >= 1.0.4 -Requires: python-sqlalchemy >= 0.4 -Requires: python-TurboMail -Requires: python-fedora-infrastructure >= 0.2.99.2 -Requires: babel -Requires: pygpgme -Requires: python-babel -Requires: python-genshi -Requires: pytz - -%description -The Fedora Account System is a web application that manages the accounts of -Fedora Project Contributors. It's built in TurboGears and comes with a json -API for querying against remotely. - -The python-fedora-infrastructure package has a TurboGears identity provider -that works with the Account System. - -%package clients -Summary: Clients for the Fedora Account System -Group: Applications/System -Requires: python-fedora -Requires: rhpl - -%description clients -Additional scripts that work as clients to the accounts system. - -%prep -%setup -q - - -%build -%{__python} setup.py build --install-data='%{_datadir}' - - -%install -%{__rm} -rf %{buildroot} -%{__python} setup.py install -O1 --skip-build --install-data='%{_datadir}' --root %{buildroot} -%{__mkdir_p} %{buildroot}%{_sbindir} -%{__mkdir_p} %{buildroot}%{_sysconfdir} -%{__mv} %{buildroot}%{_bindir}/start-fas %{buildroot}%{_sbindir} -# Unreadable by others because it's going to contain a database password. -%{__install} -m 640 fas.cfg %{buildroot}%{_sysconfdir} -%{__install} -m 600 client/fas.conf %{buildroot}%{_sysconfdir} -%find_lang %{name} - -%clean -%{__rm} -rf %{buildroot} - - -%pre -/usr/sbin/useradd -c 'Fedora Acocunt System user' -s /sbin/nologin \ - -r -M -d %{_datadir}fas fas &> /dev/null || : - - -%files -f %{name}.lang -%defattr(-,root,root,-) -%doc README TODO COPYING fas2.sql -%{python_sitelib}/* -%{_datadir}/fas/ -%{_sbindir}/start-fas -%attr(-,root,fas) %config(noreplace) %{_sysconfdir}/fas.cfg - -%files clients -%{_bindir}/* -%config(noreplace) %{_sysconfdir}/fas.conf - -%changelog -* Tue Mar 14 2008 Mike McGrath - 0.8.1-1 -- Upstream released a new version - -* Tue Mar 14 2008 Mike McGrath - 0.8-1 -- Upstream released a new version - -* Tue Mar 13 2008 Mike McGrath - 0.7.1-1 -- Upstream released new version - -* Tue Mar 13 2008 Mike McGrath - 0.7-1 -- Upstream released new version - -* Tue Mar 13 2008 Mike McGrath - 0.6-1 -- Upstream released a new version - -* Tue Mar 11 2008 Mike McGrath - 0.5-1 -- Upstream released a new version - -* Tue Mar 11 2008 Mike McGrath - 0.4-1 -- added fas.conf will fix later. - -* Mon Mar 10 2008 Mike McGrath - 0.3-1 -- Upstream released a new version. - -* Mon Mar 10 2008 Mike McGrath - 0.2-1 -- Added fas user/group - -* Mon Mar 10 2008 Toshio Kuratomi - 0.1-1 -- Initial Build. diff --git a/fas/fas/__init__.py b/fas/fas/__init__.py deleted file mode 100644 index e333e3c..0000000 --- a/fas/fas/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -from fas import release -__version__ = release.VERSION - -class FASError(Exception): - '''FAS Error''' - pass - -class ApplyError(FASError): - '''Raised when a user could not apply to a group''' - pass - -class ApproveError(FASError): - '''Raised when a user could not be approved in a group''' - pass - -class SponsorError(FASError): - '''Raised when a user could not be sponsored in a group''' - pass - -class UpgradeError(FASError): - '''Raised when a user could not be upgraded in a group''' - pass - -class DowngradeError(FASError): - '''Raised when a user could not be downgraded in a group''' - pass - -class RemoveError(FASError): - '''Raised when a user could not be removed from a group''' - pass diff --git a/fas/fas/auth.py b/fas/fas/auth.py deleted file mode 100644 index a4a18d0..0000000 --- a/fas/fas/auth.py +++ /dev/null @@ -1,192 +0,0 @@ -from turbogears import config - -from fas.model import Groups -from fas.model import PersonRoles -from fas.model import People - -from sqlalchemy.exceptions import * -import turbogears - -import re - -def isAdmin(person): - ''' - Returns True if the user is a FAS admin (a member of the admingroup) - ''' - admingroup = config.get('admingroup') - try: - if person.group_roles[admingroup].role_status == 'approved': - return True - except KeyError: - return False - return False - -def canAdminGroup(person, group, role=None): - ''' - Returns True if the user is allowed to act as an admin for a group - ''' - if isAdmin(person) or (group.owner == person): - return True - if not role: - try: - role = PersonRoles.query.filter_by(group=group, member=person).one() - except InvalidRequestError: - ''' Not in the group ''' - return False - if role.role_status == 'approved' and role.role_type == 'administrator': - return True - return False - -def canSponsorGroup(person, group): - ''' - Returns True if the user is allowed to act as a sponsor for a group - ''' - if isAdmin(person) or \ - group.owner == person: - return True - try: - role = PersonRoles.query.filter_by(group=group, member=person).one() - except InvalidRequestError: - ''' Not in the group ''' - return False - if (role.role_status == 'approved' and role.role_type == 'sponsor') \ - or canAdminGroup(person, group, role): - return True - return False - -def isApproved(person, group): - ''' - Returns True if the user is an approved member of a group - ''' - try: - if person.group_roles[group.name].role_status == 'approved': - return True - except KeyError: - return False - return False - -def CLADone(person): - ''' - Returns True if the user has completed the CLA - ''' - cla_done_group =config.get('cla_done_group') - try: - if person.group_roles[cla_done_group].role_status == 'approved': - return True - except KeyError: - return False - return False - -def canEditUser(person, target): - ''' - Returns True if the user has privileges to edit the target user - ''' - if person == target: - return True - elif isAdmin(person): - return True - return False - -def canCreateGroup(person, group): - ''' - Returns True if the user can create groups - ''' - # Should groupname restrictions go here? - if isAdmin(person): - return True - return False - -def canEditGroup(person, group): - ''' - Returns True if the user can edit the group - ''' - if canAdminGroup(person, group): - return True - return False - -def canViewGroup(person, group): - ''' - Returns True if the user can view the group - ''' - # If the group matched by privileged_view_groups, then - # only people that can admin the group can view it - privilegedViewGroups = config.get('privileged_view_groups') - if re.compile(privilegedViewGroups).match(group.name): - if not canAdminGroup(person, group): - return False - return True - -def canApplyGroup(person, group, applicant): - ''' - Returns True if the user can apply applicant to the group - ''' - # User must satisfy all dependencies to join. - # This is bypassed for people already in the group and for the - # owner of the group (when they initially make it). - prerequisite = group.prerequisite - # TODO: Make this raise more useful info. - if prerequisite: - if prerequisite not in applicant.approved_memberships: - turbogears.flash(_('%s membership required before application to this group is allowed') % prerequisite.name) - return False - # A user can apply themselves, and group sponsors can apply other people. - if (person == applicant) or \ - canSponsorGroup(person, group): - return True - return False - -def canSponsorUser(person, group, target): - ''' - Returns True if the user can sponsor target in the group - ''' - # This is just here in case we want to add more complex checks in the future - if canSponsorGroup(person, group): - return True - return False - -def canRemoveUser(person, group, target): - ''' - Returns True if the user can remove target from the group - ''' - # Only administrators can remove administrators. - if canAdminGroup(target, group) and \ - not canAdminGroup(person, group): - return False - # A user can remove themself from a group if user_can_remove is 1 - # Otherwise, a sponsor can remove sponsors/users. - elif ((person == target) and (group.user_can_remove == True)) or \ - canSponsorGroup(person, group): - return True - return False - -def canUpgradeUser(person, group, target): - ''' - Returns True if the user can upgrade target in the group - ''' - # Group admins can upgrade anybody. - # The controller should handle the case where the target - # is already a group admin. - if canAdminGroup(person, group): - return True - # Sponsors can only upgrade non-sponsors (i.e. normal users) - # TODO: Don't assume that canSponsorGroup means that the user is a sponsor - elif canSponsorGroup(person, group) and \ - not canSponsorGroup(target, group): - return True - return False - -def canDowngradeUser(person, group, target): - ''' - Returns True if the user can downgrade target in the group - ''' - # Group admins can downgrade anybody. - if canAdminGroup(person, group): - return True - # Sponsors can only downgrade sponsors. - # The controller should handle the case where the target - # is already a normal user. - elif canSponsorGroup(person, group) and \ - not canAdminGroup(person, group): - return True - return False - diff --git a/fas/fas/cla.py b/fas/fas/cla.py deleted file mode 100644 index c6aa636..0000000 --- a/fas/fas/cla.py +++ /dev/null @@ -1,120 +0,0 @@ -import turbogears -from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler -from turbogears.database import session - -import cherrypy - -from datetime import datetime -import re -import turbomail -from genshi.template.plugin import TextTemplateEnginePlugin - -from fas.model import People -from fas.model import Log -from fas.auth import * - -class CLA(controllers.Controller): - - def __init__(self): - '''Create a CLA Controller.''' - - @identity.require(turbogears.identity.not_anonymous()) - @expose(template="fas.templates.cla.index") - def index(self): - '''Display the CLAs (and accept/do not accept buttons)''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - if not person.telephone or not person.postal_address: - turbogears.flash('Postal Address and telephone number are required to complete the cla, please fill them out') - turbogears.redirect('/user/edit/%s' % username) - cla = CLADone(person) - return dict(cla=cla, person=person, date=datetime.utcnow().ctime()) - - def jsonRequest(self): - return 'tg_format' in cherrypy.request.params and \ - cherrypy.request.params['tg_format'] == 'json' - - @expose(template="fas.templates.error") - def error(self, tg_errors=None): - '''Show a friendly error message''' - if not tg_errors: - turbogears.redirect('/') - return dict(tg_errors=tg_errors) - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template="genshi-text:fas.templates.cla.cla", format="text", content_type='text/plain; charset=utf-8') - def text(self, type=None): - '''View CLA as text''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - return dict(person=person, date=datetime.utcnow().ctime()) - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template="genshi-text:fas.templates.cla.cla", format="text", content_type='text/plain; charset=utf-8') - def download(self, type=None): - '''Download CLA''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - return dict(person=person, date=datetime.utcnow().ctime()) - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template="fas.templates.cla.index") - def send(self, agree=False): - '''Send CLA''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - if CLADone(person): - turbogears.flash(_('You have already completed the CLA.')) - turbogears.redirect('/cla/') - return dict() - if not agree: - turbogears.flash(_("You have not completed the CLA.")) - turbogears.redirect('/user/view/%s' % person.username) - if not person.telephone or \ - not person.postal_address: - turbogears.flash(_('To complete the CLA, we must have your telephone number and postal address. Please ensure they have been filled out.')) - turbogears.redirect('/user/edit/%s' % username) - groupname = config.get('cla_fedora_group') - group = Groups.by_name(groupname) - try: - # Everything is correct. - person.apply(group, person) # Apply... - session.flush() - person.sponsor(group, person) # Sponsor! - except: - # TODO: If apply succeeds and sponsor fails, the user has - # to remove themselves from the CLA group before they can - # complete the CLA and go through the above try block again. - turbogears.flash(_("You could not be added to the '%s' group.") % group.name) - turbogears.redirect('/cla/') - return dict() - else: - dt = datetime.utcnow() - Log(author_id=person.id, description='Completed CLA', changetime=dt) - message = turbomail.Message(config.get('accounts_email'), config.get('legal_cla_email'), 'Fedora ICLA completed') - message.plain = ''' -Fedora user %(username)s has completed an ICLA (below). -Username: %(username)s -Email: %(email)s -Date: %(date)s - -=== CLA === - -''' % {'username': person.username, - 'human_name': person.human_name, - 'email': person.email, - 'postal_address': person.postal_address, - 'telephone': person.telephone, - 'facsimile': person.facsimile, - 'date': dt.ctime(),} - # Sigh.. if only there were a nicer way. - plugin = TextTemplateEnginePlugin() - message.plain += plugin.render(template='fas.templates.cla.cla', info=dict(person=person), format='text') - turbomail.enqueue(message) - turbogears.flash(_("You have successfully completed the CLA. You are now in the '%s' group.") % group.name) - turbogears.redirect('/user/view/%s' % person.username) - return dict() - diff --git a/fas/fas/commands.py b/fas/fas/commands.py deleted file mode 100644 index dbbaf2d..0000000 --- a/fas/fas/commands.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -"""This module contains functions called from console script entry points.""" - -import os -import sys - -import pkg_resources -pkg_resources.require("TurboGears") - -import turbogears -import cherrypy - -cherrypy.lowercase_api = True - -class ConfigurationError(Exception): - pass - -def start(): - '''Start the CherryPy application server.''' - setupdir = os.path.dirname(os.path.dirname(__file__)) - curdir = os.getcwd() - - # First look on the command line for a desired config file, - # if it's not on the command line, then look for 'setup.py' - # in the current directory. If there, load configuration - # from a file called 'dev.cfg'. If it's not there, the project - # is probably installed and we'll look first for a file called - # 'prod.cfg' in the current directory and then for a default - # config file called 'default.cfg' packaged in the egg. - if len(sys.argv) > 1: - configfile = sys.argv[1] - elif os.path.exists(os.path.join(setupdir, 'setup.py')) \ - and os.path.exists(os.path.join(setupdir, 'dev.cfg')): - configfile = os.path.join(setupdir, 'dev.cfg') - elif os.path.exists(os.path.join(curdir, 'fas.cfg')): - configfile = os.path.join(curdir, 'fas.cfg') - elif os.path.exists(os.path.join('/etc/fas.cfg')): - configfile = os.path.join('/etc/fas.cfg') - else: - try: - configfile = pkg_resources.resource_filename( - pkg_resources.Requirement.parse("fas"), - "config/default.cfg") - except pkg_resources.DistributionNotFound: - raise ConfigurationError("Could not find default configuration.") - - turbogears.update_config(configfile=configfile, - modulename="fas.config") - - from fas.controllers import Root - turbogears.start_server(Root()) diff --git a/fas/fas/config/.gitignore b/fas/fas/config/.gitignore deleted file mode 100644 index 55cae88..0000000 --- a/fas/fas/config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -app.cfg diff --git a/fas/fas/config/__init__.py b/fas/fas/config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fas/fas/config/app.cfg.in b/fas/fas/config/app.cfg.in deleted file mode 100644 index d22bcf3..0000000 --- a/fas/fas/config/app.cfg.in +++ /dev/null @@ -1,169 +0,0 @@ -[global] -# The settings in this file should not vary depending on the deployment -# environment. dev.cfg and prod.cfg are the locations for -# the different deployment settings. Settings in this file will -# be overridden by settings in those other files. - -# The commented out values below are the defaults - -# Database values -sqlalchemy.convert_unicode=True - -# VIEW - -# which view (template engine) to use if one is not specified in the -# template name -# tg.defaultview = "kid" -tg.defaultview = "genshi" - -# The following kid settings determine the settings used by the kid serializer. - -# One of (html|html-strict|xhtml|xhtml-strict|xml|json) -# kid.outputformat="html" -genshi.outputformat = "xhtml" - -# When @#%*( TG supports this.... -#genshi.outputformat = "html" -#genshi.default_doctype = "html-strict" - -# kid.encoding="utf-8" -genshi.encoding="utf-8" - -# The sitetemplate is used for overall styling of a site that -# includes multiple TurboGears applications -# tg.sitetemplate="" - -# Allow every exposed function to be called as json, -# tg.allow_json = False - -# Suppress the inclusion of the shipped MochiKit version, which is rather outdated. -# Attention: setting this to True and listing 'turbogears.mochikit' in 'tg.include_widgets' -# is a contradiction. This option will overrule the default-inclusion to prevent version -# mismatch bugs. -# tg.mochikit_suppress = True - -# List of Widgets to include on every page. -# for example ['turbogears.mochikit'] -# tg.include_widgets = [] - -# Set to True if the scheduler should be started -# tg.scheduler = False - -# Set to True to allow paginate decorator redirects when page number gets -# out of bound. Useful for getting the real page id in the url -# paginate.redirect_on_out_of_range = True - -# Set to True to allow paginate decorator redirects when last page is requested. -# This is useful for getting the real last page id in the url -# paginate.redirect_on_last_page = True - -# i18n -session_filter.on = True -i18n.run_template_filter = True -i18n.domain = 'fas' -i18n.locale_dir = '@LOCALEDIR@' - -# VISIT TRACKING -# Each visit to your application will be assigned a unique visit ID tracked via -# a cookie sent to the visitor's browser. -# -------------- - -# Enable Visit tracking -visit.on=True - -# Number of minutes a visit may be idle before it expires. -visit.timeout=20 - -# The name of the cookie to transmit to the visitor's browser. -# visit.cookie.name="tg-visit" - -# Domain name to specify when setting the cookie (must begin with . according to -# RFC 2109). The default (None) should work for most cases and will default to -# the machine to which the request was made. NOTE: localhost is NEVER a valid -# value and will NOT WORK. -# visit.cookie.domain=None - -# Specific path for the cookie -# visit.cookie.path="/" - -# The name of the VisitManager plugin to use for visitor tracking. -visit.manager="sqlalchemy" -#visit.manager="sqlobject" - -# Database class to use for visit tracking -visit.saprovider.model = "fas.model.Visit" -identity.saprovider.model.visit = "fas.model.VisitIdentity" -#visit.saprovider.model = "fedora.accounts.tgfas.visit_identity_table" -#visit.soprovider.model = "fas.model.Visit" - -#identity.saprovider.model.visit="fedora.accounts.tgfas.VisitIdentity" -#identity.saprovider.model= -#sqlalchemy.dburi='sqlite://' - -# IDENTITY -# General configuration of the TurboGears Identity management module -# -------- - -# Switch to turn on or off the Identity management module -identity.on=True - -# [REQUIRED] URL to which CherryPy will internally redirect when an access -# control check fails. If Identity management is turned on, a value for this -# option must be specified. -identity.failure_url="/login" - -identity.provider='safas3' -# identity.provider='sqlobject' - -# The names of the fields on the login form containing the visitor's user ID -# and password. In addition, the submit button is specified simply so its -# existence may be stripped out prior to passing the form data to the target -# controller. -# identity.form.user_name="user_name" -# identity.form.password="password" -# identity.form.submit="login" - -# What sources should the identity provider consider when determining the -# identity associated with a request? Comma separated list of identity sources. -# Valid sources: form, visit, http_auth -# identity.source="form,http_auth,visit" - -# SqlAlchemyIdentityProvider -# Configuration options for the default IdentityProvider -# ------------------------- - -# The classes you wish to use for your Identity model. Remember to not use reserved -# SQL keywords for class names (at least unless you specify a different table -# name using sqlmeta). -identity.saprovider.model.user="fas.model.People" -identity.saprovider.model.group="fas.model.Groups" -#identity.saprovider.model.permission="fas.model.Visit" - -# The password encryption algorithm used when comparing passwords against what's -# stored in the database. Valid values are 'md5' or 'sha1'. If you do not -# specify an encryption algorithm, passwords are expected to be clear text. -# The SqlAlchemyProvider *will* encrypt passwords supplied as part of your login -# form. If you set the password through the password property, like: -# my_user.password = 'secret' -# the password will be encrypted in the database, provided identity is up and -# running, or you have loaded the configuration specifying what encryption to -# use (in situations where identity may not yet be running, like tests). - -# identity.saprovider.encryption_algorithm=None - -# compress the data sends to the web browser -# [/] -# gzip_filter.on = TrueNote: in the SVG XML, change the 'interviewee-name' text to be the name of whichever in -# gzip_filter.mime_types = ["application/x-javascript", "text/javascript", "text/html", "text/css", "text/plain"] -[/robots.txt] -static_filter.on = True -static_filter.dir = "@DATADIR@/static/robots.txt" - -[/static] -static_filter.on = True -static_filter.dir = "@DATADIR@/static" - -[/favicon.ico] -static_filter.on = True -static_filter.file = "@DATADIR@/static/images/favicon.ico" - diff --git a/fas/fas/config/log.cfg b/fas/fas/config/log.cfg deleted file mode 100644 index ce776f8..0000000 --- a/fas/fas/config/log.cfg +++ /dev/null @@ -1,29 +0,0 @@ -# LOGGING -# Logging is often deployment specific, but some handlers and -# formatters can be defined here. - -[logging] -[[formatters]] -[[[message_only]]] -format='*(message)s' - -[[[full_content]]] -format='*(asctime)s *(name)s *(levelname)s *(message)s' - -[[handlers]] -[[[debug_out]]] -class='StreamHandler' -level='DEBUG' -args='(sys.stdout,)' -formatter='full_content' - -[[[access_out]]] -class='StreamHandler' -level='INFO' -args='(sys.stdout,)' -formatter='message_only' - -[[[error_out]]] -class='StreamHandler' -level='ERROR' -args='(sys.stdout,)' diff --git a/fas/fas/controllers.py b/fas/fas/controllers.py deleted file mode 100644 index 40d412d..0000000 --- a/fas/fas/controllers.py +++ /dev/null @@ -1,150 +0,0 @@ -from turbogears import controllers, expose, config -from model import * -from turbogears import identity, redirect, widgets, validate, validators, error_handler -from cherrypy import request, response - -from turbogears import exception_handler -import turbogears -import cherrypy -import time - -from fas.user import User -from fas.group import Group -from fas.cla import CLA -from fas.json_request import JsonRequest -from fas.help import Help -from fas.auth import * -from fas.util import available_languages -#from fas.openid_fas import OpenID - -import os -import sys -reload(sys) -sys.setdefaultencoding('utf-8') - -def get_locale(locale=None): - if locale: - return locale - try: - return turbogears.identity.current.user.locale - except AttributeError: - return turbogears.i18n.utils._get_locale() - -config.update({'i18n.get_locale': get_locale}) - -def add_custom_stdvars(vars): - return vars.update({'gettext': _, "lang": get_locale(), 'available_languages': available_languages()}) - -turbogears.view.variable_providers.append(add_custom_stdvars) - - -# from fas import json -# import logging -# log = logging.getLogger("fas.controllers") - -#TODO: Appropriate flash icons for errors, etc. -# mmcgrath wonders if it will be handy to expose an encrypted mailer with fas over json for our apps - -class Root(controllers.RootController): - - user = User() - group = Group() - cla = CLA() - json = JsonRequest() - help = Help() - #openid = OpenID() - - # TODO: Find a better place for this. - os.environ['GNUPGHOME'] = config.get('gpghome') - - @expose(template="fas.templates.welcome", allow_json=True) - def index(self): - if turbogears.identity.not_anonymous(): - if 'tg_format' in request.params \ - and request.params['tg_format'] == 'json': - # redirects don't work with JSON calls. This is a bit of a - # hack until we can figure out something better. - return dict() - turbogears.redirect('/home') - return dict(now=time.ctime()) - - @expose(template="fas.templates.home", allow_json=True) - @identity.require(identity.not_anonymous()) - def home(self): - user_name = turbogears.identity.current.user_name - person = People.by_username(user_name) - cla = CLADone(person) - return dict(person=person, cla=cla) - - @expose(template="fas.templates.about") - def about(self): - return dict() - - @expose(template="fas.templates.login", allow_json=True) - def login(self, forward_url=None, previous_url=None, *args, **kwargs): - '''Page to become authenticated to the Account System. - - This shows a small login box to type in your username and password - from the Fedora Account System. - - Arguments: - :forward_url: The url to send to once authentication succeeds - :previous_url: The url that sent us to the login page - ''' - if forward_url == '.': - forward_url = turbogears.url('/../home') - if not identity.current.anonymous \ - and identity.was_login_attempted() \ - and not identity.get_identity_errors(): - # User is logged in - turbogears.flash(_('Welcome, %s') % People.by_username(turbogears.identity.current.user_name).human_name) - if 'tg_format' in request.params \ - and request.params['tg_format'] == 'json': - # When called as a json method, doesn't make any sense to - # redirect to a page. Returning the logged in identity - # is better. - return dict(user = identity.current.user) - if not forward_url: - forward_url = turbogears.url('/') - raise redirect(forward_url) - - forward_url=None - previous_url= request.path - - if identity.was_login_attempted(): - msg=_("The credentials you supplied were not correct or " - "did not grant access to this resource.") - elif identity.get_identity_errors(): - msg=_("You must provide your credentials before accessing " - "this resource.") - else: - msg=_("Please log in.") - forward_url= '.' - - cherrypy.response.status=403 - return dict(message=msg, previous_url=previous_url, logging_in=True, - original_parameters=request.params, - forward_url=forward_url) - - @expose(allow_json=True) - def logout(self): - identity.current.logout() - turbogears.flash(_('You have successfully logged out.')) - if 'tg_format' in request.params \ - and request.params['tg_format'] == 'json': - # When called as a json method, doesn't make any sense to - # redirect to a page. Returning the logged in identity - # is better. - return dict(status=True) - raise redirect('/') - - @expose() - def language(self, locale): - if locale not in available_languages(): - turbogears.flash(_('The language \'%s\' is not available.') % locale) - redirect(request.headers.get("Referer", "/")) - return dict() - turbogears.i18n.set_session_locale(locale) - redirect(request.headers.get("Referer", "/")) - return dict() - diff --git a/fas/fas/feeds.py b/fas/fas/feeds.py deleted file mode 100644 index 93a64c8..0000000 --- a/fas/fas/feeds.py +++ /dev/null @@ -1,17 +0,0 @@ -import urllib -from xml.dom import minidom - - -class Koji: - def __init__(self, userName, url='http://publictest8/koji/recentbuilds?user='): - buildFeed = minidom.parse(urllib.urlopen(url + userName)) - try: - self.userLink = buildFeed.getElementsByTagName('link')[0].childNodes[0].data - self.builds = {} - for build in buildFeed.getElementsByTagName('item'): - link = build.getElementsByTagName('link')[0].childNodes[0].data - self.builds[link] = {} - self.builds[link]['title'] = build.getElementsByTagName('title')[0].childNodes[0].data - self.builds[link]['pubDate'] = build.getElementsByTagName('pubDate')[0].childNodes[0].data - except IndexError: - return diff --git a/fas/fas/group.py b/fas/fas/group.py deleted file mode 100644 index d4edfc7..0000000 --- a/fas/fas/group.py +++ /dev/null @@ -1,544 +0,0 @@ -import turbogears -from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler -from turbogears.database import session - -import cherrypy -import sqlalchemy - -import fas -from fas.auth import * -from fas.user import KnownUser - -import re -import turbomail - -class KnownGroup(validators.FancyValidator): - '''Make sure that a group already exists''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - try: - g = Groups.by_name(value) - except InvalidRequestError: - raise validators.Invalid(_("The group '%s' does not exist.") % value, value, state) - -class UnknownGroup(validators.FancyValidator): - '''Make sure that a group doesn't already exist''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - try: - g = Groups.by_name(value) - except InvalidRequestError: - pass - else: - raise validators.Invalid(_("The group '%s' already exists.") % value, value, state) - -class ValidGroupType(validators.FancyValidator): - '''Make sure that a group type is valid''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - if value not in ('system', 'bugzilla','cvs', 'bzr', 'git', \ - 'hg', 'mtn', 'svn', 'shell', 'torrent', 'tracker', \ - 'tracking', 'user'): - raise validators.Invalid(_("Invalid group type.") % value, value, state) - -class GroupCreate(validators.Schema): - - name = validators.All( - UnknownGroup, - validators.String(max=32, min=3), - validators.Regex(regex='^[a-z0-9\-_]+$'), - ) - display_name = validators.NotEmpty - owner = validators.All( - KnownUser, - validators.NotEmpty, - ) - prerequisite = KnownGroup - group_type = ValidGroupType - -class GroupSave(validators.Schema): - groupname = validators.All(KnownGroup, validators.String(max=32, min=3)) - display_name = validators.NotEmpty - owner = KnownUser - prerequisite = KnownGroup - group_type = ValidGroupType - -class GroupApply(validators.Schema): - groupname = KnownGroup - targetname = KnownUser - -class GroupSponsor(validators.Schema): - groupname = KnownGroup - targetname = KnownUser - -class GroupRemove(validators.Schema): - groupname = KnownGroup - targetname = KnownUser - -class GroupUpgrade(validators.Schema): - groupname = KnownGroup - targetname = KnownUser - -class GroupDowngrade(validators.Schema): - groupname = KnownGroup - targetname = KnownUser - -class GroupView(validators.Schema): - groupname = KnownGroup - -class GroupEdit(validators.Schema): - groupname = KnownGroup - -class GroupInvite(validators.Schema): - groupname = KnownGroup - -class GroupSendInvite(validators.Schema): - groupname = KnownGroup - target = validators.Email(not_empty=True, strip=True), - -#class findUser(widgets.WidgetsList): -# username = widgets.AutoCompleteField(label=_('Username'), search_controller='search', search_param='username', result_name='people') -# action = widgets.HiddenField(default='apply', validator=validators.String(not_empty=True)) -# groupname = widgets.HiddenField(validator=validators.String(not_empty=True)) -# -#findUserForm = widgets.ListForm(fields=findUser(), submit_text=_('Invite')) - -class Group(controllers.Controller): - - def __init__(self): - '''Create a Group Controller.''' - - @identity.require(turbogears.identity.not_anonymous()) - def index(self): - '''Perhaps show a nice explanatory message about groups here?''' - return dict() - - def jsonRequest(self): - return 'tg_format' in cherrypy.request.params and \ - cherrypy.request.params['tg_format'] == 'json' - - @expose(template="fas.templates.error") - def error(self, tg_errors=None): - '''Show a friendly error message''' - if not tg_errors: - turbogears.redirect('/') - return dict(tg_errors=tg_errors) - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupView()) - @error_handler(error) - @expose(template="fas.templates.group.view", allow_json=True) - def view(self, groupname): - '''View group''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - group = Groups.by_name(groupname) - - if not canViewGroup(person, group): - turbogears.flash(_("You cannot view '%s'") % group.name) - turbogears.redirect('/group/list') - return dict() - else: - return dict(group=group) - - @identity.require(turbogears.identity.not_anonymous()) - @expose(template="fas.templates.group.new") - def new(self): - '''Display create group form''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - - if not canCreateGroup(person, Groups.by_name(config.get('admingroup'))): - turbogears.flash(_('Only FAS adminstrators can create groups.')) - turbogears.redirect('/') - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupCreate()) - @error_handler(error) - @expose(template="fas.templates.group.new") - def create(self, name, display_name, owner, group_type, needs_sponsor=0, user_can_remove=1, prerequisite='', joinmsg=''): - '''Create a group''' - - groupname = name - person = People.by_username(turbogears.identity.current.user_name) - person_owner = People.by_username(owner) - - if not canCreateGroup(person, Groups.by_name(config.get('admingroup'))): - turbogears.flash(_('Only FAS adminstrators can create groups.')) - turbogears.redirect('/') - try: - owner = People.by_username(owner) - group = Groups() - group.name = name - group.display_name = display_name - group.owner_id = person_owner.id - group.group_type = group_type - group.needs_sponsor = bool(needs_sponsor) - group.user_can_remove = bool(user_can_remove) - if prerequisite: - prerequisite = Groups.by_name(prerequisite) - group.prerequisite = prerequisite - group.joinmsg = joinmsg - # Log here - session.flush() - except TypeError: - turbogears.flash(_("The group: '%s' could not be created.") % groupname) - return dict() - else: - try: - owner.apply(group, person) # Apply... - session.flush() - owner.sponsor(group, person) - owner.upgrade(group, person) - owner.upgrade(group, person) - except KeyError: - turbogears.flash(_("The group: '%(group)s' has been created, but '%(user)s' could not be added as a group administrator.") % {'group': group.name, 'user': owner.username}) - else: - turbogears.flash(_("The group: '%s' has been created.") % group.name) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupEdit()) - @error_handler(error) - @expose(template="fas.templates.group.edit") - def edit(self, groupname): - '''Display edit group form''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - group = Groups.by_name(groupname) - - if not canAdminGroup(person, group): - turbogears.flash(_("You cannot edit '%s'.") % group.name) - turbogears.redirect('/group/view/%s' % group.name) - return dict(group=group) - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupSave()) - @error_handler(error) - @expose(template="fas.templates.group.edit") - def save(self, groupname, display_name, owner, group_type, needs_sponsor=0, user_can_remove=1, prerequisite='', joinmsg=''): - '''Edit a group''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - group = Groups.by_name(groupname) - - if not canEditGroup(person, group): - turbogears.flash(_("You cannot edit '%s'.") % group.name) - turbogears.redirect('/group/view/%s' % group.name) - else: - try: - owner = People.by_username(owner) - group.display_name = display_name - group.owner = owner - group.group_type = group_type - group.needs_sponsor = bool(needs_sponsor) - group.user_can_remove = bool(user_can_remove) - if prerequisite: - prerequisite = Groups.by_name(prerequisite) - group.prerequisite = prerequisite - else: - group.prerequisite = None - group.joinmsg = joinmsg - # Log here - session.flush() - except: - turbogears.flash(_('The group details could not be saved.')) - else: - turbogears.flash(_('The group details have been saved.')) - turbogears.redirect('/group/view/%s' % group.name) - return dict(group=group) - - @identity.require(turbogears.identity.not_anonymous()) - @expose(template="fas.templates.group.list", allow_json=True) - def list(self, search='*'): - username = turbogears.identity.current.user_name - person = People.by_username(username) - - memberships = {} - groups = [] - re_search = re.sub(r'\*', r'%', search).lower() - results = Groups.query.filter(Groups.name.like(re_search)).order_by('name').all() - if self.jsonRequest(): - membersql = sqlalchemy.select([PersonRoles.c.person_id, PersonRoles.c.group_id, PersonRoles.c.role_type], PersonRoles.c.role_status=='approved').order_by(PersonRoles.c.group_id) - members = membersql.execute() - for member in members: - try: - memberships[member[1]].append({'person_id': member[0], 'role_type': member[2]}) - except KeyError: - memberships[member[1]]=[{'person_id': member[0], 'role_type': member[2]}] - for group in results: - if canViewGroup(person, group): - groups.append(group) - if not len(groups): - turbogears.flash(_("No Groups found matching '%s'") % search) - return dict(groups=groups, search=search, memberships=memberships) - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupApply()) - @error_handler(error) - @expose(template='fas.templates.group.view') - def apply(self, groupname, targetname=None): - '''Apply to a group''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - if not targetname: - target = person - else: - target = People.by_username(targetname) - group = Groups.by_name(groupname) - - if not canApplyGroup(person, group, target): - turbogears.flash(_('%(user)s can not apply to %(group)s.') % \ - {'user': target.username, 'group': group.name }) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - else: - try: - target.apply(group, person) - except fas.ApplyError, e: - turbogears.flash(_('%(user)s could not apply to %(group)s: %(error)s') % \ - {'user': target.username, 'group': group.name, 'error': e}) - turbogears.redirect('/group/view/%s' % group.name) - else: - # TODO: How do we handle gettext calls for these kinds of emails? - # TODO: CC to right place, put a bit more thought into how to most elegantly do this - # TODO: Maybe that @fedoraproject.org (and even -sponsors) should be configurable somewhere? - message = turbomail.Message(config.get('accounts_email'), '%(group)s-sponsors@%(host)s' % {'group': group.name, 'host': config.get('email_host')}, \ - "Fedora '%(group)s' sponsor needed for %(user)s" % {'user': target.username, 'group': group.name}) - url = config.get('base_url_filter.base_url') + '/group/edit/%s' % groupname - - message.plain = _(''' -Fedora user %(user)s, aka %(name)s <%(email)s> has requested -membership for %(applicant)s (%(applicant_name)s) in the %(group)s group and needs a sponsor. - -Please go to %(url)s to take action. -''') % {'user': person.username, 'name': person.human_name, 'applicant': target.username, 'applicant_name': target.human_name, 'email': person.email, 'url': url, 'group': group.name} - turbomail.enqueue(message) - turbogears.flash(_('%(user)s has applied to %(group)s!') % \ - {'user': target.username, 'group': group.name}) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupSponsor()) - @error_handler(error) - @expose(template='fas.templates.group.view') - def sponsor(self, groupname, targetname): - '''Sponsor user''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - target = People.by_username(targetname) - group = Groups.by_name(groupname) - - if not canSponsorUser(person, group, target): - turbogears.flash(_("You cannot sponsor '%s'") % target.username) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - else: - try: - target.sponsor(group, person) - except fas.SponsorError, e: - turbogears.flash(_("%(user)s could not be sponsored in %(group)s: %(error)s") % \ - {'user': target.username, 'group': group.name, 'error': e}) - turbogears.redirect('/group/view/%s' % group.name) - else: - import turbomail - message = turbomail.Message(config.get('accounts_email'), target.email, "Your Fedora '%s' membership has been sponsored" % group.name) - message.plain = _(''' -%(name)s <%(email)s> has sponsored you for membership in the %(group)s -group of the Fedora account system. If applicable, this change should -propagate into the e-mail aliases and CVS repository within an hour. - -%(joinmsg)s -''') % {'group': group.name, 'name': person.human_name, 'email': person.email, 'joinmsg': group.joinmsg} - turbomail.enqueue(message) - turbogears.flash(_("'%s' has been sponsored!") % target.human_name) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupRemove()) - @error_handler(error) - @expose(template='fas.templates.group.view') - def remove(self, groupname, targetname): - '''Remove user from group''' - # TODO: Add confirmation? - username = turbogears.identity.current.user_name - person = People.by_username(username) - target = People.by_username(targetname) - group = Groups.by_name(groupname) - - if not canRemoveUser(person, group, target): - turbogears.flash(_("You cannot remove '%(user)s' from '%(group)s'.") % {'user': target.username, 'group': group.name}) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - else: - try: - target.remove(group, target) - except fas.RemoveError, e: - turbogears.flash(_("%(user)s could not be removed from %(group)s: %(error)s") % \ - {'user': target.username, 'group': group.name, 'error': e}) - turbogears.redirect('/group/view/%s' % group.name) - else: - message = turbomail.Message(config.get('accounts_email'), target.email, "Your Fedora '%s' membership has been removed" % group.name) - message.plain = _(''' -%(name)s <%(email)s> has removed you from the '%(group)s' -group of the Fedora Accounts System This change is effective -immediately for new operations, and should propagate into the e-mail -aliases within an hour. -''') % {'group': group.name, 'name': person.human_name, 'email': person.email} - turbomail.enqueue(message) - turbogears.flash(_('%(name)s has been removed from %(group)s') % \ - {'name': target.username, 'group': group.name}) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupUpgrade()) - @error_handler(error) - @expose(template='fas.templates.group.view') - def upgrade(self, groupname, targetname): - '''Upgrade user in group''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - target = People.by_username(targetname) - group = Groups.by_name(groupname) - - if not canUpgradeUser(person, group, target): - turbogears.flash(_("You cannot upgrade '%s'") % target.username) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - else: - try: - target.upgrade(group, person) - except fas.UpgradeError, e: - turbogears.flash(_('%(name)s could not be upgraded in %(group)s: %(error)s') % \ - {'name': target.username, 'group': group.name, 'error': e}) - turbogears.redirect('/group/view/%s' % group.name) - else: - import turbomail - message = turbomail.Message(config.get('accounts_email'), target.email, "Your Fedora '%s' membership has been upgraded" % group.name) - # Should we make person.upgrade return this? - role = PersonRoles.query.filter_by(group=group, member=target).one() - status = role.role_type - message.plain = _(''' -%(name)s <%(email)s> has upgraded you to %(status)s status in the -'%(group)s' group of the Fedora Accounts System This change is -effective immediately for new operations, and should propagate -into the e-mail aliases within an hour. -''') % {'group': group.name, 'name': person.human_name, 'email': person.email, 'status': status} - turbomail.enqueue(message) - turbogears.flash(_('%s has been upgraded!') % target.username) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=GroupDowngrade()) - @error_handler(error) - @expose(template='fas.templates.group.view') - def downgrade(self, groupname, targetname): - '''Upgrade user in group''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - target = People.by_username(targetname) - group = Groups.by_name(groupname) - - if not canDowngradeUser(person, group, target): - turbogears.flash(_("You cannot downgrade '%s'") % target.username) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - else: - try: - target.downgrade(group, person) - except fas.DowngradeError, e: - turbogears.flash(_('%(name)s could not be downgraded in %(group)s: %(error)s') % \ - {'name': target.username, 'group': group.name, 'error': e}) - turbogears.redirect('/group/view/%s' % group.name) - else: - import turbomail - message = turbomail.Message(config.get('accounts_email'), target.email, "Your Fedora '%s' membership has been downgraded" % group.name) - role = PersonRoles.query.filter_by(group=group, member=target).one() - status = role.role_type - message.plain = _(''' -%(name)s <%(email)s> has downgraded you to %(status)s status in the -'%(group)s' group of the Fedora Accounts System This change is -effective immediately for new operations, and should propagate -into the e-mail aliases within an hour. -''') % {'group': group.name, 'name': person.human_name, 'email': person.email, 'status': status} - turbomail.enqueue(message) - turbogears.flash(_('%s has been downgraded!') % target.username) - turbogears.redirect('/group/view/%s' % group.name) - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template="genshi-text:fas.templates.group.dump", format="text", content_type='text/plain; charset=utf-8') - def dump(self, groupname=None): - username = turbogears.identity.current.user_name - person = People.by_username(username) - if not groupname: -# groupname = config.get('cla_done_group') - people = People.query.order_by('username').all() - else: - people = [] - groups = Groups.by_name(groupname) - for role in groups.approved_roles: - people.append(role.member) - if not canViewGroup(person, groups): - turbogears.flash(_("You cannot view '%s'") % group.name) - turbogears.redirect('/group/list') - return dict() - - return dict(people=people) - - @identity.require(identity.not_anonymous()) - @validate(validators=GroupInvite()) - @error_handler(error) - @expose(template='fas.templates.group.invite') - def invite(self, groupname): - username = turbogears.identity.current.user_name - person = People.by_username(username) - group = Groups.by_name(groupname) - - return dict(person=person, group=group) - - @identity.require(identity.not_anonymous()) - @validate(validators=GroupSendInvite()) - @error_handler(error) - @expose(template='fas.templates.group.invite') - def sendinvite(self, groupname, target): - import turbomail - username = turbogears.identity.current.user_name - person = People.by_username(username) - group = Groups.by_name(groupname) - - if isApproved(person, group): - message = turbomail.Message(person.email, target, _('Come join The Fedora Project!')) - message.plain = _(''' -%(name)s <%(email)s> has invited you to join the Fedora -Project! We are a community of users and developers who produce a -complete operating system from entirely free and open source software -(FOSS). %(name)s thinks that you have knowledge and skills -that make you a great fit for the Fedora community, and that you might -be interested in contributing. - -How could you team up with the Fedora community to use and develop your -skills? Check out http://fedoraproject.org/join-fedora for some ideas. -Our community is more than just software developers -- we also have a -place for you whether you're an artist, a web site builder, a writer, or -a people person. You'll grow and learn as you work on a team with other -very smart and talented people. - -Fedora and FOSS are changing the world -- come be a part of it!''') % {'name': person.human_name, 'email': person.email} - turbomail.enqueue(message) - turbogears.flash(_('Message sent to: %s') % target) - turbogears.redirect('/group/view/%s' % group.name) - else: - turbogears.flash(_("You are not in the '%s' group.") % group.name) - return dict(target=target, person=person, group=group) - diff --git a/fas/fas/help.py b/fas/fas/help.py deleted file mode 100644 index 208176d..0000000 --- a/fas/fas/help.py +++ /dev/null @@ -1,49 +0,0 @@ -import turbogears -from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler -from turbogears.database import session - -from fas.auth import * - -class Help(controllers.Controller): - help = { 'none' : [_('Error'), _('

We could not find that help item

')], - 'user_ircnick' : [_('IRC Nick (Optional)'), _('

IRC Nick is used to identify yourself on irc.freenode.net. Please register your nick on irc.freenode.net first, then fill this in so people can find you online when they need to

')], - 'user_email' : [_('Email (Required)'), _('

This email address should be your prefered email contact and will be used to send various official emails to. This is also where your @fedoraproject.org email will get forwarded

')], - 'user_human_name' : [_('Full Name (Required)'), _('

Your Human Name or "real life" name

')], - 'user_gpg_keyid' : [_('GPG Key ID'), _('

A GPG key is generally used to prove that a message or email came from you or to encrypt information so that only the recipients can read it. This can be used when a password reset is sent to your email.

')], - 'user_telephone' : [_('Telephone'), _('

Required in order to complete the CLA. Sometimes during a time of emergency someone from the Fedora Project may need to contact you. For more information see our Privacy Policy

')], - 'user_postal_address': [_('Postal Address'), _('

Required in order to complete the CLA. This should be a mailing address where you can be contacted. See our Privacy Policy about any concerns.

')], - 'user_timezone': [_('Timezone (Optional)'), _('

Please specify the time zone you are in.

')], - 'user_comments': [_('Comments (Optional)'), _('

Misc comments about yourself.

')], - 'user_account_status': [_('Account Status'), _('

Shows account status, possible values include

  • Valid
  • Disabled
  • Expired

')], - 'user_cla' : [_('CLA'), _('

In order to become a full Fedora contributor you must complete the Contributor License Agreement. This license is a legal agreement between you and Red Hat. Full status allows people to contribute content and code and is recommended for anyone interested in getting involved in the Fedora Project.

')], - 'user_ssh_key' : [_('Public SSH Key'), _('

Many resources require public key authentiaction to work. By uploading your public key to us, you can then log in to our servers. Type "man ssh-keygen" for more information on creating your key. Once created you will want to upload ~/.ssh/id_dsa.pub or ~/.ssh/id_rsa.pub

')], - 'user_locale': [_('Locale'), _('

For non-english speaking peoples this allows individuals to select which locale they are in.

')], - - 'group_apply': [_('Apply'), _('

Applying for a group is like applying for a job and it can certainly take a while to get in. Many groups have their own rules about how to actually get approved or sponsored. For more information on how the account system works see the about page.

')], - 'group_remove': [_('Remove'), _('''

Removing a person from a group will cause that user to no longer be in the group. They will need to re-apply to get in. Admins can remove anyone, Sponsors can remove users, users can't remove anyone.

''')], - 'group_upgrade': [_('Upgrade'), _('''

Upgrade a persons status in this group.

  • from user -> to sponsor
  • From sponsor -> administrator
  • administrators cannot be upgraded beyond administrator

''')], - 'group_downgrade': [_('Downgrade'), _('''

Downgrade a persons status in the group.

  • from administrator -> to sponsor
  • From sponsor -> user
  • users cannot be downgraded below user, you may want to remove them

''')], - 'group_approve': [_('Approve'), _('''

A sponsor or administrator can approve users to be in a group. Once the user has applied for the group, go to the group's page and click approve to approve the user.

''')], - 'group_sponsor': [_('Sponsor'), _('''

A sponsor or administrator can sponsor users to be in a gruop. Once the user has applied for the group, go to the group's page and click approve to sponsor the user. Sponsorship of a user implies that you are approving a user and may mentor and answer their questions as they come up.

''')], - 'group_user_add': [_('Add User'), _('''

Manually add a user to a group. Place their username in this field and click 'Add'

''')], - 'group_name': [_('Group Name'), _('''

The name of the group you'd like to create. It should be alphanumeric though '-'s are allowed

''')], - 'group_display_name': [_('Display Name'), _('''

More human readable name of the group

''')], - 'group_owner': [_('Group Owner'), _('''

The name of the owner who will run this group

''')], - 'group_type': [_('Group Type'), _('''

Normally it is safe to leave this blank. Though some values include 'tracking', 'shell', 'cvs', 'git', 'hg', 'svn', and 'mtn'. This value only really matters if the group is to end up getting shell access or commit access somewhere like fedorahosted.

''')], - 'group_needs_sponsor': [_('Needs Sponsor'), _('''

If your group requires sponsorship (recommended), this means that when a user is approved by a sponsor. That relationship is recorded in the account system. If user A sponsors user N, then in viewing the members of this group, people will know to contact user A about user N if something goes wrong. If this box is unchecked, this means that only approval is needed and no relationship is recorded about who did the approving

''')], - 'group_self_removal': [_('Self Removal'), _('''

Should users be able to remove themselves from this group without sponsor / admin intervention? (recommended yes)

''')], - 'group_prerequisite': [_('Must Belong To'), _('''

Before a user can join this group, they must belong to the group listed in this box. This value cannot be removed without administrative intervention, only changed. Recommended values are for the 'cla_done' group.

''')], - 'group_join_message': [_('Join Message'), _('''

This message will go out to users when they join the group. It should be informative and offer tips about what to do next. A description of the group would also be valuable here

''')], - 'gencert': [_('Client Side Cert'), _('''

The client side cert is generally used to grant access to upload packages to Fedora or for other authentication purposes like with koji. If you are not a package maintainer there is no need to worry about the client side cert

''')], - } - - def __init__(self): - '''Create a JsonRequest Controller.''' - - @expose(template="fas.templates.help") - def get_help(self, id='none'): - try: - helpItem = self.help[id] - except KeyError: - return dict(title=_('Error'), helpItem=[_('Error'), _('

We could not find that help item

')]) - return dict(help=helpItem) diff --git a/fas/fas/json_request.py b/fas/fas/json_request.py deleted file mode 100644 index 8e75438..0000000 --- a/fas/fas/json_request.py +++ /dev/null @@ -1,73 +0,0 @@ -import turbogears -from turbogears import controllers, expose, identity - -import sqlalchemy - -from fas.model import People -from fas.model import Groups -from fas.model import Log - -from fas.auth import * - -class JsonRequest(controllers.Controller): - def __init__(self): - """Create a JsonRequest Controller.""" - - @identity.require(turbogears.identity.not_anonymous()) - @expose("json", allow_json=True) - def index(self): - """Return a help message""" - return dict(help='This is a JSON interface.') - - @identity.require(turbogears.identity.not_anonymous()) - @expose("json", allow_json=True) - def person_by_id(self, id): - try: - person = People.by_id(id) - person.jsonProps = { - 'People': ('approved_memberships', 'unapproved_memberships') - } - return dict(success=True, person=person) - except InvalidRequestError: - return dict(success=False) - - @identity.require(turbogears.identity.not_anonymous()) - @expose("json", allow_json=True) - def person_by_username(self, username): - try: - person = People.by_username(username) - person.jsonProps = { - 'People': ('approved_memberships', 'unapproved_memberships') - } - return dict(success=True, person=person) - except InvalidRequestError: - return dict(success=False) - - @identity.require(turbogears.identity.not_anonymous()) - @expose("json", allow_json=True) - def group_by_id(self, id): - try: - group = Groups.by_id(id) - return dict(success=True, group=group) - except InvalidRequestError: - return dict(success=False) - - @identity.require(turbogears.identity.not_anonymous()) - @expose("json", allow_json=True) - def group_by_name(self, groupname): - try: - group = Groups.by_name(groupname) - return dict(success=True, group=group) - except InvalidRequestError: - return dict(success=False) - - @identity.require(turbogears.identity.not_anonymous()) - @expose("json", allow_json=True) - def user_id(self): - people = {} - peoplesql = sqlalchemy.select([People.c.id, People.c.username]) - persons = peoplesql.execute() - for person in persons: - people[person[0]] = person[1] - return dict(people=people) - diff --git a/fas/fas/model.py b/fas/fas/model.py deleted file mode 100644 index 57786f5..0000000 --- a/fas/fas/model.py +++ /dev/null @@ -1,470 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2008 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, modify, -# copy, or redistribute it subject to the terms and conditions of the GNU -# General Public License v.2. This program is distributed in the hope that it -# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the -# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. You should have -# received a copy of the GNU General Public License along with this program; -# if not, write to the Free Software Foundation, Inc., 51 Franklin Street, -# Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are -# incorporated in the source code or documentation are not subject to the GNU -# General Public License and may only be used or replicated with the express -# permission of Red Hat, Inc. -# -# Author(s): Toshio Kuratomi -# Ricky Zhou -# - -''' -Model for the Fedora Account System -''' -from datetime import datetime -import pytz -from turbogears.database import metadata, mapper, get_engine -# import some basic SQLAlchemy classes for declaring the data model -# (see http://www.sqlalchemy.org/docs/04/ormtutorial.html) -from sqlalchemy import Table, Column, ForeignKey -from sqlalchemy.orm import relation -# import some datatypes for table columns from SQLAlchemy -# (see http://www.sqlalchemy.org/docs/04/types.html for more) -from sqlalchemy import String, Unicode, Integer, DateTime -# A few sqlalchemy tricks: -# Allow viewing foreign key relations as a dictionary -from sqlalchemy.orm.collections import column_mapped_collection, attribute_mapped_collection -# Allow us to reference the remote table of a many:many as a simple list -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy import select, and_ - -from sqlalchemy.exceptions import InvalidRequestError - -from turbogears.database import session - -from turbogears import identity, config - -import turbogears - -from fedora.tg.json import SABase -import fas - -# Bind us to the database defined in the config file. -get_engine() - -# -# Tables Mapped from the DB -# - -PeopleTable = Table('people', metadata, autoload=True) -PersonRolesTable = Table('person_roles', metadata, autoload=True) - -ConfigsTable = Table('configs', metadata, autoload=True) -GroupsTable = Table('groups', metadata, autoload=True) -GroupRolesTable = Table('group_roles', metadata, autoload=True) -BugzillaQueueTable = Table('bugzilla_queue', metadata, autoload=True) -LogTable = Table('log', metadata, autoload=True) -RequestsTable = Table('requests', metadata, autoload=True) - -# -# Selects for filtering roles -# -ApprovedRolesSelect = PersonRolesTable.select(and_( - PeopleTable.c.id==PersonRolesTable.c.person_id, - PersonRolesTable.c.role_status=='approved')).alias('approved') -UnApprovedRolesSelect = PersonRolesTable.select(and_( - PeopleTable.c.id==PersonRolesTable.c.person_id, - PersonRolesTable.c.role_status!='approved')).alias('unapproved') - -# The identity schema -- These must follow some conventions that TG -# understands and are shared with other Fedora services via the python-fedora -# module. - -visits_table = Table('visit', metadata, - Column('visit_key', String(40), primary_key=True), - Column('created', DateTime, nullable=False, default=datetime.now(pytz.utc)), - Column('expiry', DateTime) -) - -visit_identity_table = Table('visit_identity', metadata, - Column('visit_key', String(40), ForeignKey('visit.visit_key'), - primary_key=True), - Column('user_id', Integer, ForeignKey('people.id'), index=True) -) - -# -# Mapped Classes -# - -class People(SABase): - '''Records for all the contributors to Fedora.''' - - @classmethod - def by_id(cls, id): - ''' - A class method that can be used to search users - based on their unique id - ''' - return cls.query.filter_by(id=id).one() - - @classmethod - def by_email_address(cls, email): - ''' - A class method that can be used to search users - based on their email addresses since it is unique. - ''' - return cls.query.filter_by(email=email).one() - - @classmethod - def by_username(cls, username): - ''' - A class method that permits to search users - based on their username attribute. - ''' - return cls.query.filter_by(username=username).one() - - # If we're going to do logging here, we'll have to pass the person that did the applying. - def apply(cls, group, requester): - ''' - Apply a person to a group - ''' - if group in cls.memberships: - raise fas.ApplyError, _('user is already in this group') - else: - role = PersonRoles() - role.role_status = 'unapproved' - role.role_type = 'user' - role.member = cls - role.group = group - - def upgrade(cls, group, requester): - ''' - Upgrade a user in a group - requester for logging purposes - ''' - if not group in cls.memberships: - raise fas.UpgradeError, _('user is not a member') - else: - role = PersonRoles.query.filter_by(member=cls, group=group).one() - if role.role_type == 'administrator': - raise fas.UpgradeError, _('administrators cannot be upgraded any further') - elif role.role_type == 'sponsor': - role.role_type = 'administrator' - elif role.role_type == 'user': - role.role_type = 'sponsor' - - def downgrade(cls, group, requester): - ''' - Downgrade a user in a group - requester for logging purposes - ''' - if not group in cls.memberships: - raise fas.DowngradeError, _('user is not a member') - else: - role = PersonRoles.query.filter_by(member=cls, group=group).one() - if role.role_type == 'user': - raise fas.DowngradeError, _('users cannot be downgraded any further') - elif role.role_type == 'sponsor': - role.role_type = 'user' - elif role.role_type == 'administrator': - role.role_type = 'sponsor' - - def sponsor(cls, group, requester): - # If we want to do logging, this might be the place. - if not group in cls.unapproved_memberships: - raise fas.SponsorError, _('user is not an unapproved member') - role = PersonRoles.query.filter_by(member=cls, group=group).one() - role.role_status = 'approved' - role.sponsor = requester - role.approval = datetime.now(pytz.utc) - cls._handle_auto_add(group, requester) - - def _handle_auto_add(cls, group, requester): - """ - Handle automatic group approvals - """ - auto_approve_groups = config.get('auto_approve_groups') - associations = auto_approve_groups.split('|') - approve_group_queue = [] - for association in associations: - (groupname, approve_groups) = association.split(':', 1) - if groupname == group.name: - approve_group_queue.extend(approve_groups.split(',')) - for groupname in approve_group_queue: - approve_group = Groups.by_name(groupname) - cls._auto_add(approve_group, requester) - - def _auto_add(cls, group, requester): - """ - Ensure that a person is approved in a group - """ - try: - role = PersonRoles.query.filter_by(member=cls, group=group).one() - if role.role_status != 'approved': - role.role_status = 'approved' - role.sponsor = requester - role.approval = datetime.now(pytz.utc) - except InvalidRequestError: - role = PersonRoles() - role.role_status = 'approved' - role.role_type = 'user' - role.member = cls - role.group = group - - def remove(cls, group, requester): - if not group in cls.memberships: - raise fas.RemoveError, _('user is not a member') - else: - role = PersonRoles.query.filter_by(member=cls, group=group).one() - session.delete(role) - - def __repr__(cls): - return "User(%s,%s)" % (cls.username, cls.human_name) - - def __json__(self): - '''We want to make sure we keep a tight reign on sensistive information. - Thus we strip out certain information unless a user is an admin or the - current user. - - Current access restrictions - =========================== - - Anonymous users can see: - :id: The id in the account system and on the shell servers - :username: Username in FAS - :human_name: Human name of the person - :comments: Comments that the user leaves about themselves - :creation: Date this account was created - :ircnick: User's nickname on IRC - :last_seen: timestamp the user last logged into anything tied to - the account system - :status: Whether the user is active, inactive, on vacation, etc - :status_change: timestamp that the status was last updated - :locale: User's default locale for Fedora Services - :timezone: User's timezone - :latitude: Used for constructing maps of contributors - :longitude: Used for contructing maps of contributors - - Authenticated Users add: - :ssh_key: Public key for connecting to over ssh - :gpg_keyid: gpg key of the user - :affiliation: company or group the user wishes to identify with - :certificate_serial: serial number of the user's Fedora SSL - Certificate - - User Themselves add: - :password: hashed password to identify the user - :passwordtoken: used when the user needs to reset a password - :password_changed: last time the user changed the password - :postal_address: user's postal address - :telephone: user's telephone number - :facsimile: user's FAX number - - Admins gets access to this final field as well: - :internal_comments: Comments an admin wants to write about a user - - Note: There are a few other resources that are not located directly in - the People structure that you are likely to want to pass to consuming - code like email address and groups. Please see the documentation on - SABase.__json__() to find out how to set jsonProps to handle those. - ''' - props = super(People, self).__json__() - if not identity.in_group('admin'): - # Only admins can see internal_comments - del props['internal_comments'] - del props['emailtoken'] - del props['passwordtoken'] - if identity.current.anonymous: - # Anonymous users can't see any of these - del props['email'] - del props['unverified_email'] - del props['ssh_key'] - del props['gpg_keyid'] - del props['affiliation'] - del props['certificate_serial'] - del props['password'] - del props['password_changed'] - del props['postal_address'] - del props['telephone'] - del props['facsimile'] - # TODO: Are we still doing the fas-system thing? I think I saw a systems users somewhere... - elif not identity.current.user.username == self.username and 'fas-system' not in identity.current.groups: - # Only an admin or the user themselves can see these fields - del props['unverified_email'] - del props['password'] - del props['postal_address'] - del props['password_changed'] - del props['telephone'] - del props['facsimile'] - - return props - - memberships = association_proxy('roles', 'group') - approved_memberships = association_proxy('approved_roles', 'group') - unapproved_memberships = association_proxy('unapproved_roles', 'group') - -class PersonRoles(SABase): - '''Record people that are members of groups.''' - def __repr__(cls): - return "PersonRole(%s,%s,%s,%s)" % (cls.member.username, cls.group.name, cls.role_type, cls.role_status) - groupname = association_proxy('group', 'name') - -class Configs(SABase): - '''Configs for applications that a Fedora Contributor uses.''' - pass - -class Groups(SABase): - '''Group that people can belong to.''' - - @classmethod - def by_id(cls, id): - ''' - A class method that can be used to search groups - based on their unique id - ''' - return cls.query.filter_by(id=id).one() - - @classmethod - def by_email_address(cls, email): - ''' - A class method that can be used to search groups - based on their email addresses since it is unique. - ''' - return cls.query.filter_by(email=email).one() - - - @classmethod - def by_name(cls, name): - ''' - A class method that permits to search groups - based on their name attribute. - ''' - return cls.query.filter_by(name=name).one() - - def __repr__(cls): - return "Groups(%s,%s)" % (cls.name, cls.display_name) - - # People in the group - people = association_proxy('roles', 'member') - # Groups in the group - groups = association_proxy('group_members', 'member') - # Groups that this group belongs to - memberships = association_proxy('group_roles', 'group') - -class GroupRoles(SABase): - '''Record groups that are members of other groups.''' - pass - -class BugzillaQueue(SABase): - '''Queued up changes that need to be applied to bugzilla.''' - pass - -class Log(SABase): - '''Write simple logs of changes to the database.''' - pass - -class Requests(SABase): - ''' - Requests for certain resources may be restricted based on the user or host. - ''' - pass - -# -# Classes for mapping arbitrary selectables (This is similar to a view in -# python rather than in the db -# - -class ApprovedRoles(PersonRoles): - '''Only display roles that are approved.''' - pass - -class UnApprovedRoles(PersonRoles): - '''Only show Roles that are not approved.''' - pass - -# -# Classes for the SQLAlchemy Visit Manager -# - -class Visit(SABase): - '''Track how many people are visiting the website. - - It doesn't currently make sense for us to track this here so we clear this - table of stale records every hour. - ''' - @classmethod - def lookup_visit(cls, visit_key): - return cls.query.get(visit_key) - -class VisitIdentity(SABase): - '''Associate a user with a visit cookie. - - This allows users to log in to app. - ''' - pass - -# -# set up mappers between tables and classes -# - -# -# mappers for filtering roles -# -mapper(ApprovedRoles, ApprovedRolesSelect, properties = { - 'group': relation(Groups, backref='approved_roles', lazy = False) - }) -mapper(UnApprovedRoles, UnApprovedRolesSelect, properties = { - 'group': relation(Groups, backref='unapproved_roles', lazy = False) - }) - -mapper(People, PeopleTable, properties = { - # This name is kind of confusing. It's to allow person.group_roles['groupname'] in order to make auth.py (hopefully) slightly faster. - 'group_roles': relation(PersonRoles, - collection_class = attribute_mapped_collection('groupname'), - primaryjoin = PeopleTable.c.id==PersonRolesTable.c.person_id), - 'approved_roles': relation(ApprovedRoles, backref='member', - primaryjoin = PeopleTable.c.id==ApprovedRoles.c.person_id), - 'unapproved_roles': relation(UnApprovedRoles, backref='member', - primaryjoin = PeopleTable.c.id==UnApprovedRoles.c.person_id) - }) -mapper(PersonRoles, PersonRolesTable, properties = { - 'member': relation(People, backref = 'roles', lazy = False, - primaryjoin=PersonRolesTable.c.person_id==PeopleTable.c.id), - 'group': relation(Groups, backref='roles', lazy = False, - primaryjoin=PersonRolesTable.c.group_id==GroupsTable.c.id), - 'sponsor': relation(People, uselist=False, - primaryjoin = PersonRolesTable.c.sponsor_id==PeopleTable.c.id) - }) -mapper(Configs, ConfigsTable, properties = { - 'person': relation(People, backref = 'configs') - }) -mapper(Groups, GroupsTable, properties = { - 'owner': relation(People, uselist=False, - primaryjoin = GroupsTable.c.owner_id==PeopleTable.c.id), - 'prerequisite': relation(Groups, uselist=False, - primaryjoin = GroupsTable.c.prerequisite_id==GroupsTable.c.id) - }) -# GroupRoles are complex because the group is a member of a group and thus -# is referencing the same table. -mapper(GroupRoles, GroupRolesTable, properties = { - 'member': relation(Groups, backref = 'group_roles', - primaryjoin = GroupsTable.c.id==GroupRolesTable.c.member_id), - 'group': relation(Groups, backref = 'group_members', - primaryjoin = GroupsTable.c.id==GroupRolesTable.c.group_id), - 'sponsor': relation(People, uselist=False, - primaryjoin = GroupRolesTable.c.sponsor_id==PeopleTable.c.id) - }) -mapper(BugzillaQueue, BugzillaQueueTable, properties = { - 'group': relation(Groups, backref = 'pending'), - 'person': relation(People, backref = 'pending'), - ### TODO: test to be sure SQLAlchemy only loads the backref on demand - 'author': relation(People, backref='changes') - }) -mapper(Requests, RequestsTable, properties = { - 'person': relation(People, backref='requests') - }) -mapper(Log, LogTable) - -# TurboGears Identity -mapper(Visit, visits_table) -mapper(VisitIdentity, visit_identity_table, - properties=dict(users=relation(People, backref='visit_identity'))) diff --git a/fas/fas/openid_fas.py b/fas/fas/openid_fas.py deleted file mode 100644 index f9ca8c7..0000000 --- a/fas/fas/openid_fas.py +++ /dev/null @@ -1,112 +0,0 @@ -import turbogears -from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler, config -from cherrypy import session - -import cherrypy - -from openid.server.server import Server as OpenIDServer -from openid.server.server import BROWSER_REQUEST_MODES -from openid.server.server import OPENID_PREFIX -from openid.store.filestore import FileOpenIDStore - -from fas.auth import * - -from fas.user import KnownUser - -class UserID(validators.Schema): - targetname = KnownUser - -class OpenID(controllers.Controller): - - def __init__(self): - '''Create a OpenID Controller.''' - store = FileOpenIDStore(config.get('openidstore')) - self.openid_server = OpenIDServer(store)#, turbogears.url('/openid/server')) - - @expose() - def index(self): - turbogears.redirect('/openid/about') - return dict() - - @expose(template="fas.templates.openid.about") - def about(self): - '''Display an explanatory message about the OpenID service''' - username = turbogears.identity.current.user_name - return dict(username=username) - - @expose(template="genshi-text:fas.templates.openid.auth", format="text", content_type='text/plain; charset=utf-8') - def server(self, **query): - '''Perform OpenID auth''' - openid_server = self.openid_server - openid_query = {} - openid_request = None - if not session.has_key('openid_trusted'): - session['openid_trusted'] = [] - if query.has_key('url') and query.has_key('trusted') and query['trusted'] == 'allow': - session['openid_trusted'].append(query['url']) - if query.has_key('openid'): - try: - for key in query['openid'].keys(): - openid_key = OPENID_PREFIX + key - openid_query[openid_key] = query['openid'][key] - openid_request = openid_server.decodeRequest(openid_query) - session['openid_request'] = openid_request - except KeyError: - turbogears.flash(_('The OpenID request could not be decoded.')) - elif session.has_key('openid_request'): - openid_request = session['openid_request'] - if openid_request is None: - turbogears.redirect('/openid/about') - return dict() - else: - openid_response = None - if openid_request.mode in BROWSER_REQUEST_MODES: - username = turbogears.identity.current.user_name; - url = None - if username is not None: - url = config.get('base_url') + turbogears.url('/openid/id/%s' % username) - if openid_request.identity == url: - if openid_request.trust_root in session['openid_trusted']: - openid_response = openid_request.answer(True) - elif openid_request.immediate: - openid_response = openid_request.answer(False, server_url=config.get('base_url') + turbogears.url('/openid/server')) - else: - if query.has_key('url') and not query.has_key('allow'): - openid_response = openid_request.answer(False, server_url=config.get('base_url') + turbogears.url('/openid/server')) - else: - turbogears.redirect('/openid/trusted', url=openid_request.trust_root) - elif openid_request.immediate: - openid_response = openid_request.answer(False, server_url=config.get('base_url') + turbogears.url('/openid/server')) - else: - turbogears.redirect('/openid/login') - return dict() - else: - openid_response = openid_server.handleRequest(openid_request) - web_response = openid_server.encodeResponse(openid_response) - for name, value in web_response.headers.items(): - cherrypy.response.headers[name] = value; - cherrypy.response.status = web_response.code - return dict(body=web_response.body) - - @identity.require(turbogears.identity.not_anonymous()) - @expose(template="fas.templates.openid.trusted") - def trusted(self, url): - '''Ask the user if they trust a site for OpenID authentication''' - return dict(url=url) - - @identity.require(turbogears.identity.not_anonymous()) - @expose() - def login(self): - '''This exists only to make the user login and then redirect to /openid/server''' - turbogears.redirect('/openid/server') - return dict() - - - @expose(template="fas.templates.openid.id") - @validate(validators=UserID()) - def id(self, username): - '''The "real" OpenID URL''' - person = People.by_username(username) - server = config.get('base_url') + turbogears.url('/openid/server') - return dict(person=person, server=server) - diff --git a/fas/fas/openssl_fas.py b/fas/fas/openssl_fas.py deleted file mode 100644 index 1681d22..0000000 --- a/fas/fas/openssl_fas.py +++ /dev/null @@ -1,82 +0,0 @@ -# Pretty much all copied from pyOpenSSL's certgen.py example and func's certs.py - -from OpenSSL import crypto -TYPE_RSA = crypto.TYPE_RSA -TYPE_DSA = crypto.TYPE_DSA - -def retrieve_key_from_file(keyfile): - fo = open(keyfile, 'r') - buf = fo.read() - keypair = crypto.load_privatekey(crypto.FILETYPE_PEM, buf) - return keypair - -def retrieve_cert_from_file(certfile): - fo = open(certfile, 'r') - buf = fo.read() - cert = crypto.load_certificate(crypto.FILETYPE_PEM, buf) - return cert - -def createKeyPair(type, bits): - """ - Create a public/private key pair. - - Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA - bits - Number of bits to use in the key - Returns: The public/private key pair in a PKey object - """ - pkey = crypto.PKey() - pkey.generate_key(type, bits) - return pkey - -def createCertRequest(pkey, digest="md5", **name): - """ - Create a certificate request. - - Arguments: pkey - The key to associate with the request - digest - Digestion method to use for signing, default is md5 - **name - The name of the subject of the request, possible - arguments are: - C - Country name - ST - State or province name - L - Locality name - O - Organization name - OU - Organizational unit name - CN - Common name - emailAddress - E-mail address - Returns: The certificate request in an X509Req object - """ - req = crypto.X509Req() - subj = req.get_subject() - - for (key,value) in name.items(): - setattr(subj, key, value) - - req.set_pubkey(pkey) - req.sign(pkey, digest) - return req - -def createCertificate(req, (issuerCert, issuerKey), serial, (notBefore, notAfter), digest="md5"): - """ - Generate a certificate given a certificate request. - - Arguments: req - Certificate reqeust to use - issuerCert - The certificate of the issuer - issuerKey - The private key of the issuer - serial - Serial number for the certificate - notBefore - Timestamp (relative to now) when the certificate - starts being valid - notAfter - Timestamp (relative to now) when the certificate - stops being valid - digest - Digest method to use for signing, default is md5 - Returns: The signed certificate in an X509 object - """ - cert = crypto.X509() - cert.set_serial_number(serial) - cert.gmtime_adj_notBefore(notBefore) - cert.gmtime_adj_notAfter(notAfter) - cert.set_issuer(issuerCert.get_subject()) - cert.set_subject(req.get_subject()) - cert.set_pubkey(req.get_pubkey()) - cert.sign(issuerKey, digest) - return cert - diff --git a/fas/fas/release.py b/fas/fas/release.py deleted file mode 100644 index 35e271d..0000000 --- a/fas/fas/release.py +++ /dev/null @@ -1,18 +0,0 @@ -''' -Release information about the Fedora Accounts System -''' - -VERSION = '0.8.1' -NAME = 'fas' -DESCRIPTION = 'The Fedora Account System' -LONG_DESCRIPTION = ''' -Manage the accounts of contributors to the Fedora Project. -''' -AUTHOR = 'Ricky Zhou, Mike McGrath, and Toshio Kuratomi' -EMAIL = 'fedora-infrastructure-list@fedoraproject.org' -COPYRIGHT = '2007-2008 Red Hat, Inc.' - -# if it's open source, you might want to specify these -URL = 'https://admin.fedoraproject.org/accounts/' -DOWNLOAD_URL = 'https://fas2.fedorahosted.org/' -LICENSE = 'GPLv2' diff --git a/fas/fas/safasprovider.py b/fas/fas/safasprovider.py deleted file mode 100644 index ac0220e..0000000 --- a/fas/fas/safasprovider.py +++ /dev/null @@ -1,219 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2007-2008 Red Hat, Inc. All rights reserved. -# -# This copyrighted material is made available to anyone wishing to use, modify, -# copy, or redistribute it subject to the terms and conditions of the GNU -# General Public License v.2. This program is distributed in the hope that it -# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the -# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. You should have -# received a copy of the GNU General Public License along with this program; -# if not, write to the Free Software Foundation, Inc., 51 Franklin Street, -# Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are -# incorporated in the source code or documentation are not subject to the GNU -# General Public License and may only be used or replicated with the express -# permission of Red Hat, Inc. -# -# Red Hat Author(s): Toshio Kuratomi -# - -''' -This plugin provides authentication of passwords against the Fedora Account -System. -''' - - - -from sqlalchemy.orm import class_mapper -from turbogears import config, identity -from turbogears.identity.saprovider import SqlAlchemyIdentity, \ - SqlAlchemyIdentityProvider -from turbogears.database import session -from turbogears.util import load_class - -import gettext -t = gettext.translation('python-fedora', '/usr/share/locale', fallback=True) -_ = t.ugettext - -import crypt - -import logging -log = logging.getLogger('turbogears.identity.safasprovider') - -try: - set, frozenset -except NameError: - from sets import Set as set, ImmutableSet as frozenset - -# Global class references -- -# these will be set when the provider is initialised. -user_class = None -visit_identity_class = None - -class SaFasIdentity(SqlAlchemyIdentity): - def __init__(self, visit_key, user=None): - super(SaFasIdentity, self).__init__(visit_key, user) - - def _get_user(self): - try: - return self._user - except AttributeError: - # User hasn't already been set - pass - # Attempt to load the user. After this code executes, there *WILL* be - # a _user attribute, even if the value is None. - ### TG: Difference: Can't use the inherited method b/c of global var - visit = visit_identity_class.query.filter_by(visit_key = self.visit_key).first() - if not visit: - self._user = None - return None - self._user = user_class.query.get(visit.user_id) - return self._user - user = property(_get_user) - - def _get_user_name(self): - if not self.user: - return None - ### TG: Difference: Different name for the field - return self.user.username - user_name = property(_get_user_name) - - def _get_groups(self): - try: - return self._groups - except AttributeError: - # Groups haven't been computed yet - pass - if not self.user: - self._groups = frozenset() - else: - ### TG: Difference. Our model has a many::many for people:groups - # And an association proxy that links them together - self._groups = frozenset([g.name for g in self.user.approved_memberships]) - return self._groups - groups = property(_get_groups) - - def logout(self): - ''' - Remove the link between this identity and the visit. - ''' - if not self.visit_key: - return - try: - ### TG: Difference: Can't inherit b/c this uses a global var - visit = visit_identity_class.query.filter_by(visit_key=self.visit_key).first() - session.delete(visit) - # Clear the current identity - anon = SqlAlchemyIdentity(None,None) - identity.set_current_identity(anon) - except: - pass - else: - session.flush() - -class SaFasIdentityProvider(SqlAlchemyIdentityProvider): - ''' - IdentityProvider that authenticates users against the fedora account system - ''' - def __init__(self): - global visit_identity_class - global user_class - - user_class_path = config.get("identity.saprovider.model.user", None) - user_class = load_class(user_class_path) - visit_identity_class_path = config.get("identity.saprovider.model.visit", None) - log.info(_("Loading: %(visitmod)s") % \ - {'visitmod': visit_identity_class_path}) - visit_identity_class = load_class(visit_identity_class_path) - - def create_provider_model(self): - ''' - Create the database tables if they don't already exist. - ''' - class_mapper(user_class).local_table.create(checkfirst=True) - class_mapper(visit_identity_class).local_table.create(checkfirst=True) - - def validate_identity(self, user_name, password, visit_key): - ''' - Look up the identity represented by user_name and determine whether the - password is correct. - - Must return either None if the credentials weren't valid or an object - with the following properties: - user_name: original user name - user: a provider dependant object (TG_User or similar) - groups: a set of group IDs - permissions: a set of permission IDs - ''' - user = user_class.query.filter_by(username=user_name).first() - if not user: - log.warning("No such user: %s", user_name) - return None - if not self.validate_password(user, user_name, password): - log.info("Passwords don't match for user: %s", user_name) - return None - - log.info("associating user (%s) with visit (%s)", user.username, - visit_key) - # Link the user to the visit - link = visit_identity_class.query.filter_by(visit_key=visit_key).first() - if not link: - link = visit_identity_class() - link.visit_key = visit_key - link.user_id = user.id - else: - link.user_id = user.id - session.flush() - return SaFasIdentity(visit_key, user) - - def validate_password(self, user, user_name, password): - ''' - Check the supplied user_name and password against existing credentials. - Note: user_name is not used here, but is required by external - password validation schemes that might override this method. - If you use SqlAlchemyIdentityProvider, but want to check the passwords - against an external source (i.e. PAM, LDAP, Windows domain, etc), - subclass SqlAlchemyIdentityProvider, and override this method. - - Arguments: - :user: User information. Not used. - :user_name: Given username. - :password: Given, plaintext password. - - Returns: True if the password matches the username. Otherwise False. - Can return False for problems within the Account System as well. - ''' - - return user.password == crypt.crypt(password, user.password) - - def load_identity(self, visit_key): - '''Lookup the principal represented by visit_key. - - Arguments: - :visit_key: The session key for whom we're looking up an identity. - - Must return an object with the following properties: - user_name: original user name - user: a provider dependant object (TG_User or similar) - groups: a set of group IDs - permissions: a set of permission IDs - ''' - return SaFasIdentity(visit_key) - - def anonymous_identity(self): - ''' - Must return an object with the following properties: - user_name: original user name - user: a provider dependant object (TG_User or similar) - groups: a set of group IDs - permissions: a set of permission IDs - ''' - - return SaFasIdentity(None) - - def authenticated_identity(self, user): - ''' - Constructs Identity object for user that has no associated visit_key. - ''' - return SaFasIdentity(None, user) diff --git a/fas/fas/static/images/balloons/balloon-0.png b/fas/fas/static/images/balloons/balloon-0.png deleted file mode 100644 index acf8e6a..0000000 Binary files a/fas/fas/static/images/balloons/balloon-0.png and /dev/null differ diff --git a/fas/fas/static/images/balloons/balloon-1.png b/fas/fas/static/images/balloons/balloon-1.png deleted file mode 100644 index 3441c36..0000000 Binary files a/fas/fas/static/images/balloons/balloon-1.png and /dev/null differ diff --git a/fas/fas/static/images/balloons/balloon-2.png b/fas/fas/static/images/balloons/balloon-2.png deleted file mode 100644 index 79de868..0000000 Binary files a/fas/fas/static/images/balloons/balloon-2.png and /dev/null differ diff --git a/fas/fas/static/images/balloons/balloon-3.png b/fas/fas/static/images/balloons/balloon-3.png deleted file mode 100644 index c8ca463..0000000 Binary files a/fas/fas/static/images/balloons/balloon-3.png and /dev/null differ diff --git a/fas/fas/static/images/balloons/button.png b/fas/fas/static/images/balloons/button.png deleted file mode 100644 index 40516bc..0000000 Binary files a/fas/fas/static/images/balloons/button.png and /dev/null differ diff --git a/fas/fas/static/images/balloons/icon.gif b/fas/fas/static/images/balloons/icon.gif deleted file mode 100644 index 79df285..0000000 Binary files a/fas/fas/static/images/balloons/icon.gif and /dev/null differ diff --git a/fas/fas/static/js/HelpBalloon.js b/fas/fas/static/js/HelpBalloon.js deleted file mode 100644 index cb8e7d8..0000000 --- a/fas/fas/static/js/HelpBalloon.js +++ /dev/null @@ -1,874 +0,0 @@ -// -// Copyright (c) 2008 Beau D. Scott | http://www.beauscott.com -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -/** - * HelpBalloon.js - * Prototype/Scriptaculous based help balloons / dialog balloons - * @version 1.5 - * @requires prototype.js - * @requires scriptaculous.js - * @author Beau D. Scott - */ -var HelpBalloon = Class.create(); -HelpBalloon.prototype = { - /** - * Instantiates the object - * @param {Object} options - * @see HelpBalloonOptions - * @constructor - */ - initialize: function(options) - { - /** - * Display and behavioral options - * @see HelpBalloonOptions - */ - this.options = new HelpBalloonOptions(); - Object.extend(this.options, options || {}); - - /** - * The local store of 'title'. Will change if the balloon is making a remote call - * unless options.title is specified - * @var {String} - * @private - */ - this.title = this.options.title; - - /** - * Display elements - * @var {Object} - * @private - */ - this._elements = new HelpBalloonElements(); - - /** - * The balloons visibility state. - * @var {Boolean} - * @private - */ - this.visible = false; - - /** - * Rendering status - * @var {Boolean} - * @private - */ - this.drawn = false; - - /** - * X/Y coordinate of icon at time of render - * @var {Array} - * @private - */ - this.renderXY = [0,0]; - - /** - * Stores the balloon coordinates - * @private - * @var {Object} - */ - this.balloonCoords = null; - - /** - * Balloon styling - * @private - * @var {Object} - */ - this.balloonStyle = { - 'position': 'absolute', - 'border': 'none', - 'display': 'none' - } - - /** - * Title Bar style - * @var {Object} - * @private - */ - this.titleStyle = { - 'color': 'black', - 'fontSize': '16px', - 'fontWeight': 'bold', - 'fontFamily': 'Verdana' - } - - /** - * Width,height of the balloons - * @private - * @var {Array} - */ - this.balloonDimensions = [0,0]; - - /** - * ID for object and Icon, Requires prototype.improvements.js - * @var {String} - */ - this.id = "HelpBalloon_" + Object.genGUID(); - - // - // Preload the balloon and button images so they're ready - // at render time - // - // 0 1 - // X - // 2 3 - // - for(var i = 0; i < 4; i++) - { - var balloon = new Element('img', { - src: this.options.balloonPrefix + i + this.options.balloonSuffix - }); - this._elements.balloons.push(balloon.src); - } - /** - * @private - */ - this.lastBalloon = balloon; - - this._elements.button = new Element('img', { - src: this.options.button - }); - - // - // Create the anchoring icon, or attach the balloon to the given icon element - // If a string is passed in, assume it's a URL, if it's an object, assume it's - // a DOM member. - // - if(typeof this.options.icon == 'string') - { - this._elements.icon = new Element('img', { - src: this.options.icon, - id: this.id + "_icon" - }); - this._elements.icon.setStyle('cursor', 'pointer'); - } - else - { - // Not a string given (most likely an object. Do not append the element - // Kind of a hack for now, but I'll fix it in the next version. - - this._elements.icon = this.options.icon; - this.options.returnElement = true; - } - - this._elements.icon._HelpBalloon = this; - - // - // Attach rendering events - // - - for(i = 0; i < this.options.useEvent.length; i++) - { - Event.observe(this._elements.icon, this.options.useEvent[i], this.toggle.bindAsEventListener(this)); - } - - this._elements.container = new Element('div', { - '_HelpBalloon': this - }); - - // - // If we are not relying on other javascript to attach the anchoring icon - // to the DOM, we'll just do where the script is called from. Default behavior. - // - // If you want to use external JavaScript to attach it to the DOM, attach this._elements.icon - // - if(!this.options.returnElement) - { - document.write(''); - var te = $(this.id); - var p = te.parentNode; - p.insertBefore(this._elements.icon, te); - p.removeChild(te); - } - }, - - /** - * Toggles the help balloon - * @param {Object} e Event - */ - toggle: function(e) - { - if(!e) e = window.event || {type: this.options.useEvent, target: this._elements.icon}; - var icon = Event.element(e); - - if(e.type == this.options.useEvent && !this.visible && icon == this._elements.icon) - this.show(); - else - this.hide(); - }, - - /** - * Triggers the balloon to appear - */ - show: function() - { - if(!this.drawn) this._draw(); - this._reposition(); - this._hideOtherHelps(); - - Effect.Appear(this._elements.container, { - duration: this.options.duration, - afterFinish: function(e){ - this._elements.container.setStyle('display', 'block'); - this._hideLowerElements(); - }.bindAsEventListener(this) - }); - this.visible = true; - Event.observe(window, 'resize', this._reposition.bindAsEventListener(this)); - }, - - /** - * Hides the balloon - */ - hide: function() - { - this._showLowerElements(); - Effect.Fade(this._elements.container, {duration: this.options.duration}); - - setTimeout(function(){ - this._elements.container.setStyle('display', 'none'); - }.bind(this), this.options.duration * 1000); - - this.visible = false; - Event.stopObserving(window, 'resize', this._reposition.bindAsEventListener(this)); - }, - - /** - * Redraws the balloon based on the current coordinates of the icon. - * @private - */ - _reposition: function() - { - this.balloonCoords = this._getXY(this._elements.icon); - //Horizontal and vertical offsets in relation to the icon's 0,0 position. - // Default is the middle of the object - var ho = this._elements.icon.offsetWidth / 2; - var vo = this._elements.icon.offsetHeight / 2; - - var offsets = this.options.anchorPosition.split(/\s+/gi); - for(var i = 0; i < offsets.length; i++) - { - switch(offsets[i].toLowerCase()) - { - case 'left': - ho = 0; - break; - case 'right': - ho = this._elements.icon.offsetWidth; - break; - case 'center': - ho = this._elements.icon.offsetWidth / 2; - break; - case 'top': - vo = 0; - break; - case 'middle': - vo = this._elements.icon.offsetHeight / 2; - break; - case 'bottom': - vo = this._elements.icon.offsetHeight; - break; - } - } - - this.balloonCoords.y += vo; - this.balloonCoords.x += ho; - - // - // Figure out what position to show based on available realestate - // 0 1 - // X - // 2 3 - // Number indicates position of corner opposite anchor - // - var pos = 1; - var offsetHeight = this.balloonCoords.y - this.balloonDimensions[1]; - if(offsetHeight < 0) - pos += 2; - - var offsetWidth = this.balloonCoords.x + this.balloonDimensions[0]; - var ww = Browser.isMSIE() ? document.body.clientWidth : window.outerWidth; - if(offsetWidth > ww) - pos -- ; - - var zx = 0; - var zy = 0; - - // - // 0 1 - // X - // 2 3 - // - switch(pos) - { - case 0: - zx = this.balloonCoords.x - this.balloonDimensions[0]; - zy = this.balloonCoords.y - this.balloonDimensions[1]; - break; - - case 1: - zx = this.balloonCoords.x; - zy = this.balloonCoords.y - this.balloonDimensions[1]; - break; - - case 2: - zx = this.balloonCoords.x - this.balloonDimensions[0]; - zy = this.balloonCoords.y; - break; - - case 3: - zx = this.balloonCoords.x; - zy = this.balloonCoords.y; - break; - } - var containerStyle = { - /*'backgroundRepeat': 'no-repeat', - 'backgroundColor': 'transparent', - 'backgroundPosition': 'top left',*/ - 'left' : zx + "px", - 'top' : zy + "px", - 'width' : this.balloonDimensions[0] + 'px', - 'height' : this.balloonDimensions[1] + 'px' - } - if(Browser.isMSIE()) - { - // - // Fix for IE alpha transparencies - // - if(this._elements.balloons[pos].toLowerCase().indexOf('.png') > -1) - { - Element.setStyle(this._elements.bgContainer, { - 'left' : '0px', - 'top' : '0px', - 'filter' : "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this._elements.balloons[pos] + "', sizingMethod='scale')", - 'width' : this.balloonDimensions[0] + 'px', - 'height' : this.balloonDimensions[1] + 'px', - 'position' : 'absolute' - }); - } - else - containerStyle['background'] = 'transparent url(' + this._elements.balloons[pos] + ') top left no-repeat'; - } - else - { - containerStyle['background'] = 'transparent url(' + this._elements.balloons[pos] + ') top left no-repeat'; - } - Element.setStyle(this._elements.container, containerStyle); - }, - - /** - * Render's the Balloon - * @private - */ - _draw: function() - { - Element.setStyle(this._elements.container, this.balloonStyle); - if(this.options.dataURL && (!this.drawn || !this.options.cacheRemoteContent)) - { - var cont = new Ajax.Request(this.options.dataURL, {asynchronous: false, method: this.options.method}); - // - // Expects the following XML format: - // - // My Title - // My content - // - // - var doHTML = false; - if(cont.transport.responseXML) - { - var xml = cont.transport.responseXML.getElementsByTagName('HelpBalloon')[0]; - - if(xml) - { - if(!this.options.title) - { - xmlTitle = xml.getElementsByTagName('title')[0]; - if(xmlTitle) this.title = xmlTitle.firstChild.nodeValue; - } - - xmlContent = xml.getElementsByTagName('content')[0]; - if(xmlContent) this.options.content = xmlContent.firstChild.nodeValue; - } - else - doHTML = true; - } - else - doHTML = true; - - if(doHTML) - { - // Attempt to get the title from a HTML tag, unless the title option has been set. If so, use that. - if(!this.options.title) - { - var htmlTitle = cont.transport.responseText.match(/\<title\>([^\<]+)\<\/title\>/gi); - if(htmlTitle) - { - htmlTitle = htmlTitle.toString().replace(/\<title\>|\<\/title\>/gi, ''); - this.title = htmlTitle; - } - } - this.options.content = cont.transport.responseText; - } - } - - this.balloonDimensions[0] = this.lastBalloon.width; - this.balloonDimensions[1] = this.lastBalloon.height; - - var contentDimensions = [ - this.balloonDimensions[0] - (2 * this.options.contentMargin), - this.balloonDimensions[1] - (2 * this.options.contentMargin) - ]; - - var buttonDimensions = [ - this._elements.button.width, - this._elements.button.height - ]; - - // - // Create all the elements on demand if they haven't been created yet - // - if(!this.drawn) - { - this._elements.inner = new Element('div'); - - this._elements.title = new Element('div'); - this._elements.inner.appendChild(this._elements.title); - - // PNG fix for IE - if(Browser.isMSIE() && this.options.button.toLowerCase().indexOf('.png') > -1) - { - this._elements.bgContainer = new Element('div'); - - // Have to create yet-another-child of container to house the background for IE... when it was set in - // the main container, it for some odd reason prevents child components from being clickable. - this._elements.container.appendChild(this._elements.bgContainer); - - this._elements.closer = new Element('div'); - this._elements.closer.setStyle('filter', - "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.options.button + "', sizingMethod='scale')"); - } - else - { - this._elements.closer = this._elements.button; - } - - Event.observe(this._elements.closer, 'click', this.toggle.bindAsEventListener(this)); - this._elements.inner.appendChild(this._elements.closer); - - this._elements.content = new Element('div'); - this._elements.inner.appendChild(this._elements.content); - - this._elements.container.appendChild(this._elements.inner); - - document.getElementsByTagName('body')[0].appendChild(this._elements.container); - - this.drawn = true; - } - - // Reset the title element and reappend the title value (could have changed with a new URL) - this._elements.title.innerHTML = ''; - this._elements.title.appendChild(document.createTextNode(this.title)); - - // Reset content value: - this._elements.content.innerHTML = this.options.content; - - // - // Reapply styling to components as values might have changed - // - - Element.setStyle(this._elements.inner, { - 'position': 'absolute', - 'top': this.options.contentMargin + 'px', - 'left': this.options.contentMargin + 'px', - 'width': contentDimensions[0] + 'px', - 'height': contentDimensions[1] + 'px' - }); - - Element.setStyle(this._elements.title, { - 'width': (contentDimensions[0] - buttonDimensions[0]) + 'px', - 'height': buttonDimensions[1] + 'px', - 'position': 'absolute', - 'overflow': 'hidden', - 'top': '0px', - 'left': '0px' - }); - - Element.setStyle(this._elements.title, this.titleStyle); - - Element.setStyle(this._elements.closer, { - 'width': buttonDimensions[0] + 'px', - 'height': buttonDimensions[1] + 'px', - 'cursor': 'pointer', - 'position': 'absolute', - 'top': '0px', - 'right': '0px' - }); - - Element.setStyle(this._elements.content, { - 'width': contentDimensions[0] + 'px', - 'height': (contentDimensions[1] - this._elements.button.height) + 'px', - 'overflow': 'auto', - 'position': 'absolute', - 'top': buttonDimensions[1] + 'px', - 'left': '0px', - 'fontFamily': 'verdana', - 'fontSize': '11px', - 'fontWeight': 'normal', - 'color': 'black' - }); - - }, - - /** - * Gets the current position of the obj - * @param {Element} element to get position of - * @return Object of (x, y, x2, y2) - */ - _getXY: function(obj) - { - var pos = Position.cumulativeOffset(obj) - var y = pos[1]; - var x = pos[0]; - var x2 = x + parseInt(obj.offsetWidth); - var y2 = y + parseInt(obj.offsetHeight); - return {'x':x, 'y':y, 'x2':x2, 'y2':y2}; - - }, - - /** - * Determins if the object is a child of the balloon element - * @param {Element} Element to check parentage - * @return {Boolean} - * @private - */ - _isChild: function(obj) - { - var i = 15; - do{ - if(obj == this._elements.container) - return true; - obj = obj.parentNode; - }while(obj && i--); - return false - }, - - /** - * Determines if the balloon is over this_obj object - * @param {Element} Object to look under - * @return {Boolean} - * @private - */ - _isOver: function(this_obj) - { - if(!this.visible) return false; - if(this_obj == this._elements.container || this._isChild(this_obj)) return false; - var this_coords = this._getXY(this_obj); - var that_coords = this._getXY(this._elements.container); - if( - ( - ( - (this_coords.x >= that_coords.x && this_coords.x <= that_coords.x2) - || - (this_coords.x2 >= that_coords.x && this_coords.x2 <= that_coords.x2) - ) - && - ( - (this_coords.y >= that_coords.y && this_coords.y <= that_coords.y2) - || - (this_coords.y2 >= that_coords.y && this_coords.y2 <= that_coords.y2) - ) - ) - - ){ - return true; - } - else - return false; - }, - - /** - * Restores visibility of elements under the balloon - * (For IE) - * TODO: suck yourself - * @private - */ - _showLowerElements: function() - { - if(this.options.hideUnderElementsInIE) - { - var elements = this._getWeirdAPIElements(); - for(var i = 0; i < elements.length; i++) - { - if(this._isOver(elements[i])) - { - if(elements[i].style.visibility != 'visible' && elements[i].hiddenBy == this) - { - elements[i].style.visibility = 'visible'; - elements[i].hiddenBy = null; - } - } - } - } - }, - - /** - * Hides elements below the balloon - * (For IE) - * @private - */ - _hideLowerElements: function() - { - if(this.options.hideUnderElementsInIE) - { - var elements = this._getWeirdAPIElements(); - for(var i = 0; i < elements.length; i++) - { - if(this._isOver(elements[i])) - { - if(elements[i].style.visibility != 'hidden') - { - elements[i].style.visibility = 'hidden'; - elements[i].hiddenBy = this; - } - } - } - } - }, - - /** - * Determines which elements need to be hidden - * (For IE) - * @return {Array} array of elements - */ - _getWeirdAPIElements: function() - { - if(!Browser.isMSIE()) return []; - var objs = ['select', 'input', 'object']; - var elements = []; - for(var i = 0; i < objs.length; i++) - { - var e = document.getElementsByTagName(objs[i]); - for(var j = 0; j < e.length; j++) - { - elements.push(e[j]); - } - } - return elements; - }, - - /** - * Hides the other visible help balloons - * @param {Event} e - */ - _hideOtherHelps: function(e) - { - if(!e) e = window.event; - var divs = document.getElementsByTagName('div'); - for(var i = 0; i < divs.length; i++) - { - if(divs[i]._HelpBalloon && divs[i]._HelpBalloon.visible && (divs[i] != this._elements.container)) - divs[i]._HelpBalloon.toggle(e); - } - } -}; - -/** - * HelpBalloonOptions - * Helper class for defining options for the HelpBalloon object - * @author Beau D. Scott <beau_scott@hotmail.com> - */ -var HelpBalloonOptions = Class.create(); -HelpBalloonOptions.prototype = { - /** - * @constructor - */ - initialize: function(){}, - /** - * For use with embedding this object into another. If true, the icon is not created - * and not appeneded to the DOM at construction. - * Default is false - * @var {Boolean} - */ - returnElement: false, - /** - * URL to the anchoring icon image file to use. This can also be a direct reference - * to an existing element if you're using that as your anchoring icon. - * @var {Object} - */ - icon: '/accounts/static/images/balloons/icon.gif', - /** - * Alt text of the help icon - * @var {String} - */ - altText: 'Click here for help with this topic.', - /** - * URL to pull the title/content XML - * @var {String} - */ - dataURL: null, - /** - * Static title of the balloon - * @var {String} - */ - title: null, - /** - * Static content of the balloon - * @var {String} - */ - content: null, - /** - * Show/Hide effect duration - * @var {Number} - */ - duration: 0.2, - /** - * The event type to listen for on the icon to show the balloon. - * Default 'click' - * @var {String} - */ - useEvent: ['click'], - /** - * Request method for dynamic content. (get, post) - * Default 'get' - * @var {String} - */ - method: 'get', - /** - * Flag indicating cache the request result. If this is false, every - * time the balloon is shown, it will retrieve the remote url and parse it - * before the balloon appears, updating the content. Otherwise, it will make - * the call once and use the same content with each subsequent showing. - * Default true - * @var {Boolean} - */ - cacheRemoteContent: true, - /** - * Vertical and horizontal margin of the content pane - * @var {Number} - */ - contentMargin: 35, - /** - * X coordinate of the closing button - * @var {Number} - */ - buttonX: 246, - /** - * Y coordinate of the closing button - * @var {Number} - */ - buttonY: 35, - /** - * Clossing button image path - * @var {String} - */ - button: '/accounts/static/images/balloons/button.png', - /** - * Balloon image path prefix. There are 4 button images, numerically named, starting with 0. - * 0, 1 - * 2, 3 - * (the number indicates the corner opposite the anchor (the pointing direction) - * @var {String} - */ - balloonPrefix: '/accounts/static/images/balloons/balloon-', - /** - * The image filename suffix, including the file extension - * @var {String} - */ - balloonSuffix: '.png', - /** - * Position of the balloon's anchor relative to the icon element. - * Combine one horizontal indicator (left, center, right) and one vertical indicator (top, middle, bottom). - * Default is 'center middle' - * @var {String} - */ - anchorPosition: 'center middle', - /** - * Flag indicating whether to hide the elements under the balloon in IE. - * Setting this to false can cause rendering issues in Internet Explorer - * as some elements appear on top of the balloon if they're not hidden. - * Default is true. - * @var {Boolean} - */ - hideUnderElementsInIE: true -}; - -/** - * HelpBalloonElements - * Helper class for defining elements for the HelpBalloon object - * @author Beau D. Scott <beau_scott@hotmail.com> - */ -var HelpBalloonElements = Class.create(); -HelpBalloonElements.prototype = { - /** - * @constructor - */ - initialize: function(){}, - /** - * Containing element of the balloon - * @var {Element} - */ - container: null, - /** - * Inner content container - * @var {Element} - */ - inner: null, - /** - * A reference to the anchoring element/icon - * @var {Element} - */ - icon: null, - /** - * Content container - * @var {Element} - */ - content: null, - /** - * Closing button element - * @var {Element} - */ - button: null, - /** - * The closer object. This can be the same as button, but could - * also be a div with a png loaded as the back ground, browser dependent. - * @var {Element} - */ - closer: null, - /** - * Title container - * @var {Element} - */ - title: null, - /** - * Background container (houses the balloon images - * @var {Element} - */ - bgContainer: null, - /** - * Array of balloon image references - * @var {Array} - */ - balloons: [] -}; diff --git a/fas/fas/static/js/effects.js b/fas/fas/static/js/effects.js deleted file mode 100644 index b8c0259..0000000 --- a/fas/fas/static/js/effects.js +++ /dev/null @@ -1,1122 +0,0 @@ -// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 - -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// Contributors: -// Justin Palmer (http://encytemedia.com/) -// Mark Pilgrim (http://diveintomark.org/) -// Martin Bialasinki -// -// script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ - -// converts rgb() and #xxx to #xxxxxx format, -// returns self (or first argument) if not convertable -String.prototype.parseColor = function() { - var color = '#'; - if (this.slice(0,4) == 'rgb(') { - var cols = this.slice(4,this.length-1).split(','); - var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); - } else { - if (this.slice(0,1) == '#') { - if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); - if (this.length==7) color = this.toLowerCase(); - } - } - return (color.length==7 ? color : (arguments[0] || this)); -}; - -/*--------------------------------------------------------------------------*/ - -Element.collectTextNodes = function(element) { - return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); - }).flatten().join(''); -}; - -Element.collectTextNodesIgnoreClass = function(element, className) { - return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? - Element.collectTextNodesIgnoreClass(node, className) : '')); - }).flatten().join(''); -}; - -Element.setContentZoom = function(element, percent) { - element = $(element); - element.setStyle({fontSize: (percent/100) + 'em'}); - if (Prototype.Browser.WebKit) window.scrollBy(0,0); - return element; -}; - -Element.getInlineOpacity = function(element){ - return $(element).style.opacity || ''; -}; - -Element.forceRerendering = function(element) { - try { - element = $(element); - var n = document.createTextNode(' '); - element.appendChild(n); - element.removeChild(n); - } catch(e) { } -}; - -/*--------------------------------------------------------------------------*/ - -var Effect = { - _elementDoesNotExistError: { - name: 'ElementDoesNotExistError', - message: 'The specified DOM element does not exist, but is required for this effect to operate' - }, - Transitions: { - linear: Prototype.K, - sinoidal: function(pos) { - return (-Math.cos(pos*Math.PI)/2) + 0.5; - }, - reverse: function(pos) { - return 1-pos; - }, - flicker: function(pos) { - var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; - return pos > 1 ? 1 : pos; - }, - wobble: function(pos) { - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; - }, - pulse: function(pos, pulses) { - pulses = pulses || 5; - return ( - ((pos % (1/pulses)) * pulses).round() == 0 ? - ((pos * pulses * 2) - (pos * pulses * 2).floor()) : - 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor()) - ); - }, - spring: function(pos) { - return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); - }, - none: function(pos) { - return 0; - }, - full: function(pos) { - return 1; - } - }, - DefaultOptions: { - duration: 1.0, // seconds - fps: 100, // 100= assume 66fps max. - sync: false, // true for combining - from: 0.0, - to: 1.0, - delay: 0.0, - queue: 'parallel' - }, - tagifyText: function(element) { - var tagifyStyle = 'position:relative'; - if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; - - element = $(element); - $A(element.childNodes).each( function(child) { - if (child.nodeType==3) { - child.nodeValue.toArray().each( function(character) { - element.insertBefore( - new Element('span', {style: tagifyStyle}).update( - character == ' ' ? String.fromCharCode(160) : character), - child); - }); - Element.remove(child); - } - }); - }, - multiple: function(element, effect) { - var elements; - if (((typeof element == 'object') || - Object.isFunction(element)) && - (element.length)) - elements = element; - else - elements = $(element).childNodes; - - var options = Object.extend({ - speed: 0.1, - delay: 0.0 - }, arguments[2] || { }); - var masterDelay = options.delay; - - $A(elements).each( function(element, index) { - new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); - }); - }, - PAIRS: { - 'slide': ['SlideDown','SlideUp'], - 'blind': ['BlindDown','BlindUp'], - 'appear': ['Appear','Fade'] - }, - toggle: function(element, effect) { - element = $(element); - effect = (effect || 'appear').toLowerCase(); - var options = Object.extend({ - queue: { position:'end', scope:(element.id || 'global'), limit: 1 } - }, arguments[2] || { }); - Effect[element.visible() ? - Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); - } -}; - -Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; - -/* ------------- core effects ------------- */ - -Effect.ScopedQueue = Class.create(Enumerable, { - initialize: function() { - this.effects = []; - this.interval = null; - }, - _each: function(iterator) { - this.effects._each(iterator); - }, - add: function(effect) { - var timestamp = new Date().getTime(); - - var position = Object.isString(effect.options.queue) ? - effect.options.queue : effect.options.queue.position; - - switch(position) { - case 'front': - // move unstarted effects after this effect - this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { - e.startOn += effect.finishOn; - e.finishOn += effect.finishOn; - }); - break; - case 'with-last': - timestamp = this.effects.pluck('startOn').max() || timestamp; - break; - case 'end': - // start effect after last queued effect has finished - timestamp = this.effects.pluck('finishOn').max() || timestamp; - break; - } - - effect.startOn += timestamp; - effect.finishOn += timestamp; - - if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) - this.effects.push(effect); - - if (!this.interval) - this.interval = setInterval(this.loop.bind(this), 15); - }, - remove: function(effect) { - this.effects = this.effects.reject(function(e) { return e==effect }); - if (this.effects.length == 0) { - clearInterval(this.interval); - this.interval = null; - } - }, - loop: function() { - var timePos = new Date().getTime(); - for(var i=0, len=this.effects.length;i<len;i++) - this.effects[i] && this.effects[i].loop(timePos); - } -}); - -Effect.Queues = { - instances: $H(), - get: function(queueName) { - if (!Object.isString(queueName)) return queueName; - - return this.instances.get(queueName) || - this.instances.set(queueName, new Effect.ScopedQueue()); - } -}; -Effect.Queue = Effect.Queues.get('global'); - -Effect.Base = Class.create({ - position: null, - start: function(options) { - function codeForEvent(options,eventName){ - return ( - (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') + - (options[eventName] ? 'this.options.'+eventName+'(this);' : '') - ); - } - if (options && options.transition === false) options.transition = Effect.Transitions.linear; - this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { }); - this.currentFrame = 0; - this.state = 'idle'; - this.startOn = this.options.delay*1000; - this.finishOn = this.startOn+(this.options.duration*1000); - this.fromToDelta = this.options.to-this.options.from; - this.totalTime = this.finishOn-this.startOn; - this.totalFrames = this.options.fps*this.options.duration; - - eval('this.render = function(pos){ '+ - 'if (this.state=="idle"){this.state="running";'+ - codeForEvent(this.options,'beforeSetup')+ - (this.setup ? 'this.setup();':'')+ - codeForEvent(this.options,'afterSetup')+ - '};if (this.state=="running"){'+ - 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ - 'this.position=pos;'+ - codeForEvent(this.options,'beforeUpdate')+ - (this.update ? 'this.update(pos);':'')+ - codeForEvent(this.options,'afterUpdate')+ - '}}'); - - this.event('beforeStart'); - if (!this.options.sync) - Effect.Queues.get(Object.isString(this.options.queue) ? - 'global' : this.options.queue.scope).add(this); - }, - loop: function(timePos) { - if (timePos >= this.startOn) { - if (timePos >= this.finishOn) { - this.render(1.0); - this.cancel(); - this.event('beforeFinish'); - if (this.finish) this.finish(); - this.event('afterFinish'); - return; - } - var pos = (timePos - this.startOn) / this.totalTime, - frame = (pos * this.totalFrames).round(); - if (frame > this.currentFrame) { - this.render(pos); - this.currentFrame = frame; - } - } - }, - cancel: function() { - if (!this.options.sync) - Effect.Queues.get(Object.isString(this.options.queue) ? - 'global' : this.options.queue.scope).remove(this); - this.state = 'finished'; - }, - event: function(eventName) { - if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); - if (this.options[eventName]) this.options[eventName](this); - }, - inspect: function() { - var data = $H(); - for(property in this) - if (!Object.isFunction(this[property])) data.set(property, this[property]); - return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>'; - } -}); - -Effect.Parallel = Class.create(Effect.Base, { - initialize: function(effects) { - this.effects = effects || []; - this.start(arguments[1]); - }, - update: function(position) { - this.effects.invoke('render', position); - }, - finish: function(position) { - this.effects.each( function(effect) { - effect.render(1.0); - effect.cancel(); - effect.event('beforeFinish'); - if (effect.finish) effect.finish(position); - effect.event('afterFinish'); - }); - } -}); - -Effect.Tween = Class.create(Effect.Base, { - initialize: function(object, from, to) { - object = Object.isString(object) ? $(object) : object; - var args = $A(arguments), method = args.last(), - options = args.length == 5 ? args[3] : null; - this.method = Object.isFunction(method) ? method.bind(object) : - Object.isFunction(object[method]) ? object[method].bind(object) : - function(value) { object[method] = value }; - this.start(Object.extend({ from: from, to: to }, options || { })); - }, - update: function(position) { - this.method(position); - } -}); - -Effect.Event = Class.create(Effect.Base, { - initialize: function() { - this.start(Object.extend({ duration: 0 }, arguments[0] || { })); - }, - update: Prototype.emptyFunction -}); - -Effect.Opacity = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - // make this work on IE on elements without 'layout' - if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) - this.element.setStyle({zoom: 1}); - var options = Object.extend({ - from: this.element.getOpacity() || 0.0, - to: 1.0 - }, arguments[1] || { }); - this.start(options); - }, - update: function(position) { - this.element.setOpacity(position); - } -}); - -Effect.Move = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - x: 0, - y: 0, - mode: 'relative' - }, arguments[1] || { }); - this.start(options); - }, - setup: function() { - this.element.makePositioned(); - this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); - this.originalTop = parseFloat(this.element.getStyle('top') || '0'); - if (this.options.mode == 'absolute') { - this.options.x = this.options.x - this.originalLeft; - this.options.y = this.options.y - this.originalTop; - } - }, - update: function(position) { - this.element.setStyle({ - left: (this.options.x * position + this.originalLeft).round() + 'px', - top: (this.options.y * position + this.originalTop).round() + 'px' - }); - } -}); - -// for backwards compatibility -Effect.MoveBy = function(element, toTop, toLeft) { - return new Effect.Move(element, - Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); -}; - -Effect.Scale = Class.create(Effect.Base, { - initialize: function(element, percent) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - scaleX: true, - scaleY: true, - scaleContent: true, - scaleFromCenter: false, - scaleMode: 'box', // 'box' or 'contents' or { } with provided values - scaleFrom: 100.0, - scaleTo: percent - }, arguments[2] || { }); - this.start(options); - }, - setup: function() { - this.restoreAfterFinish = this.options.restoreAfterFinish || false; - this.elementPositioning = this.element.getStyle('position'); - - this.originalStyle = { }; - ['top','left','width','height','fontSize'].each( function(k) { - this.originalStyle[k] = this.element.style[k]; - }.bind(this)); - - this.originalTop = this.element.offsetTop; - this.originalLeft = this.element.offsetLeft; - - var fontSize = this.element.getStyle('font-size') || '100%'; - ['em','px','%','pt'].each( function(fontSizeType) { - if (fontSize.indexOf(fontSizeType)>0) { - this.fontSize = parseFloat(fontSize); - this.fontSizeType = fontSizeType; - } - }.bind(this)); - - this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; - - this.dims = null; - if (this.options.scaleMode=='box') - this.dims = [this.element.offsetHeight, this.element.offsetWidth]; - if (/^content/.test(this.options.scaleMode)) - this.dims = [this.element.scrollHeight, this.element.scrollWidth]; - if (!this.dims) - this.dims = [this.options.scaleMode.originalHeight, - this.options.scaleMode.originalWidth]; - }, - update: function(position) { - var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); - if (this.options.scaleContent && this.fontSize) - this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); - this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); - }, - finish: function(position) { - if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); - }, - setDimensions: function(height, width) { - var d = { }; - if (this.options.scaleX) d.width = width.round() + 'px'; - if (this.options.scaleY) d.height = height.round() + 'px'; - if (this.options.scaleFromCenter) { - var topd = (height - this.dims[0])/2; - var leftd = (width - this.dims[1])/2; - if (this.elementPositioning == 'absolute') { - if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; - if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; - } else { - if (this.options.scaleY) d.top = -topd + 'px'; - if (this.options.scaleX) d.left = -leftd + 'px'; - } - } - this.element.setStyle(d); - } -}); - -Effect.Highlight = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); - this.start(options); - }, - setup: function() { - // Prevent executing on elements not in the layout flow - if (this.element.getStyle('display')=='none') { this.cancel(); return; } - // Disable background image during the effect - this.oldStyle = { }; - if (!this.options.keepBackgroundImage) { - this.oldStyle.backgroundImage = this.element.getStyle('background-image'); - this.element.setStyle({backgroundImage: 'none'}); - } - if (!this.options.endcolor) - this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); - if (!this.options.restorecolor) - this.options.restorecolor = this.element.getStyle('background-color'); - // init color calculations - this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); - this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); - }, - update: function(position) { - this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ - return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); - }, - finish: function() { - this.element.setStyle(Object.extend(this.oldStyle, { - backgroundColor: this.options.restorecolor - })); - } -}); - -Effect.ScrollTo = function(element) { - var options = arguments[1] || { }, - scrollOffsets = document.viewport.getScrollOffsets(), - elementOffsets = $(element).cumulativeOffset(), - max = (window.height || document.body.scrollHeight) - document.viewport.getHeight(); - - if (options.offset) elementOffsets[1] += options.offset; - - return new Effect.Tween(null, - scrollOffsets.top, - elementOffsets[1] > max ? max : elementOffsets[1], - options, - function(p){ scrollTo(scrollOffsets.left, p.round()) } - ); -}; - -/* ------------- combination effects ------------- */ - -Effect.Fade = function(element) { - element = $(element); - var oldOpacity = element.getInlineOpacity(); - var options = Object.extend({ - from: element.getOpacity() || 1.0, - to: 0.0, - afterFinishInternal: function(effect) { - if (effect.options.to!=0) return; - effect.element.hide().setStyle({opacity: oldOpacity}); - } - }, arguments[1] || { }); - return new Effect.Opacity(element,options); -}; - -Effect.Appear = function(element) { - element = $(element); - var options = Object.extend({ - from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), - to: 1.0, - // force Safari to render floated elements properly - afterFinishInternal: function(effect) { - effect.element.forceRerendering(); - }, - beforeSetup: function(effect) { - effect.element.setOpacity(effect.options.from).show(); - }}, arguments[1] || { }); - return new Effect.Opacity(element,options); -}; - -Effect.Puff = function(element) { - element = $(element); - var oldStyle = { - opacity: element.getInlineOpacity(), - position: element.getStyle('position'), - top: element.style.top, - left: element.style.left, - width: element.style.width, - height: element.style.height - }; - return new Effect.Parallel( - [ new Effect.Scale(element, 200, - { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], - Object.extend({ duration: 1.0, - beforeSetupInternal: function(effect) { - Position.absolutize(effect.effects[0].element) - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().setStyle(oldStyle); } - }, arguments[1] || { }) - ); -}; - -Effect.BlindUp = function(element) { - element = $(element); - element.makeClipping(); - return new Effect.Scale(element, 0, - Object.extend({ scaleContent: false, - scaleX: false, - restoreAfterFinish: true, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); - } - }, arguments[1] || { }) - ); -}; - -Effect.BlindDown = function(element) { - element = $(element); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping(); - } - }, arguments[1] || { })); -}; - -Effect.SwitchOff = function(element) { - element = $(element); - var oldOpacity = element.getInlineOpacity(); - return new Effect.Appear(element, Object.extend({ - duration: 0.4, - from: 0, - transition: Effect.Transitions.flicker, - afterFinishInternal: function(effect) { - new Effect.Scale(effect.element, 1, { - duration: 0.3, scaleFromCenter: true, - scaleX: false, scaleContent: false, restoreAfterFinish: true, - beforeSetup: function(effect) { - effect.element.makePositioned().makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); - } - }) - } - }, arguments[1] || { })); -}; - -Effect.DropOut = function(element) { - element = $(element); - var oldStyle = { - top: element.getStyle('top'), - left: element.getStyle('left'), - opacity: element.getInlineOpacity() }; - return new Effect.Parallel( - [ new Effect.Move(element, {x: 0, y: 100, sync: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 }) ], - Object.extend( - { duration: 0.5, - beforeSetup: function(effect) { - effect.effects[0].element.makePositioned(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); - } - }, arguments[1] || { })); -}; - -Effect.Shake = function(element) { - element = $(element); - var options = Object.extend({ - distance: 20, - duration: 0.5 - }, arguments[1] || {}); - var distance = parseFloat(options.distance); - var split = parseFloat(options.duration) / 10.0; - var oldStyle = { - top: element.getStyle('top'), - left: element.getStyle('left') }; - return new Effect.Move(element, - { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { - effect.element.undoPositioned().setStyle(oldStyle); - }}) }}) }}) }}) }}) }}); -}; - -Effect.SlideDown = function(element) { - element = $(element).cleanWhitespace(); - // SlideDown need to have the content of the element wrapped in a container element with fixed height! - var oldInnerBottom = element.down().getStyle('bottom'); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, - scaleFrom: window.opera ? 0 : 1, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makePositioned(); - effect.element.down().makePositioned(); - if (window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, - afterUpdateInternal: function(effect) { - effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping().undoPositioned(); - effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } - }, arguments[1] || { }) - ); -}; - -Effect.SlideUp = function(element) { - element = $(element).cleanWhitespace(); - var oldInnerBottom = element.down().getStyle('bottom'); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, window.opera ? 0 : 1, - Object.extend({ scaleContent: false, - scaleX: false, - scaleMode: 'box', - scaleFrom: 100, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makePositioned(); - effect.element.down().makePositioned(); - if (window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().show(); - }, - afterUpdateInternal: function(effect) { - effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().undoPositioned(); - effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); - } - }, arguments[1] || { }) - ); -}; - -// Bug in opera makes the TD containing this element expand for a instance after finish -Effect.Squish = function(element) { - return new Effect.Scale(element, window.opera ? 1 : 0, { - restoreAfterFinish: true, - beforeSetup: function(effect) { - effect.element.makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); - } - }); -}; - -Effect.Grow = function(element) { - element = $(element); - var options = Object.extend({ - direction: 'center', - moveTransition: Effect.Transitions.sinoidal, - scaleTransition: Effect.Transitions.sinoidal, - opacityTransition: Effect.Transitions.full - }, arguments[1] || { }); - var oldStyle = { - top: element.style.top, - left: element.style.left, - height: element.style.height, - width: element.style.width, - opacity: element.getInlineOpacity() }; - - var dims = element.getDimensions(); - var initialMoveX, initialMoveY; - var moveX, moveY; - - switch (options.direction) { - case 'top-left': - initialMoveX = initialMoveY = moveX = moveY = 0; - break; - case 'top-right': - initialMoveX = dims.width; - initialMoveY = moveY = 0; - moveX = -dims.width; - break; - case 'bottom-left': - initialMoveX = moveX = 0; - initialMoveY = dims.height; - moveY = -dims.height; - break; - case 'bottom-right': - initialMoveX = dims.width; - initialMoveY = dims.height; - moveX = -dims.width; - moveY = -dims.height; - break; - case 'center': - initialMoveX = dims.width / 2; - initialMoveY = dims.height / 2; - moveX = -dims.width / 2; - moveY = -dims.height / 2; - break; - } - - return new Effect.Move(element, { - x: initialMoveX, - y: initialMoveY, - duration: 0.01, - beforeSetup: function(effect) { - effect.element.hide().makeClipping().makePositioned(); - }, - afterFinishInternal: function(effect) { - new Effect.Parallel( - [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), - new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), - new Effect.Scale(effect.element, 100, { - scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, - sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) - ], Object.extend({ - beforeSetup: function(effect) { - effect.effects[0].element.setStyle({height: '0px'}).show(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); - } - }, options) - ) - } - }); -}; - -Effect.Shrink = function(element) { - element = $(element); - var options = Object.extend({ - direction: 'center', - moveTransition: Effect.Transitions.sinoidal, - scaleTransition: Effect.Transitions.sinoidal, - opacityTransition: Effect.Transitions.none - }, arguments[1] || { }); - var oldStyle = { - top: element.style.top, - left: element.style.left, - height: element.style.height, - width: element.style.width, - opacity: element.getInlineOpacity() }; - - var dims = element.getDimensions(); - var moveX, moveY; - - switch (options.direction) { - case 'top-left': - moveX = moveY = 0; - break; - case 'top-right': - moveX = dims.width; - moveY = 0; - break; - case 'bottom-left': - moveX = 0; - moveY = dims.height; - break; - case 'bottom-right': - moveX = dims.width; - moveY = dims.height; - break; - case 'center': - moveX = dims.width / 2; - moveY = dims.height / 2; - break; - } - - return new Effect.Parallel( - [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), - new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), - new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) - ], Object.extend({ - beforeStartInternal: function(effect) { - effect.effects[0].element.makePositioned().makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } - }, options) - ); -}; - -Effect.Pulsate = function(element) { - element = $(element); - var options = arguments[1] || { }; - var oldOpacity = element.getInlineOpacity(); - var transition = options.transition || Effect.Transitions.sinoidal; - var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; - reverser.bind(transition); - return new Effect.Opacity(element, - Object.extend(Object.extend({ duration: 2.0, from: 0, - afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } - }, options), {transition: reverser})); -}; - -Effect.Fold = function(element) { - element = $(element); - var oldStyle = { - top: element.style.top, - left: element.style.left, - width: element.style.width, - height: element.style.height }; - element.makeClipping(); - return new Effect.Scale(element, 5, Object.extend({ - scaleContent: false, - scaleX: false, - afterFinishInternal: function(effect) { - new Effect.Scale(element, 1, { - scaleContent: false, - scaleY: false, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().setStyle(oldStyle); - } }); - }}, arguments[1] || { })); -}; - -Effect.Morph = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - style: { } - }, arguments[1] || { }); - - if (!Object.isString(options.style)) this.style = $H(options.style); - else { - if (options.style.include(':')) - this.style = options.style.parseStyle(); - else { - this.element.addClassName(options.style); - this.style = $H(this.element.getStyles()); - this.element.removeClassName(options.style); - var css = this.element.getStyles(); - this.style = this.style.reject(function(style) { - return style.value == css[style.key]; - }); - options.afterFinishInternal = function(effect) { - effect.element.addClassName(effect.options.style); - effect.transforms.each(function(transform) { - effect.element.style[transform.style] = ''; - }); - } - } - } - this.start(options); - }, - - setup: function(){ - function parseColor(color){ - if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; - color = color.parseColor(); - return $R(0,2).map(function(i){ - return parseInt( color.slice(i*2+1,i*2+3), 16 ) - }); - } - this.transforms = this.style.map(function(pair){ - var property = pair[0], value = pair[1], unit = null; - - if (value.parseColor('#zzzzzz') != '#zzzzzz') { - value = value.parseColor(); - unit = 'color'; - } else if (property == 'opacity') { - value = parseFloat(value); - if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) - this.element.setStyle({zoom: 1}); - } else if (Element.CSS_LENGTH.test(value)) { - var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); - value = parseFloat(components[1]); - unit = (components.length == 3) ? components[2] : null; - } - - var originalValue = this.element.getStyle(property); - return { - style: property.camelize(), - originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), - targetValue: unit=='color' ? parseColor(value) : value, - unit: unit - }; - }.bind(this)).reject(function(transform){ - return ( - (transform.originalValue == transform.targetValue) || - ( - transform.unit != 'color' && - (isNaN(transform.originalValue) || isNaN(transform.targetValue)) - ) - ) - }); - }, - update: function(position) { - var style = { }, transform, i = this.transforms.length; - while(i--) - style[(transform = this.transforms[i]).style] = - transform.unit=='color' ? '#'+ - (Math.round(transform.originalValue[0]+ - (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + - (Math.round(transform.originalValue[1]+ - (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + - (Math.round(transform.originalValue[2]+ - (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : - (transform.originalValue + - (transform.targetValue - transform.originalValue) * position).toFixed(3) + - (transform.unit === null ? '' : transform.unit); - this.element.setStyle(style, true); - } -}); - -Effect.Transform = Class.create({ - initialize: function(tracks){ - this.tracks = []; - this.options = arguments[1] || { }; - this.addTracks(tracks); - }, - addTracks: function(tracks){ - tracks.each(function(track){ - track = $H(track); - var data = track.values().first(); - this.tracks.push($H({ - ids: track.keys().first(), - effect: Effect.Morph, - options: { style: data } - })); - }.bind(this)); - return this; - }, - play: function(){ - return new Effect.Parallel( - this.tracks.map(function(track){ - var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); - var elements = [$(ids) || $$(ids)].flatten(); - return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); - }).flatten(), - this.options - ); - } -}); - -Element.CSS_PROPERTIES = $w( - 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + - 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + - 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + - 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + - 'fontSize fontWeight height left letterSpacing lineHeight ' + - 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ - 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + - 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + - 'right textIndent top width wordSpacing zIndex'); - -Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; - -String.__parseStyleElement = document.createElement('div'); -String.prototype.parseStyle = function(){ - var style, styleRules = $H(); - if (Prototype.Browser.WebKit) - style = new Element('div',{style:this}).style; - else { - String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>'; - style = String.__parseStyleElement.childNodes[0].style; - } - - Element.CSS_PROPERTIES.each(function(property){ - if (style[property]) styleRules.set(property, style[property]); - }); - - if (Prototype.Browser.IE && this.include('opacity')) - styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); - - return styleRules; -}; - -if (document.defaultView && document.defaultView.getComputedStyle) { - Element.getStyles = function(element) { - var css = document.defaultView.getComputedStyle($(element), null); - return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { - styles[property] = css[property]; - return styles; - }); - }; -} else { - Element.getStyles = function(element) { - element = $(element); - var css = element.currentStyle, styles; - styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { - results[property] = css[property]; - return results; - }); - if (!styles.opacity) styles.opacity = element.getOpacity(); - return styles; - }; -}; - -Effect.Methods = { - morph: function(element, style) { - element = $(element); - new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); - return element; - }, - visualEffect: function(element, effect, options) { - element = $(element) - var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); - new Effect[klass](element, options); - return element; - }, - highlight: function(element, options) { - element = $(element); - new Effect.Highlight(element, options); - return element; - } -}; - -$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ - 'pulsate shake puff squish switchOff dropOut').each( - function(effect) { - Effect.Methods[effect] = function(element, options){ - element = $(element); - Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); - return element; - } - } -); - -$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( - function(f) { Effect.Methods[f] = Element[f]; } -); - -Element.addMethods(Effect.Methods); diff --git a/fas/fas/static/js/prototype.improvements.js b/fas/fas/static/js/prototype.improvements.js deleted file mode 100644 index 8d83921..0000000 --- a/fas/fas/static/js/prototype.improvements.js +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright (c) 2008 Beau D. Scott | http://www.beauscott.com -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -/** - * Prototype Improvements v0.1 - * Various additions to the prototype.js - */ - -Object.extend(Event, { - KEY_SHIFT: 16, - KEY_CONTROL: 17, - KEY_CAPSLOCK: 20, - KEY_SPACE: 32, - keyPressed: function(event) - { - return Browser.isMSIE() ? window.event.keyCode : event.which; - } -}); - -Browser = { - - /** - * Returns the user agent - * @param {bool} useAlert - */ - inspect: function(useAlert) - { - if(useAlert) - alert(navigator.userAgent); - else - return navigator.userAgent; - }, - /** - * Returns true if browser is MS Internet Explorer - */ - isMSIE: function() - { - return (navigator.userAgent.toLowerCase().indexOf("msie") > -1) && !this.isOpera(); - }, - /** - * Returns true if browser is Opera - */ - isOpera: function() - { - return navigator.userAgent.toLowerCase().indexOf("opera") > -1; - }, - /** - * Returns true if browzer is Mozilla - */ - isMozilla: function() - { - return (navigator.userAgent.toLowerCase().indexOf("mozilla") > -1) && !this.isOpera() && !this.isMSIE(); - } -} - - -Object.genGUID = function() -{ - var len = 8; - if(!isNaN(parseInt(arguments[0]))) len = parseInt(arguments[0]); - var chars = "abcdef0123456789"; - var output = ""; - while(output.length < len) - { - var rnd = Math.floor(Math.random() * (chars.length - 1)); - output += chars.charAt(rnd); - } - return output; -} \ No newline at end of file diff --git a/fas/fas/static/js/prototype.js b/fas/fas/static/js/prototype.js deleted file mode 100644 index 6385503..0000000 --- a/fas/fas/static/js/prototype.js +++ /dev/null @@ -1,4221 +0,0 @@ -/* Prototype JavaScript framework, version 1.6.0.2 - * (c) 2005-2008 Sam Stephenson - * - * Prototype is freely distributable under the terms of an MIT-style license. - * For details, see the Prototype web site: http://www.prototypejs.org/ - * - *--------------------------------------------------------------------------*/ - -var Prototype = { - Version: '1.6.0.2', - - Browser: { - IE: !!(window.attachEvent && !window.opera), - Opera: !!window.opera, - WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, - Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, - MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) - }, - - BrowserFeatures: { - XPath: !!document.evaluate, - ElementExtensions: !!window.HTMLElement, - SpecificElementExtensions: - document.createElement('div').__proto__ && - document.createElement('div').__proto__ !== - document.createElement('form').__proto__ - }, - - ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', - JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, - - emptyFunction: function() { }, - K: function(x) { return x } -}; - -if (Prototype.Browser.MobileSafari) - Prototype.BrowserFeatures.SpecificElementExtensions = false; - - -/* Based on Alex Arnell's inheritance implementation. */ -var Class = { - create: function() { - var parent = null, properties = $A(arguments); - if (Object.isFunction(properties[0])) - parent = properties.shift(); - - function klass() { - this.initialize.apply(this, arguments); - } - - Object.extend(klass, Class.Methods); - klass.superclass = parent; - klass.subclasses = []; - - if (parent) { - var subclass = function() { }; - subclass.prototype = parent.prototype; - klass.prototype = new subclass; - parent.subclasses.push(klass); - } - - for (var i = 0; i < properties.length; i++) - klass.addMethods(properties[i]); - - if (!klass.prototype.initialize) - klass.prototype.initialize = Prototype.emptyFunction; - - klass.prototype.constructor = klass; - - return klass; - } -}; - -Class.Methods = { - addMethods: function(source) { - var ancestor = this.superclass && this.superclass.prototype; - var properties = Object.keys(source); - - if (!Object.keys({ toString: true }).length) - properties.push("toString", "valueOf"); - - for (var i = 0, length = properties.length; i < length; i++) { - var property = properties[i], value = source[property]; - if (ancestor && Object.isFunction(value) && - value.argumentNames().first() == "$super") { - var method = value, value = Object.extend((function(m) { - return function() { return ancestor[m].apply(this, arguments) }; - })(property).wrap(method), { - valueOf: function() { return method }, - toString: function() { return method.toString() } - }); - } - this.prototype[property] = value; - } - - return this; - } -}; - -var Abstract = { }; - -Object.extend = function(destination, source) { - for (var property in source) - destination[property] = source[property]; - return destination; -}; - -Object.extend(Object, { - inspect: function(object) { - try { - if (Object.isUndefined(object)) return 'undefined'; - if (object === null) return 'null'; - return object.inspect ? object.inspect() : String(object); - } catch (e) { - if (e instanceof RangeError) return '...'; - throw e; - } - }, - - toJSON: function(object) { - var type = typeof object; - switch (type) { - case 'undefined': - case 'function': - case 'unknown': return; - case 'boolean': return object.toString(); - } - - if (object === null) return 'null'; - if (object.toJSON) return object.toJSON(); - if (Object.isElement(object)) return; - - var results = []; - for (var property in object) { - var value = Object.toJSON(object[property]); - if (!Object.isUndefined(value)) - results.push(property.toJSON() + ': ' + value); - } - - return '{' + results.join(', ') + '}'; - }, - - toQueryString: function(object) { - return $H(object).toQueryString(); - }, - - toHTML: function(object) { - return object && object.toHTML ? object.toHTML() : String.interpret(object); - }, - - keys: function(object) { - var keys = []; - for (var property in object) - keys.push(property); - return keys; - }, - - values: function(object) { - var values = []; - for (var property in object) - values.push(object[property]); - return values; - }, - - clone: function(object) { - return Object.extend({ }, object); - }, - - isElement: function(object) { - return object && object.nodeType == 1; - }, - - isArray: function(object) { - return object != null && typeof object == "object" && - 'splice' in object && 'join' in object; - }, - - isHash: function(object) { - return object instanceof Hash; - }, - - isFunction: function(object) { - return typeof object == "function"; - }, - - isString: function(object) { - return typeof object == "string"; - }, - - isNumber: function(object) { - return typeof object == "number"; - }, - - isUndefined: function(object) { - return typeof object == "undefined"; - } -}); - -Object.extend(Function.prototype, { - argumentNames: function() { - var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); - return names.length == 1 && !names[0] ? [] : names; - }, - - bind: function() { - if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; - var __method = this, args = $A(arguments), object = args.shift(); - return function() { - return __method.apply(object, args.concat($A(arguments))); - } - }, - - bindAsEventListener: function() { - var __method = this, args = $A(arguments), object = args.shift(); - return function(event) { - return __method.apply(object, [event || window.event].concat(args)); - } - }, - - curry: function() { - if (!arguments.length) return this; - var __method = this, args = $A(arguments); - return function() { - return __method.apply(this, args.concat($A(arguments))); - } - }, - - delay: function() { - var __method = this, args = $A(arguments), timeout = args.shift() * 1000; - return window.setTimeout(function() { - return __method.apply(__method, args); - }, timeout); - }, - - wrap: function(wrapper) { - var __method = this; - return function() { - return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); - } - }, - - methodize: function() { - if (this._methodized) return this._methodized; - var __method = this; - return this._methodized = function() { - return __method.apply(null, [this].concat($A(arguments))); - }; - } -}); - -Function.prototype.defer = Function.prototype.delay.curry(0.01); - -Date.prototype.toJSON = function() { - return '"' + this.getUTCFullYear() + '-' + - (this.getUTCMonth() + 1).toPaddedString(2) + '-' + - this.getUTCDate().toPaddedString(2) + 'T' + - this.getUTCHours().toPaddedString(2) + ':' + - this.getUTCMinutes().toPaddedString(2) + ':' + - this.getUTCSeconds().toPaddedString(2) + 'Z"'; -}; - -var Try = { - these: function() { - var returnValue; - - for (var i = 0, length = arguments.length; i < length; i++) { - var lambda = arguments[i]; - try { - returnValue = lambda(); - break; - } catch (e) { } - } - - return returnValue; - } -}; - -RegExp.prototype.match = RegExp.prototype.test; - -RegExp.escape = function(str) { - return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); -}; - -/*--------------------------------------------------------------------------*/ - -var PeriodicalExecuter = Class.create({ - initialize: function(callback, frequency) { - this.callback = callback; - this.frequency = frequency; - this.currentlyExecuting = false; - - this.registerCallback(); - }, - - registerCallback: function() { - this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - execute: function() { - this.callback(this); - }, - - stop: function() { - if (!this.timer) return; - clearInterval(this.timer); - this.timer = null; - }, - - onTimerEvent: function() { - if (!this.currentlyExecuting) { - try { - this.currentlyExecuting = true; - this.execute(); - } finally { - this.currentlyExecuting = false; - } - } - } -}); -Object.extend(String, { - interpret: function(value) { - return value == null ? '' : String(value); - }, - specialChar: { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '\\': '\\\\' - } -}); - -Object.extend(String.prototype, { - gsub: function(pattern, replacement) { - var result = '', source = this, match; - replacement = arguments.callee.prepareReplacement(replacement); - - while (source.length > 0) { - if (match = source.match(pattern)) { - result += source.slice(0, match.index); - result += String.interpret(replacement(match)); - source = source.slice(match.index + match[0].length); - } else { - result += source, source = ''; - } - } - return result; - }, - - sub: function(pattern, replacement, count) { - replacement = this.gsub.prepareReplacement(replacement); - count = Object.isUndefined(count) ? 1 : count; - - return this.gsub(pattern, function(match) { - if (--count < 0) return match[0]; - return replacement(match); - }); - }, - - scan: function(pattern, iterator) { - this.gsub(pattern, iterator); - return String(this); - }, - - truncate: function(length, truncation) { - length = length || 30; - truncation = Object.isUndefined(truncation) ? '...' : truncation; - return this.length > length ? - this.slice(0, length - truncation.length) + truncation : String(this); - }, - - strip: function() { - return this.replace(/^\s+/, '').replace(/\s+$/, ''); - }, - - stripTags: function() { - return this.replace(/<\/?[^>]+>/gi, ''); - }, - - stripScripts: function() { - return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); - }, - - extractScripts: function() { - var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); - var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); - return (this.match(matchAll) || []).map(function(scriptTag) { - return (scriptTag.match(matchOne) || ['', ''])[1]; - }); - }, - - evalScripts: function() { - return this.extractScripts().map(function(script) { return eval(script) }); - }, - - escapeHTML: function() { - var self = arguments.callee; - self.text.data = this; - return self.div.innerHTML; - }, - - unescapeHTML: function() { - var div = new Element('div'); - div.innerHTML = this.stripTags(); - return div.childNodes[0] ? (div.childNodes.length > 1 ? - $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : - div.childNodes[0].nodeValue) : ''; - }, - - toQueryParams: function(separator) { - var match = this.strip().match(/([^?#]*)(#.*)?$/); - if (!match) return { }; - - return match[1].split(separator || '&').inject({ }, function(hash, pair) { - if ((pair = pair.split('='))[0]) { - var key = decodeURIComponent(pair.shift()); - var value = pair.length > 1 ? pair.join('=') : pair[0]; - if (value != undefined) value = decodeURIComponent(value); - - if (key in hash) { - if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; - hash[key].push(value); - } - else hash[key] = value; - } - return hash; - }); - }, - - toArray: function() { - return this.split(''); - }, - - succ: function() { - return this.slice(0, this.length - 1) + - String.fromCharCode(this.charCodeAt(this.length - 1) + 1); - }, - - times: function(count) { - return count < 1 ? '' : new Array(count + 1).join(this); - }, - - camelize: function() { - var parts = this.split('-'), len = parts.length; - if (len == 1) return parts[0]; - - var camelized = this.charAt(0) == '-' - ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) - : parts[0]; - - for (var i = 1; i < len; i++) - camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); - - return camelized; - }, - - capitalize: function() { - return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); - }, - - underscore: function() { - return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); - }, - - dasherize: function() { - return this.gsub(/_/,'-'); - }, - - inspect: function(useDoubleQuotes) { - var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { - var character = String.specialChar[match[0]]; - return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); - }); - if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; - return "'" + escapedString.replace(/'/g, '\\\'') + "'"; - }, - - toJSON: function() { - return this.inspect(true); - }, - - unfilterJSON: function(filter) { - return this.sub(filter || Prototype.JSONFilter, '#{1}'); - }, - - isJSON: function() { - var str = this; - if (str.blank()) return false; - str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); - return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); - }, - - evalJSON: function(sanitize) { - var json = this.unfilterJSON(); - try { - if (!sanitize || json.isJSON()) return eval('(' + json + ')'); - } catch (e) { } - throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); - }, - - include: function(pattern) { - return this.indexOf(pattern) > -1; - }, - - startsWith: function(pattern) { - return this.indexOf(pattern) === 0; - }, - - endsWith: function(pattern) { - var d = this.length - pattern.length; - return d >= 0 && this.lastIndexOf(pattern) === d; - }, - - empty: function() { - return this == ''; - }, - - blank: function() { - return /^\s*$/.test(this); - }, - - interpolate: function(object, pattern) { - return new Template(this, pattern).evaluate(object); - } -}); - -if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { - escapeHTML: function() { - return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); - }, - unescapeHTML: function() { - return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); - } -}); - -String.prototype.gsub.prepareReplacement = function(replacement) { - if (Object.isFunction(replacement)) return replacement; - var template = new Template(replacement); - return function(match) { return template.evaluate(match) }; -}; - -String.prototype.parseQuery = String.prototype.toQueryParams; - -Object.extend(String.prototype.escapeHTML, { - div: document.createElement('div'), - text: document.createTextNode('') -}); - -with (String.prototype.escapeHTML) div.appendChild(text); - -var Template = Class.create({ - initialize: function(template, pattern) { - this.template = template.toString(); - this.pattern = pattern || Template.Pattern; - }, - - evaluate: function(object) { - if (Object.isFunction(object.toTemplateReplacements)) - object = object.toTemplateReplacements(); - - return this.template.gsub(this.pattern, function(match) { - if (object == null) return ''; - - var before = match[1] || ''; - if (before == '\\') return match[2]; - - var ctx = object, expr = match[3]; - var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; - match = pattern.exec(expr); - if (match == null) return before; - - while (match != null) { - var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; - ctx = ctx[comp]; - if (null == ctx || '' == match[3]) break; - expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); - match = pattern.exec(expr); - } - - return before + String.interpret(ctx); - }); - } -}); -Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; - -var $break = { }; - -var Enumerable = { - each: function(iterator, context) { - var index = 0; - iterator = iterator.bind(context); - try { - this._each(function(value) { - iterator(value, index++); - }); - } catch (e) { - if (e != $break) throw e; - } - return this; - }, - - eachSlice: function(number, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var index = -number, slices = [], array = this.toArray(); - while ((index += number) < array.length) - slices.push(array.slice(index, index+number)); - return slices.collect(iterator, context); - }, - - all: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result = true; - this.each(function(value, index) { - result = result && !!iterator(value, index); - if (!result) throw $break; - }); - return result; - }, - - any: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result = false; - this.each(function(value, index) { - if (result = !!iterator(value, index)) - throw $break; - }); - return result; - }, - - collect: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var results = []; - this.each(function(value, index) { - results.push(iterator(value, index)); - }); - return results; - }, - - detect: function(iterator, context) { - iterator = iterator.bind(context); - var result; - this.each(function(value, index) { - if (iterator(value, index)) { - result = value; - throw $break; - } - }); - return result; - }, - - findAll: function(iterator, context) { - iterator = iterator.bind(context); - var results = []; - this.each(function(value, index) { - if (iterator(value, index)) - results.push(value); - }); - return results; - }, - - grep: function(filter, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var results = []; - - if (Object.isString(filter)) - filter = new RegExp(filter); - - this.each(function(value, index) { - if (filter.match(value)) - results.push(iterator(value, index)); - }); - return results; - }, - - include: function(object) { - if (Object.isFunction(this.indexOf)) - if (this.indexOf(object) != -1) return true; - - var found = false; - this.each(function(value) { - if (value == object) { - found = true; - throw $break; - } - }); - return found; - }, - - inGroupsOf: function(number, fillWith) { - fillWith = Object.isUndefined(fillWith) ? null : fillWith; - return this.eachSlice(number, function(slice) { - while(slice.length < number) slice.push(fillWith); - return slice; - }); - }, - - inject: function(memo, iterator, context) { - iterator = iterator.bind(context); - this.each(function(value, index) { - memo = iterator(memo, value, index); - }); - return memo; - }, - - invoke: function(method) { - var args = $A(arguments).slice(1); - return this.map(function(value) { - return value[method].apply(value, args); - }); - }, - - max: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result; - this.each(function(value, index) { - value = iterator(value, index); - if (result == null || value >= result) - result = value; - }); - return result; - }, - - min: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result; - this.each(function(value, index) { - value = iterator(value, index); - if (result == null || value < result) - result = value; - }); - return result; - }, - - partition: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var trues = [], falses = []; - this.each(function(value, index) { - (iterator(value, index) ? - trues : falses).push(value); - }); - return [trues, falses]; - }, - - pluck: function(property) { - var results = []; - this.each(function(value) { - results.push(value[property]); - }); - return results; - }, - - reject: function(iterator, context) { - iterator = iterator.bind(context); - var results = []; - this.each(function(value, index) { - if (!iterator(value, index)) - results.push(value); - }); - return results; - }, - - sortBy: function(iterator, context) { - iterator = iterator.bind(context); - return this.map(function(value, index) { - return {value: value, criteria: iterator(value, index)}; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }).pluck('value'); - }, - - toArray: function() { - return this.map(); - }, - - zip: function() { - var iterator = Prototype.K, args = $A(arguments); - if (Object.isFunction(args.last())) - iterator = args.pop(); - - var collections = [this].concat(args).map($A); - return this.map(function(value, index) { - return iterator(collections.pluck(index)); - }); - }, - - size: function() { - return this.toArray().length; - }, - - inspect: function() { - return '#<Enumerable:' + this.toArray().inspect() + '>'; - } -}; - -Object.extend(Enumerable, { - map: Enumerable.collect, - find: Enumerable.detect, - select: Enumerable.findAll, - filter: Enumerable.findAll, - member: Enumerable.include, - entries: Enumerable.toArray, - every: Enumerable.all, - some: Enumerable.any -}); -function $A(iterable) { - if (!iterable) return []; - if (iterable.toArray) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; -} - -if (Prototype.Browser.WebKit) { - $A = function(iterable) { - if (!iterable) return []; - if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && - iterable.toArray) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; - }; -} - -Array.from = $A; - -Object.extend(Array.prototype, Enumerable); - -if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; - -Object.extend(Array.prototype, { - _each: function(iterator) { - for (var i = 0, length = this.length; i < length; i++) - iterator(this[i]); - }, - - clear: function() { - this.length = 0; - return this; - }, - - first: function() { - return this[0]; - }, - - last: function() { - return this[this.length - 1]; - }, - - compact: function() { - return this.select(function(value) { - return value != null; - }); - }, - - flatten: function() { - return this.inject([], function(array, value) { - return array.concat(Object.isArray(value) ? - value.flatten() : [value]); - }); - }, - - without: function() { - var values = $A(arguments); - return this.select(function(value) { - return !values.include(value); - }); - }, - - reverse: function(inline) { - return (inline !== false ? this : this.toArray())._reverse(); - }, - - reduce: function() { - return this.length > 1 ? this : this[0]; - }, - - uniq: function(sorted) { - return this.inject([], function(array, value, index) { - if (0 == index || (sorted ? array.last() != value : !array.include(value))) - array.push(value); - return array; - }); - }, - - intersect: function(array) { - return this.uniq().findAll(function(item) { - return array.detect(function(value) { return item === value }); - }); - }, - - clone: function() { - return [].concat(this); - }, - - size: function() { - return this.length; - }, - - inspect: function() { - return '[' + this.map(Object.inspect).join(', ') + ']'; - }, - - toJSON: function() { - var results = []; - this.each(function(object) { - var value = Object.toJSON(object); - if (!Object.isUndefined(value)) results.push(value); - }); - return '[' + results.join(', ') + ']'; - } -}); - -// use native browser JS 1.6 implementation if available -if (Object.isFunction(Array.prototype.forEach)) - Array.prototype._each = Array.prototype.forEach; - -if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { - i || (i = 0); - var length = this.length; - if (i < 0) i = length + i; - for (; i < length; i++) - if (this[i] === item) return i; - return -1; -}; - -if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { - i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; - var n = this.slice(0, i).reverse().indexOf(item); - return (n < 0) ? n : i - n - 1; -}; - -Array.prototype.toArray = Array.prototype.clone; - -function $w(string) { - if (!Object.isString(string)) return []; - string = string.strip(); - return string ? string.split(/\s+/) : []; -} - -if (Prototype.Browser.Opera){ - Array.prototype.concat = function() { - var array = []; - for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); - for (var i = 0, length = arguments.length; i < length; i++) { - if (Object.isArray(arguments[i])) { - for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) - array.push(arguments[i][j]); - } else { - array.push(arguments[i]); - } - } - return array; - }; -} -Object.extend(Number.prototype, { - toColorPart: function() { - return this.toPaddedString(2, 16); - }, - - succ: function() { - return this + 1; - }, - - times: function(iterator) { - $R(0, this, true).each(iterator); - return this; - }, - - toPaddedString: function(length, radix) { - var string = this.toString(radix || 10); - return '0'.times(length - string.length) + string; - }, - - toJSON: function() { - return isFinite(this) ? this.toString() : 'null'; - } -}); - -$w('abs round ceil floor').each(function(method){ - Number.prototype[method] = Math[method].methodize(); -}); -function $H(object) { - return new Hash(object); -}; - -var Hash = Class.create(Enumerable, (function() { - - function toQueryPair(key, value) { - if (Object.isUndefined(value)) return key; - return key + '=' + encodeURIComponent(String.interpret(value)); - } - - return { - initialize: function(object) { - this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); - }, - - _each: function(iterator) { - for (var key in this._object) { - var value = this._object[key], pair = [key, value]; - pair.key = key; - pair.value = value; - iterator(pair); - } - }, - - set: function(key, value) { - return this._object[key] = value; - }, - - get: function(key) { - return this._object[key]; - }, - - unset: function(key) { - var value = this._object[key]; - delete this._object[key]; - return value; - }, - - toObject: function() { - return Object.clone(this._object); - }, - - keys: function() { - return this.pluck('key'); - }, - - values: function() { - return this.pluck('value'); - }, - - index: function(value) { - var match = this.detect(function(pair) { - return pair.value === value; - }); - return match && match.key; - }, - - merge: function(object) { - return this.clone().update(object); - }, - - update: function(object) { - return new Hash(object).inject(this, function(result, pair) { - result.set(pair.key, pair.value); - return result; - }); - }, - - toQueryString: function() { - return this.map(function(pair) { - var key = encodeURIComponent(pair.key), values = pair.value; - - if (values && typeof values == 'object') { - if (Object.isArray(values)) - return values.map(toQueryPair.curry(key)).join('&'); - } - return toQueryPair(key, values); - }).join('&'); - }, - - inspect: function() { - return '#<Hash:{' + this.map(function(pair) { - return pair.map(Object.inspect).join(': '); - }).join(', ') + '}>'; - }, - - toJSON: function() { - return Object.toJSON(this.toObject()); - }, - - clone: function() { - return new Hash(this); - } - } -})()); - -Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; -Hash.from = $H; -var ObjectRange = Class.create(Enumerable, { - initialize: function(start, end, exclusive) { - this.start = start; - this.end = end; - this.exclusive = exclusive; - }, - - _each: function(iterator) { - var value = this.start; - while (this.include(value)) { - iterator(value); - value = value.succ(); - } - }, - - include: function(value) { - if (value < this.start) - return false; - if (this.exclusive) - return value < this.end; - return value <= this.end; - } -}); - -var $R = function(start, end, exclusive) { - return new ObjectRange(start, end, exclusive); -}; - -var Ajax = { - getTransport: function() { - return Try.these( - function() {return new XMLHttpRequest()}, - function() {return new ActiveXObject('Msxml2.XMLHTTP')}, - function() {return new ActiveXObject('Microsoft.XMLHTTP')} - ) || false; - }, - - activeRequestCount: 0 -}; - -Ajax.Responders = { - responders: [], - - _each: function(iterator) { - this.responders._each(iterator); - }, - - register: function(responder) { - if (!this.include(responder)) - this.responders.push(responder); - }, - - unregister: function(responder) { - this.responders = this.responders.without(responder); - }, - - dispatch: function(callback, request, transport, json) { - this.each(function(responder) { - if (Object.isFunction(responder[callback])) { - try { - responder[callback].apply(responder, [request, transport, json]); - } catch (e) { } - } - }); - } -}; - -Object.extend(Ajax.Responders, Enumerable); - -Ajax.Responders.register({ - onCreate: function() { Ajax.activeRequestCount++ }, - onComplete: function() { Ajax.activeRequestCount-- } -}); - -Ajax.Base = Class.create({ - initialize: function(options) { - this.options = { - method: 'post', - asynchronous: true, - contentType: 'application/x-www-form-urlencoded', - encoding: 'UTF-8', - parameters: '', - evalJSON: true, - evalJS: true - }; - Object.extend(this.options, options || { }); - - this.options.method = this.options.method.toLowerCase(); - - if (Object.isString(this.options.parameters)) - this.options.parameters = this.options.parameters.toQueryParams(); - else if (Object.isHash(this.options.parameters)) - this.options.parameters = this.options.parameters.toObject(); - } -}); - -Ajax.Request = Class.create(Ajax.Base, { - _complete: false, - - initialize: function($super, url, options) { - $super(options); - this.transport = Ajax.getTransport(); - this.request(url); - }, - - request: function(url) { - this.url = url; - this.method = this.options.method; - var params = Object.clone(this.options.parameters); - - if (!['get', 'post'].include(this.method)) { - // simulate other verbs over post - params['_method'] = this.method; - this.method = 'post'; - } - - this.parameters = params; - - if (params = Object.toQueryString(params)) { - // when GET, append parameters to URL - if (this.method == 'get') - this.url += (this.url.include('?') ? '&' : '?') + params; - else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - params += '&_='; - } - - try { - var response = new Ajax.Response(this); - if (this.options.onCreate) this.options.onCreate(response); - Ajax.Responders.dispatch('onCreate', this, response); - - this.transport.open(this.method.toUpperCase(), this.url, - this.options.asynchronous); - - if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); - - this.transport.onreadystatechange = this.onStateChange.bind(this); - this.setRequestHeaders(); - - this.body = this.method == 'post' ? (this.options.postBody || params) : null; - this.transport.send(this.body); - - /* Force Firefox to handle ready state 4 for synchronous requests */ - if (!this.options.asynchronous && this.transport.overrideMimeType) - this.onStateChange(); - - } - catch (e) { - this.dispatchException(e); - } - }, - - onStateChange: function() { - var readyState = this.transport.readyState; - if (readyState > 1 && !((readyState == 4) && this._complete)) - this.respondToReadyState(this.transport.readyState); - }, - - setRequestHeaders: function() { - var headers = { - 'X-Requested-With': 'XMLHttpRequest', - 'X-Prototype-Version': Prototype.Version, - 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' - }; - - if (this.method == 'post') { - headers['Content-type'] = this.options.contentType + - (this.options.encoding ? '; charset=' + this.options.encoding : ''); - - /* Force "Connection: close" for older Mozilla browsers to work - * around a bug where XMLHttpRequest sends an incorrect - * Content-length header. See Mozilla Bugzilla #246651. - */ - if (this.transport.overrideMimeType && - (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) - headers['Connection'] = 'close'; - } - - // user-defined headers - if (typeof this.options.requestHeaders == 'object') { - var extras = this.options.requestHeaders; - - if (Object.isFunction(extras.push)) - for (var i = 0, length = extras.length; i < length; i += 2) - headers[extras[i]] = extras[i+1]; - else - $H(extras).each(function(pair) { headers[pair.key] = pair.value }); - } - - for (var name in headers) - this.transport.setRequestHeader(name, headers[name]); - }, - - success: function() { - var status = this.getStatus(); - return !status || (status >= 200 && status < 300); - }, - - getStatus: function() { - try { - return this.transport.status || 0; - } catch (e) { return 0 } - }, - - respondToReadyState: function(readyState) { - var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); - - if (state == 'Complete') { - try { - this._complete = true; - (this.options['on' + response.status] - || this.options['on' + (this.success() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - var contentType = response.getHeader('Content-type'); - if (this.options.evalJS == 'force' - || (this.options.evalJS && this.isSameOrigin() && contentType - && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) - this.evalResponse(); - } - - try { - (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); - Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - if (state == 'Complete') { - // avoid memory leak in MSIE: clean up - this.transport.onreadystatechange = Prototype.emptyFunction; - } - }, - - isSameOrigin: function() { - var m = this.url.match(/^\s*https?:\/\/[^\/]*/); - return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ - protocol: location.protocol, - domain: document.domain, - port: location.port ? ':' + location.port : '' - })); - }, - - getHeader: function(name) { - try { - return this.transport.getResponseHeader(name) || null; - } catch (e) { return null } - }, - - evalResponse: function() { - try { - return eval((this.transport.responseText || '').unfilterJSON()); - } catch (e) { - this.dispatchException(e); - } - }, - - dispatchException: function(exception) { - (this.options.onException || Prototype.emptyFunction)(this, exception); - Ajax.Responders.dispatch('onException', this, exception); - } -}); - -Ajax.Request.Events = - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; - -Ajax.Response = Class.create({ - initialize: function(request){ - this.request = request; - var transport = this.transport = request.transport, - readyState = this.readyState = transport.readyState; - - if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { - this.status = this.getStatus(); - this.statusText = this.getStatusText(); - this.responseText = String.interpret(transport.responseText); - this.headerJSON = this._getHeaderJSON(); - } - - if(readyState == 4) { - var xml = transport.responseXML; - this.responseXML = Object.isUndefined(xml) ? null : xml; - this.responseJSON = this._getResponseJSON(); - } - }, - - status: 0, - statusText: '', - - getStatus: Ajax.Request.prototype.getStatus, - - getStatusText: function() { - try { - return this.transport.statusText || ''; - } catch (e) { return '' } - }, - - getHeader: Ajax.Request.prototype.getHeader, - - getAllHeaders: function() { - try { - return this.getAllResponseHeaders(); - } catch (e) { return null } - }, - - getResponseHeader: function(name) { - return this.transport.getResponseHeader(name); - }, - - getAllResponseHeaders: function() { - return this.transport.getAllResponseHeaders(); - }, - - _getHeaderJSON: function() { - var json = this.getHeader('X-JSON'); - if (!json) return null; - json = decodeURIComponent(escape(json)); - try { - return json.evalJSON(this.request.options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - }, - - _getResponseJSON: function() { - var options = this.request.options; - if (!options.evalJSON || (options.evalJSON != 'force' && - !(this.getHeader('Content-type') || '').include('application/json')) || - this.responseText.blank()) - return null; - try { - return this.responseText.evalJSON(options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - } -}); - -Ajax.Updater = Class.create(Ajax.Request, { - initialize: function($super, container, url, options) { - this.container = { - success: (container.success || container), - failure: (container.failure || (container.success ? null : container)) - }; - - options = Object.clone(options); - var onComplete = options.onComplete; - options.onComplete = (function(response, json) { - this.updateContent(response.responseText); - if (Object.isFunction(onComplete)) onComplete(response, json); - }).bind(this); - - $super(url, options); - }, - - updateContent: function(responseText) { - var receiver = this.container[this.success() ? 'success' : 'failure'], - options = this.options; - - if (!options.evalScripts) responseText = responseText.stripScripts(); - - if (receiver = $(receiver)) { - if (options.insertion) { - if (Object.isString(options.insertion)) { - var insertion = { }; insertion[options.insertion] = responseText; - receiver.insert(insertion); - } - else options.insertion(receiver, responseText); - } - else receiver.update(responseText); - } - } -}); - -Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { - initialize: function($super, container, url, options) { - $super(options); - this.onComplete = this.options.onComplete; - - this.frequency = (this.options.frequency || 2); - this.decay = (this.options.decay || 1); - - this.updater = { }; - this.container = container; - this.url = url; - - this.start(); - }, - - start: function() { - this.options.onComplete = this.updateComplete.bind(this); - this.onTimerEvent(); - }, - - stop: function() { - this.updater.options.onComplete = undefined; - clearTimeout(this.timer); - (this.onComplete || Prototype.emptyFunction).apply(this, arguments); - }, - - updateComplete: function(response) { - if (this.options.decay) { - this.decay = (response.responseText == this.lastText ? - this.decay * this.options.decay : 1); - - this.lastText = response.responseText; - } - this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); - }, - - onTimerEvent: function() { - this.updater = new Ajax.Updater(this.container, this.url, this.options); - } -}); -function $(element) { - if (arguments.length > 1) { - for (var i = 0, elements = [], length = arguments.length; i < length; i++) - elements.push($(arguments[i])); - return elements; - } - if (Object.isString(element)) - element = document.getElementById(element); - return Element.extend(element); -} - -if (Prototype.BrowserFeatures.XPath) { - document._getElementsByXPath = function(expression, parentElement) { - var results = []; - var query = document.evaluate(expression, $(parentElement) || document, - null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - for (var i = 0, length = query.snapshotLength; i < length; i++) - results.push(Element.extend(query.snapshotItem(i))); - return results; - }; -} - -/*--------------------------------------------------------------------------*/ - -if (!window.Node) var Node = { }; - -if (!Node.ELEMENT_NODE) { - // DOM level 2 ECMAScript Language Binding - Object.extend(Node, { - ELEMENT_NODE: 1, - ATTRIBUTE_NODE: 2, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - ENTITY_REFERENCE_NODE: 5, - ENTITY_NODE: 6, - PROCESSING_INSTRUCTION_NODE: 7, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9, - DOCUMENT_TYPE_NODE: 10, - DOCUMENT_FRAGMENT_NODE: 11, - NOTATION_NODE: 12 - }); -} - -(function() { - var element = this.Element; - this.Element = function(tagName, attributes) { - attributes = attributes || { }; - tagName = tagName.toLowerCase(); - var cache = Element.cache; - if (Prototype.Browser.IE && attributes.name) { - tagName = '<' + tagName + ' name="' + attributes.name + '">'; - delete attributes.name; - return Element.writeAttribute(document.createElement(tagName), attributes); - } - if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); - return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); - }; - Object.extend(this.Element, element || { }); -}).call(window); - -Element.cache = { }; - -Element.Methods = { - visible: function(element) { - return $(element).style.display != 'none'; - }, - - toggle: function(element) { - element = $(element); - Element[Element.visible(element) ? 'hide' : 'show'](element); - return element; - }, - - hide: function(element) { - $(element).style.display = 'none'; - return element; - }, - - show: function(element) { - $(element).style.display = ''; - return element; - }, - - remove: function(element) { - element = $(element); - element.parentNode.removeChild(element); - return element; - }, - - update: function(element, content) { - element = $(element); - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) return element.update().insert(content); - content = Object.toHTML(content); - element.innerHTML = content.stripScripts(); - content.evalScripts.bind(content).defer(); - return element; - }, - - replace: function(element, content) { - element = $(element); - if (content && content.toElement) content = content.toElement(); - else if (!Object.isElement(content)) { - content = Object.toHTML(content); - var range = element.ownerDocument.createRange(); - range.selectNode(element); - content.evalScripts.bind(content).defer(); - content = range.createContextualFragment(content.stripScripts()); - } - element.parentNode.replaceChild(content, element); - return element; - }, - - insert: function(element, insertions) { - element = $(element); - - if (Object.isString(insertions) || Object.isNumber(insertions) || - Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) - insertions = {bottom:insertions}; - - var content, insert, tagName, childNodes; - - for (var position in insertions) { - content = insertions[position]; - position = position.toLowerCase(); - insert = Element._insertionTranslations[position]; - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - insert(element, content); - continue; - } - - content = Object.toHTML(content); - - tagName = ((position == 'before' || position == 'after') - ? element.parentNode : element).tagName.toUpperCase(); - - childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - - if (position == 'top' || position == 'after') childNodes.reverse(); - childNodes.each(insert.curry(element)); - - content.evalScripts.bind(content).defer(); - } - - return element; - }, - - wrap: function(element, wrapper, attributes) { - element = $(element); - if (Object.isElement(wrapper)) - $(wrapper).writeAttribute(attributes || { }); - else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); - else wrapper = new Element('div', wrapper); - if (element.parentNode) - element.parentNode.replaceChild(wrapper, element); - wrapper.appendChild(element); - return wrapper; - }, - - inspect: function(element) { - element = $(element); - var result = '<' + element.tagName.toLowerCase(); - $H({'id': 'id', 'className': 'class'}).each(function(pair) { - var property = pair.first(), attribute = pair.last(); - var value = (element[property] || '').toString(); - if (value) result += ' ' + attribute + '=' + value.inspect(true); - }); - return result + '>'; - }, - - recursivelyCollect: function(element, property) { - element = $(element); - var elements = []; - while (element = element[property]) - if (element.nodeType == 1) - elements.push(Element.extend(element)); - return elements; - }, - - ancestors: function(element) { - return $(element).recursivelyCollect('parentNode'); - }, - - descendants: function(element) { - return $(element).select("*"); - }, - - firstDescendant: function(element) { - element = $(element).firstChild; - while (element && element.nodeType != 1) element = element.nextSibling; - return $(element); - }, - - immediateDescendants: function(element) { - if (!(element = $(element).firstChild)) return []; - while (element && element.nodeType != 1) element = element.nextSibling; - if (element) return [element].concat($(element).nextSiblings()); - return []; - }, - - previousSiblings: function(element) { - return $(element).recursivelyCollect('previousSibling'); - }, - - nextSiblings: function(element) { - return $(element).recursivelyCollect('nextSibling'); - }, - - siblings: function(element) { - element = $(element); - return element.previousSiblings().reverse().concat(element.nextSiblings()); - }, - - match: function(element, selector) { - if (Object.isString(selector)) - selector = new Selector(selector); - return selector.match($(element)); - }, - - up: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(element.parentNode); - var ancestors = element.ancestors(); - return Object.isNumber(expression) ? ancestors[expression] : - Selector.findElement(ancestors, expression, index); - }, - - down: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return element.firstDescendant(); - return Object.isNumber(expression) ? element.descendants()[expression] : - element.select(expression)[index || 0]; - }, - - previous: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); - var previousSiblings = element.previousSiblings(); - return Object.isNumber(expression) ? previousSiblings[expression] : - Selector.findElement(previousSiblings, expression, index); - }, - - next: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); - var nextSiblings = element.nextSiblings(); - return Object.isNumber(expression) ? nextSiblings[expression] : - Selector.findElement(nextSiblings, expression, index); - }, - - select: function() { - var args = $A(arguments), element = $(args.shift()); - return Selector.findChildElements(element, args); - }, - - adjacent: function() { - var args = $A(arguments), element = $(args.shift()); - return Selector.findChildElements(element.parentNode, args).without(element); - }, - - identify: function(element) { - element = $(element); - var id = element.readAttribute('id'), self = arguments.callee; - if (id) return id; - do { id = 'anonymous_element_' + self.counter++ } while ($(id)); - element.writeAttribute('id', id); - return id; - }, - - readAttribute: function(element, name) { - element = $(element); - if (Prototype.Browser.IE) { - var t = Element._attributeTranslations.read; - if (t.values[name]) return t.values[name](element, name); - if (t.names[name]) name = t.names[name]; - if (name.include(':')) { - return (!element.attributes || !element.attributes[name]) ? null : - element.attributes[name].value; - } - } - return element.getAttribute(name); - }, - - writeAttribute: function(element, name, value) { - element = $(element); - var attributes = { }, t = Element._attributeTranslations.write; - - if (typeof name == 'object') attributes = name; - else attributes[name] = Object.isUndefined(value) ? true : value; - - for (var attr in attributes) { - name = t.names[attr] || attr; - value = attributes[attr]; - if (t.values[attr]) name = t.values[attr](element, value); - if (value === false || value === null) - element.removeAttribute(name); - else if (value === true) - element.setAttribute(name, name); - else element.setAttribute(name, value); - } - return element; - }, - - getHeight: function(element) { - return $(element).getDimensions().height; - }, - - getWidth: function(element) { - return $(element).getDimensions().width; - }, - - classNames: function(element) { - return new Element.ClassNames(element); - }, - - hasClassName: function(element, className) { - if (!(element = $(element))) return; - var elementClassName = element.className; - return (elementClassName.length > 0 && (elementClassName == className || - new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); - }, - - addClassName: function(element, className) { - if (!(element = $(element))) return; - if (!element.hasClassName(className)) - element.className += (element.className ? ' ' : '') + className; - return element; - }, - - removeClassName: function(element, className) { - if (!(element = $(element))) return; - element.className = element.className.replace( - new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); - return element; - }, - - toggleClassName: function(element, className) { - if (!(element = $(element))) return; - return element[element.hasClassName(className) ? - 'removeClassName' : 'addClassName'](className); - }, - - // removes whitespace-only text node children - cleanWhitespace: function(element) { - element = $(element); - var node = element.firstChild; - while (node) { - var nextNode = node.nextSibling; - if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) - element.removeChild(node); - node = nextNode; - } - return element; - }, - - empty: function(element) { - return $(element).innerHTML.blank(); - }, - - descendantOf: function(element, ancestor) { - element = $(element), ancestor = $(ancestor); - var originalAncestor = ancestor; - - if (element.compareDocumentPosition) - return (element.compareDocumentPosition(ancestor) & 8) === 8; - - if (element.sourceIndex && !Prototype.Browser.Opera) { - var e = element.sourceIndex, a = ancestor.sourceIndex, - nextAncestor = ancestor.nextSibling; - if (!nextAncestor) { - do { ancestor = ancestor.parentNode; } - while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); - } - if (nextAncestor && nextAncestor.sourceIndex) - return (e > a && e < nextAncestor.sourceIndex); - } - - while (element = element.parentNode) - if (element == originalAncestor) return true; - return false; - }, - - scrollTo: function(element) { - element = $(element); - var pos = element.cumulativeOffset(); - window.scrollTo(pos[0], pos[1]); - return element; - }, - - getStyle: function(element, style) { - element = $(element); - style = style == 'float' ? 'cssFloat' : style.camelize(); - var value = element.style[style]; - if (!value) { - var css = document.defaultView.getComputedStyle(element, null); - value = css ? css[style] : null; - } - if (style == 'opacity') return value ? parseFloat(value) : 1.0; - return value == 'auto' ? null : value; - }, - - getOpacity: function(element) { - return $(element).getStyle('opacity'); - }, - - setStyle: function(element, styles) { - element = $(element); - var elementStyle = element.style, match; - if (Object.isString(styles)) { - element.style.cssText += ';' + styles; - return styles.include('opacity') ? - element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; - } - for (var property in styles) - if (property == 'opacity') element.setOpacity(styles[property]); - else - elementStyle[(property == 'float' || property == 'cssFloat') ? - (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : - property] = styles[property]; - - return element; - }, - - setOpacity: function(element, value) { - element = $(element); - element.style.opacity = (value == 1 || value === '') ? '' : - (value < 0.00001) ? 0 : value; - return element; - }, - - getDimensions: function(element) { - element = $(element); - var display = $(element).getStyle('display'); - if (display != 'none' && display != null) // Safari bug - return {width: element.offsetWidth, height: element.offsetHeight}; - - // All *Width and *Height properties give 0 on elements with display none, - // so enable the element temporarily - var els = element.style; - var originalVisibility = els.visibility; - var originalPosition = els.position; - var originalDisplay = els.display; - els.visibility = 'hidden'; - els.position = 'absolute'; - els.display = 'block'; - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - els.display = originalDisplay; - els.position = originalPosition; - els.visibility = originalVisibility; - return {width: originalWidth, height: originalHeight}; - }, - - makePositioned: function(element) { - element = $(element); - var pos = Element.getStyle(element, 'position'); - if (pos == 'static' || !pos) { - element._madePositioned = true; - element.style.position = 'relative'; - // Opera returns the offset relative to the positioning context, when an - // element is position relative but top and left have not been defined - if (window.opera) { - element.style.top = 0; - element.style.left = 0; - } - } - return element; - }, - - undoPositioned: function(element) { - element = $(element); - if (element._madePositioned) { - element._madePositioned = undefined; - element.style.position = - element.style.top = - element.style.left = - element.style.bottom = - element.style.right = ''; - } - return element; - }, - - makeClipping: function(element) { - element = $(element); - if (element._overflow) return element; - element._overflow = Element.getStyle(element, 'overflow') || 'auto'; - if (element._overflow !== 'hidden') - element.style.overflow = 'hidden'; - return element; - }, - - undoClipping: function(element) { - element = $(element); - if (!element._overflow) return element; - element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; - element._overflow = null; - return element; - }, - - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - positionedOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - if (element) { - if (element.tagName == 'BODY') break; - var p = Element.getStyle(element, 'position'); - if (p !== 'static') break; - } - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - absolutize: function(element) { - element = $(element); - if (element.getStyle('position') == 'absolute') return; - // Position.prepare(); // To be done manually by Scripty when it needs it. - - var offsets = element.positionedOffset(); - var top = offsets[1]; - var left = offsets[0]; - var width = element.clientWidth; - var height = element.clientHeight; - - element._originalLeft = left - parseFloat(element.style.left || 0); - element._originalTop = top - parseFloat(element.style.top || 0); - element._originalWidth = element.style.width; - element._originalHeight = element.style.height; - - element.style.position = 'absolute'; - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.width = width + 'px'; - element.style.height = height + 'px'; - return element; - }, - - relativize: function(element) { - element = $(element); - if (element.getStyle('position') == 'relative') return; - // Position.prepare(); // To be done manually by Scripty when it needs it. - - element.style.position = 'relative'; - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); - var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); - - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.height = element._originalHeight; - element.style.width = element._originalWidth; - return element; - }, - - cumulativeScrollOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - getOffsetParent: function(element) { - if (element.offsetParent) return $(element.offsetParent); - if (element == document.body) return $(element); - - while ((element = element.parentNode) && element != document.body) - if (Element.getStyle(element, 'position') != 'static') - return $(element); - - return $(document.body); - }, - - viewportOffset: function(forElement) { - var valueT = 0, valueL = 0; - - var element = forElement; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - - // Safari fix - if (element.offsetParent == document.body && - Element.getStyle(element, 'position') == 'absolute') break; - - } while (element = element.offsetParent); - - element = forElement; - do { - if (!Prototype.Browser.Opera || element.tagName == 'BODY') { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - } - } while (element = element.parentNode); - - return Element._returnOffset(valueL, valueT); - }, - - clonePosition: function(element, source) { - var options = Object.extend({ - setLeft: true, - setTop: true, - setWidth: true, - setHeight: true, - offsetTop: 0, - offsetLeft: 0 - }, arguments[2] || { }); - - // find page position of source - source = $(source); - var p = source.viewportOffset(); - - // find coordinate system to use - element = $(element); - var delta = [0, 0]; - var parent = null; - // delta [0,0] will do fine with position: fixed elements, - // position:absolute needs offsetParent deltas - if (Element.getStyle(element, 'position') == 'absolute') { - parent = element.getOffsetParent(); - delta = parent.viewportOffset(); - } - - // correct by body offsets (fixes Safari) - if (parent == document.body) { - delta[0] -= document.body.offsetLeft; - delta[1] -= document.body.offsetTop; - } - - // set position - if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; - if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; - if (options.setWidth) element.style.width = source.offsetWidth + 'px'; - if (options.setHeight) element.style.height = source.offsetHeight + 'px'; - return element; - } -}; - -Element.Methods.identify.counter = 1; - -Object.extend(Element.Methods, { - getElementsBySelector: Element.Methods.select, - childElements: Element.Methods.immediateDescendants -}); - -Element._attributeTranslations = { - write: { - names: { - className: 'class', - htmlFor: 'for' - }, - values: { } - } -}; - -if (Prototype.Browser.Opera) { - Element.Methods.getStyle = Element.Methods.getStyle.wrap( - function(proceed, element, style) { - switch (style) { - case 'left': case 'top': case 'right': case 'bottom': - if (proceed(element, 'position') === 'static') return null; - case 'height': case 'width': - // returns '0px' for hidden elements; we want it to return null - if (!Element.visible(element)) return null; - - // returns the border-box dimensions rather than the content-box - // dimensions, so we subtract padding and borders from the value - var dim = parseInt(proceed(element, style), 10); - - if (dim !== element['offset' + style.capitalize()]) - return dim + 'px'; - - var properties; - if (style === 'height') { - properties = ['border-top-width', 'padding-top', - 'padding-bottom', 'border-bottom-width']; - } - else { - properties = ['border-left-width', 'padding-left', - 'padding-right', 'border-right-width']; - } - return properties.inject(dim, function(memo, property) { - var val = proceed(element, property); - return val === null ? memo : memo - parseInt(val, 10); - }) + 'px'; - default: return proceed(element, style); - } - } - ); - - Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( - function(proceed, element, attribute) { - if (attribute === 'title') return element.title; - return proceed(element, attribute); - } - ); -} - -else if (Prototype.Browser.IE) { - // IE doesn't report offsets correctly for static elements, so we change them - // to "relative" to get the values, then change them back. - Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( - function(proceed, element) { - element = $(element); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - - $w('positionedOffset viewportOffset').each(function(method) { - Element.Methods[method] = Element.Methods[method].wrap( - function(proceed, element) { - element = $(element); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - // Trigger hasLayout on the offset parent so that IE6 reports - // accurate offsetTop and offsetLeft values for position: fixed. - var offsetParent = element.getOffsetParent(); - if (offsetParent && offsetParent.getStyle('position') === 'fixed') - offsetParent.setStyle({ zoom: 1 }); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - }); - - Element.Methods.getStyle = function(element, style) { - element = $(element); - style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); - var value = element.style[style]; - if (!value && element.currentStyle) value = element.currentStyle[style]; - - if (style == 'opacity') { - if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) - if (value[1]) return parseFloat(value[1]) / 100; - return 1.0; - } - - if (value == 'auto') { - if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) - return element['offset' + style.capitalize()] + 'px'; - return null; - } - return value; - }; - - Element.Methods.setOpacity = function(element, value) { - function stripAlpha(filter){ - return filter.replace(/alpha\([^\)]*\)/gi,''); - } - element = $(element); - var currentStyle = element.currentStyle; - if ((currentStyle && !currentStyle.hasLayout) || - (!currentStyle && element.style.zoom == 'normal')) - element.style.zoom = 1; - - var filter = element.getStyle('filter'), style = element.style; - if (value == 1 || value === '') { - (filter = stripAlpha(filter)) ? - style.filter = filter : style.removeAttribute('filter'); - return element; - } else if (value < 0.00001) value = 0; - style.filter = stripAlpha(filter) + - 'alpha(opacity=' + (value * 100) + ')'; - return element; - }; - - Element._attributeTranslations = { - read: { - names: { - 'class': 'className', - 'for': 'htmlFor' - }, - values: { - _getAttr: function(element, attribute) { - return element.getAttribute(attribute, 2); - }, - _getAttrNode: function(element, attribute) { - var node = element.getAttributeNode(attribute); - return node ? node.value : ""; - }, - _getEv: function(element, attribute) { - attribute = element.getAttribute(attribute); - return attribute ? attribute.toString().slice(23, -2) : null; - }, - _flag: function(element, attribute) { - return $(element).hasAttribute(attribute) ? attribute : null; - }, - style: function(element) { - return element.style.cssText.toLowerCase(); - }, - title: function(element) { - return element.title; - } - } - } - }; - - Element._attributeTranslations.write = { - names: Object.extend({ - cellpadding: 'cellPadding', - cellspacing: 'cellSpacing' - }, Element._attributeTranslations.read.names), - values: { - checked: function(element, value) { - element.checked = !!value; - }, - - style: function(element, value) { - element.style.cssText = value ? value : ''; - } - } - }; - - Element._attributeTranslations.has = {}; - - $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + - 'encType maxLength readOnly longDesc').each(function(attr) { - Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; - Element._attributeTranslations.has[attr.toLowerCase()] = attr; - }); - - (function(v) { - Object.extend(v, { - href: v._getAttr, - src: v._getAttr, - type: v._getAttr, - action: v._getAttrNode, - disabled: v._flag, - checked: v._flag, - readonly: v._flag, - multiple: v._flag, - onload: v._getEv, - onunload: v._getEv, - onclick: v._getEv, - ondblclick: v._getEv, - onmousedown: v._getEv, - onmouseup: v._getEv, - onmouseover: v._getEv, - onmousemove: v._getEv, - onmouseout: v._getEv, - onfocus: v._getEv, - onblur: v._getEv, - onkeypress: v._getEv, - onkeydown: v._getEv, - onkeyup: v._getEv, - onsubmit: v._getEv, - onreset: v._getEv, - onselect: v._getEv, - onchange: v._getEv - }); - })(Element._attributeTranslations.read.values); -} - -else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { - Element.Methods.setOpacity = function(element, value) { - element = $(element); - element.style.opacity = (value == 1) ? 0.999999 : - (value === '') ? '' : (value < 0.00001) ? 0 : value; - return element; - }; -} - -else if (Prototype.Browser.WebKit) { - Element.Methods.setOpacity = function(element, value) { - element = $(element); - element.style.opacity = (value == 1 || value === '') ? '' : - (value < 0.00001) ? 0 : value; - - if (value == 1) - if(element.tagName == 'IMG' && element.width) { - element.width++; element.width--; - } else try { - var n = document.createTextNode(' '); - element.appendChild(n); - element.removeChild(n); - } catch (e) { } - - return element; - }; - - // Safari returns margins on body which is incorrect if the child is absolutely - // positioned. For performance reasons, redefine Element#cumulativeOffset for - // KHTML/WebKit only. - Element.Methods.cumulativeOffset = function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - if (element.offsetParent == document.body) - if (Element.getStyle(element, 'position') == 'absolute') break; - - element = element.offsetParent; - } while (element); - - return Element._returnOffset(valueL, valueT); - }; -} - -if (Prototype.Browser.IE || Prototype.Browser.Opera) { - // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements - Element.Methods.update = function(element, content) { - element = $(element); - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) return element.update().insert(content); - - content = Object.toHTML(content); - var tagName = element.tagName.toUpperCase(); - - if (tagName in Element._insertionTranslations.tags) { - $A(element.childNodes).each(function(node) { element.removeChild(node) }); - Element._getContentFromAnonymousElement(tagName, content.stripScripts()) - .each(function(node) { element.appendChild(node) }); - } - else element.innerHTML = content.stripScripts(); - - content.evalScripts.bind(content).defer(); - return element; - }; -} - -if ('outerHTML' in document.createElement('div')) { - Element.Methods.replace = function(element, content) { - element = $(element); - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - element.parentNode.replaceChild(content, element); - return element; - } - - content = Object.toHTML(content); - var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); - - if (Element._insertionTranslations.tags[tagName]) { - var nextSibling = element.next(); - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - parent.removeChild(element); - if (nextSibling) - fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); - else - fragments.each(function(node) { parent.appendChild(node) }); - } - else element.outerHTML = content.stripScripts(); - - content.evalScripts.bind(content).defer(); - return element; - }; -} - -Element._returnOffset = function(l, t) { - var result = [l, t]; - result.left = l; - result.top = t; - return result; -}; - -Element._getContentFromAnonymousElement = function(tagName, html) { - var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; - if (t) { - div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); - } else div.innerHTML = html; - return $A(div.childNodes); -}; - -Element._insertionTranslations = { - before: function(element, node) { - element.parentNode.insertBefore(node, element); - }, - top: function(element, node) { - element.insertBefore(node, element.firstChild); - }, - bottom: function(element, node) { - element.appendChild(node); - }, - after: function(element, node) { - element.parentNode.insertBefore(node, element.nextSibling); - }, - tags: { - TABLE: ['<table>', '</table>', 1], - TBODY: ['<table><tbody>', '</tbody></table>', 2], - TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3], - TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], - SELECT: ['<select>', '</select>', 1] - } -}; - -(function() { - Object.extend(this.tags, { - THEAD: this.tags.TBODY, - TFOOT: this.tags.TBODY, - TH: this.tags.TD - }); -}).call(Element._insertionTranslations); - -Element.Methods.Simulated = { - hasAttribute: function(element, attribute) { - attribute = Element._attributeTranslations.has[attribute] || attribute; - var node = $(element).getAttributeNode(attribute); - return node && node.specified; - } -}; - -Element.Methods.ByTag = { }; - -Object.extend(Element, Element.Methods); - -if (!Prototype.BrowserFeatures.ElementExtensions && - document.createElement('div').__proto__) { - window.HTMLElement = { }; - window.HTMLElement.prototype = document.createElement('div').__proto__; - Prototype.BrowserFeatures.ElementExtensions = true; -} - -Element.extend = (function() { - if (Prototype.BrowserFeatures.SpecificElementExtensions) - return Prototype.K; - - var Methods = { }, ByTag = Element.Methods.ByTag; - - var extend = Object.extend(function(element) { - if (!element || element._extendedByPrototype || - element.nodeType != 1 || element == window) return element; - - var methods = Object.clone(Methods), - tagName = element.tagName, property, value; - - // extend methods for specific tags - if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); - - for (property in methods) { - value = methods[property]; - if (Object.isFunction(value) && !(property in element)) - element[property] = value.methodize(); - } - - element._extendedByPrototype = Prototype.emptyFunction; - return element; - - }, { - refresh: function() { - // extend methods for all tags (Safari doesn't need this) - if (!Prototype.BrowserFeatures.ElementExtensions) { - Object.extend(Methods, Element.Methods); - Object.extend(Methods, Element.Methods.Simulated); - } - } - }); - - extend.refresh(); - return extend; -})(); - -Element.hasAttribute = function(element, attribute) { - if (element.hasAttribute) return element.hasAttribute(attribute); - return Element.Methods.Simulated.hasAttribute(element, attribute); -}; - -Element.addMethods = function(methods) { - var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; - - if (!methods) { - Object.extend(Form, Form.Methods); - Object.extend(Form.Element, Form.Element.Methods); - Object.extend(Element.Methods.ByTag, { - "FORM": Object.clone(Form.Methods), - "INPUT": Object.clone(Form.Element.Methods), - "SELECT": Object.clone(Form.Element.Methods), - "TEXTAREA": Object.clone(Form.Element.Methods) - }); - } - - if (arguments.length == 2) { - var tagName = methods; - methods = arguments[1]; - } - - if (!tagName) Object.extend(Element.Methods, methods || { }); - else { - if (Object.isArray(tagName)) tagName.each(extend); - else extend(tagName); - } - - function extend(tagName) { - tagName = tagName.toUpperCase(); - if (!Element.Methods.ByTag[tagName]) - Element.Methods.ByTag[tagName] = { }; - Object.extend(Element.Methods.ByTag[tagName], methods); - } - - function copy(methods, destination, onlyIfAbsent) { - onlyIfAbsent = onlyIfAbsent || false; - for (var property in methods) { - var value = methods[property]; - if (!Object.isFunction(value)) continue; - if (!onlyIfAbsent || !(property in destination)) - destination[property] = value.methodize(); - } - } - - function findDOMClass(tagName) { - var klass; - var trans = { - "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", - "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", - "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", - "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", - "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": - "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": - "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": - "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": - "FrameSet", "IFRAME": "IFrame" - }; - if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName.capitalize() + 'Element'; - if (window[klass]) return window[klass]; - - window[klass] = { }; - window[klass].prototype = document.createElement(tagName).__proto__; - return window[klass]; - } - - if (F.ElementExtensions) { - copy(Element.Methods, HTMLElement.prototype); - copy(Element.Methods.Simulated, HTMLElement.prototype, true); - } - - if (F.SpecificElementExtensions) { - for (var tag in Element.Methods.ByTag) { - var klass = findDOMClass(tag); - if (Object.isUndefined(klass)) continue; - copy(T[tag], klass.prototype); - } - } - - Object.extend(Element, Element.Methods); - delete Element.ByTag; - - if (Element.extend.refresh) Element.extend.refresh(); - Element.cache = { }; -}; - -document.viewport = { - getDimensions: function() { - var dimensions = { }; - var B = Prototype.Browser; - $w('width height').each(function(d) { - var D = d.capitalize(); - dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : - (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; - }); - return dimensions; - }, - - getWidth: function() { - return this.getDimensions().width; - }, - - getHeight: function() { - return this.getDimensions().height; - }, - - getScrollOffsets: function() { - return Element._returnOffset( - window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, - window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); - } -}; -/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, - * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style - * license. Please see http://www.yui-ext.com/ for more information. */ - -var Selector = Class.create({ - initialize: function(expression) { - this.expression = expression.strip(); - this.compileMatcher(); - }, - - shouldUseXPath: function() { - if (!Prototype.BrowserFeatures.XPath) return false; - - var e = this.expression; - - // Safari 3 chokes on :*-of-type and :empty - if (Prototype.Browser.WebKit && - (e.include("-of-type") || e.include(":empty"))) - return false; - - // XPath can't do namespaced attributes, nor can it read - // the "checked" property from DOM nodes - if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) - return false; - - return true; - }, - - compileMatcher: function() { - if (this.shouldUseXPath()) - return this.compileXPathMatcher(); - - var e = this.expression, ps = Selector.patterns, h = Selector.handlers, - c = Selector.criteria, le, p, m; - - if (Selector._cache[e]) { - this.matcher = Selector._cache[e]; - return; - } - - this.matcher = ["this.matcher = function(root) {", - "var r = root, h = Selector.handlers, c = false, n;"]; - - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - p = ps[i]; - if (m = e.match(p)) { - this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : - new Template(c[i]).evaluate(m)); - e = e.replace(m[0], ''); - break; - } - } - } - - this.matcher.push("return h.unique(n);\n}"); - eval(this.matcher.join('\n')); - Selector._cache[this.expression] = this.matcher; - }, - - compileXPathMatcher: function() { - var e = this.expression, ps = Selector.patterns, - x = Selector.xpath, le, m; - - if (Selector._cache[e]) { - this.xpath = Selector._cache[e]; return; - } - - this.matcher = ['.//*']; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - if (m = e.match(ps[i])) { - this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : - new Template(x[i]).evaluate(m)); - e = e.replace(m[0], ''); - break; - } - } - } - - this.xpath = this.matcher.join(''); - Selector._cache[this.expression] = this.xpath; - }, - - findElements: function(root) { - root = root || document; - if (this.xpath) return document._getElementsByXPath(this.xpath, root); - return this.matcher(root); - }, - - match: function(element) { - this.tokens = []; - - var e = this.expression, ps = Selector.patterns, as = Selector.assertions; - var le, p, m; - - while (e && le !== e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - p = ps[i]; - if (m = e.match(p)) { - // use the Selector.assertions methods unless the selector - // is too complex. - if (as[i]) { - this.tokens.push([i, Object.clone(m)]); - e = e.replace(m[0], ''); - } else { - // reluctantly do a document-wide search - // and look for a match in the array - return this.findElements(document).include(element); - } - } - } - } - - var match = true, name, matches; - for (var i = 0, token; token = this.tokens[i]; i++) { - name = token[0], matches = token[1]; - if (!Selector.assertions[name](element, matches)) { - match = false; break; - } - } - - return match; - }, - - toString: function() { - return this.expression; - }, - - inspect: function() { - return "#<Selector:" + this.expression.inspect() + ">"; - } -}); - -Object.extend(Selector, { - _cache: { }, - - xpath: { - descendant: "//*", - child: "/*", - adjacent: "/following-sibling::*[1]", - laterSibling: '/following-sibling::*', - tagName: function(m) { - if (m[1] == '*') return ''; - return "[local-name()='" + m[1].toLowerCase() + - "' or local-name()='" + m[1].toUpperCase() + "']"; - }, - className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", - id: "[@id='#{1}']", - attrPresence: function(m) { - m[1] = m[1].toLowerCase(); - return new Template("[@#{1}]").evaluate(m); - }, - attr: function(m) { - m[1] = m[1].toLowerCase(); - m[3] = m[5] || m[6]; - return new Template(Selector.xpath.operators[m[2]]).evaluate(m); - }, - pseudo: function(m) { - var h = Selector.xpath.pseudos[m[1]]; - if (!h) return ''; - if (Object.isFunction(h)) return h(m); - return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); - }, - operators: { - '=': "[@#{1}='#{3}']", - '!=': "[@#{1}!='#{3}']", - '^=': "[starts-with(@#{1}, '#{3}')]", - '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", - '*=': "[contains(@#{1}, '#{3}')]", - '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", - '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" - }, - pseudos: { - 'first-child': '[not(preceding-sibling::*)]', - 'last-child': '[not(following-sibling::*)]', - 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', - 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", - 'checked': "[@checked]", - 'disabled': "[@disabled]", - 'enabled': "[not(@disabled)]", - 'not': function(m) { - var e = m[6], p = Selector.patterns, - x = Selector.xpath, le, v; - - var exclusion = []; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in p) { - if (m = e.match(p[i])) { - v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); - exclusion.push("(" + v.substring(1, v.length - 1) + ")"); - e = e.replace(m[0], ''); - break; - } - } - } - return "[not(" + exclusion.join(" and ") + ")]"; - }, - 'nth-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); - }, - 'nth-last-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); - }, - 'nth-of-type': function(m) { - return Selector.xpath.pseudos.nth("position() ", m); - }, - 'nth-last-of-type': function(m) { - return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); - }, - 'first-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); - }, - 'last-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); - }, - 'only-of-type': function(m) { - var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); - }, - nth: function(fragment, m) { - var mm, formula = m[6], predicate; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - if (mm = formula.match(/^(\d+)$/)) // digit only - return '[' + fragment + "= " + mm[1] + ']'; - if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (mm[1] == "-") mm[1] = -1; - var a = mm[1] ? Number(mm[1]) : 1; - var b = mm[2] ? Number(mm[2]) : 0; - predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + - "((#{fragment} - #{b}) div #{a} >= 0)]"; - return new Template(predicate).evaluate({ - fragment: fragment, a: a, b: b }); - } - } - } - }, - - criteria: { - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', - className: 'n = h.className(n, r, "#{1}", c); c = false;', - id: 'n = h.id(n, r, "#{1}", c); c = false;', - attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', - attr: function(m) { - m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); - }, - pseudo: function(m) { - if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); - return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); - }, - descendant: 'c = "descendant";', - child: 'c = "child";', - adjacent: 'c = "adjacent";', - laterSibling: 'c = "laterSibling";' - }, - - patterns: { - // combinators must be listed first - // (and descendant needs to be last combinator) - laterSibling: /^\s*~\s*/, - child: /^\s*>\s*/, - adjacent: /^\s*\+\s*/, - descendant: /^\s/, - - // selectors follow - tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, - id: /^#([\w\-\*]+)(\b|$)/, - className: /^\.([\w\-\*]+)(\b|$)/, - pseudo: -/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, - attrPresence: /^\[([\w]+)\]/, - attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ - }, - - // for Selector.match and Element#match - assertions: { - tagName: function(element, matches) { - return matches[1].toUpperCase() == element.tagName.toUpperCase(); - }, - - className: function(element, matches) { - return Element.hasClassName(element, matches[1]); - }, - - id: function(element, matches) { - return element.id === matches[1]; - }, - - attrPresence: function(element, matches) { - return Element.hasAttribute(element, matches[1]); - }, - - attr: function(element, matches) { - var nodeValue = Element.readAttribute(element, matches[1]); - return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); - } - }, - - handlers: { - // UTILITY FUNCTIONS - // joins two collections - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - a.push(node); - return a; - }, - - // marks an array of nodes for counting - mark: function(nodes) { - var _true = Prototype.emptyFunction; - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = _true; - return nodes; - }, - - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = undefined; - return nodes; - }, - - // mark each child node with its position (for nth calls) - // "ofType" flag indicates whether we're indexing for nth-of-type - // rather than nth-child - index: function(parentNode, reverse, ofType) { - parentNode._countedByPrototype = Prototype.emptyFunction; - if (reverse) { - for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { - var node = nodes[i]; - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - } else { - for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - }, - - // filters out duplicates and extends all nodes - unique: function(nodes) { - if (nodes.length == 0) return nodes; - var results = [], n; - for (var i = 0, l = nodes.length; i < l; i++) - if (!(n = nodes[i])._countedByPrototype) { - n._countedByPrototype = Prototype.emptyFunction; - results.push(Element.extend(n)); - } - return Selector.handlers.unmark(results); - }, - - // COMBINATOR FUNCTIONS - descendant: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName('*')); - return results; - }, - - child: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) { - for (var j = 0, child; child = node.childNodes[j]; j++) - if (child.nodeType == 1 && child.tagName != '!') results.push(child); - } - return results; - }, - - adjacent: function(nodes) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - var next = this.nextElementSibling(node); - if (next) results.push(next); - } - return results; - }, - - laterSibling: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, Element.nextSiblings(node)); - return results; - }, - - nextElementSibling: function(node) { - while (node = node.nextSibling) - if (node.nodeType == 1) return node; - return null; - }, - - previousElementSibling: function(node) { - while (node = node.previousSibling) - if (node.nodeType == 1) return node; - return null; - }, - - // TOKEN FUNCTIONS - tagName: function(nodes, root, tagName, combinator) { - var uTagName = tagName.toUpperCase(); - var results = [], h = Selector.handlers; - if (nodes) { - if (combinator) { - // fastlane for ordinary descendant combinators - if (combinator == "descendant") { - for (var i = 0, node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName(tagName)); - return results; - } else nodes = this[combinator](nodes); - if (tagName == "*") return nodes; - } - for (var i = 0, node; node = nodes[i]; i++) - if (node.tagName.toUpperCase() === uTagName) results.push(node); - return results; - } else return root.getElementsByTagName(tagName); - }, - - id: function(nodes, root, id, combinator) { - var targetNode = $(id), h = Selector.handlers; - if (!targetNode) return []; - if (!nodes && root == document) return [targetNode]; - if (nodes) { - if (combinator) { - if (combinator == 'child') { - for (var i = 0, node; node = nodes[i]; i++) - if (targetNode.parentNode == node) return [targetNode]; - } else if (combinator == 'descendant') { - for (var i = 0, node; node = nodes[i]; i++) - if (Element.descendantOf(targetNode, node)) return [targetNode]; - } else if (combinator == 'adjacent') { - for (var i = 0, node; node = nodes[i]; i++) - if (Selector.handlers.previousElementSibling(targetNode) == node) - return [targetNode]; - } else nodes = h[combinator](nodes); - } - for (var i = 0, node; node = nodes[i]; i++) - if (node == targetNode) return [targetNode]; - return []; - } - return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; - }, - - className: function(nodes, root, className, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - return Selector.handlers.byClassName(nodes, root, className); - }, - - byClassName: function(nodes, root, className) { - if (!nodes) nodes = Selector.handlers.descendant([root]); - var needle = ' ' + className + ' '; - for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { - nodeClassName = node.className; - if (nodeClassName.length == 0) continue; - if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) - results.push(node); - } - return results; - }, - - attrPresence: function(nodes, root, attr, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var results = []; - for (var i = 0, node; node = nodes[i]; i++) - if (Element.hasAttribute(node, attr)) results.push(node); - return results; - }, - - attr: function(nodes, root, attr, value, operator, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var handler = Selector.operators[operator], results = []; - for (var i = 0, node; node = nodes[i]; i++) { - var nodeValue = Element.readAttribute(node, attr); - if (nodeValue === null) continue; - if (handler(nodeValue, value)) results.push(node); - } - return results; - }, - - pseudo: function(nodes, name, value, root, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - if (!nodes) nodes = root.getElementsByTagName("*"); - return Selector.pseudos[name](nodes, value, root); - } - }, - - pseudos: { - 'first-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.previousElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'last-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.nextElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'only-child': function(nodes, value, root) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) - results.push(node); - return results; - }, - 'nth-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root); - }, - 'nth-last-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true); - }, - 'nth-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, false, true); - }, - 'nth-last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true, true); - }, - 'first-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, false, true); - }, - 'last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, true, true); - }, - 'only-of-type': function(nodes, formula, root) { - var p = Selector.pseudos; - return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); - }, - - // handles the an+b logic - getIndices: function(a, b, total) { - if (a == 0) return b > 0 ? [b] : []; - return $R(1, total).inject([], function(memo, i) { - if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); - return memo; - }); - }, - - // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type - nth: function(nodes, formula, root, reverse, ofType) { - if (nodes.length == 0) return []; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - var h = Selector.handlers, results = [], indexed = [], m; - h.mark(nodes); - for (var i = 0, node; node = nodes[i]; i++) { - if (!node.parentNode._countedByPrototype) { - h.index(node.parentNode, reverse, ofType); - indexed.push(node.parentNode); - } - } - if (formula.match(/^\d+$/)) { // just a number - formula = Number(formula); - for (var i = 0, node; node = nodes[i]; i++) - if (node.nodeIndex == formula) results.push(node); - } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (m[1] == "-") m[1] = -1; - var a = m[1] ? Number(m[1]) : 1; - var b = m[2] ? Number(m[2]) : 0; - var indices = Selector.pseudos.getIndices(a, b, nodes.length); - for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { - for (var j = 0; j < l; j++) - if (node.nodeIndex == indices[j]) results.push(node); - } - } - h.unmark(nodes); - h.unmark(indexed); - return results; - }, - - 'empty': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - // IE treats comments as element nodes - if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; - results.push(node); - } - return results; - }, - - 'not': function(nodes, selector, root) { - var h = Selector.handlers, selectorType, m; - var exclusions = new Selector(selector).findElements(root); - h.mark(exclusions); - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node._countedByPrototype) results.push(node); - h.unmark(exclusions); - return results; - }, - - 'enabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node.disabled) results.push(node); - return results; - }, - - 'disabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.disabled) results.push(node); - return results; - }, - - 'checked': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.checked) results.push(node); - return results; - } - }, - - operators: { - '=': function(nv, v) { return nv == v; }, - '!=': function(nv, v) { return nv != v; }, - '^=': function(nv, v) { return nv.startsWith(v); }, - '$=': function(nv, v) { return nv.endsWith(v); }, - '*=': function(nv, v) { return nv.include(v); }, - '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, - '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } - }, - - split: function(expression) { - var expressions = []; - expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); - }); - return expressions; - }, - - matchElements: function(elements, expression) { - var matches = $$(expression), h = Selector.handlers; - h.mark(matches); - for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._countedByPrototype) results.push(element); - h.unmark(matches); - return results; - }, - - findElement: function(elements, expression, index) { - if (Object.isNumber(expression)) { - index = expression; expression = false; - } - return Selector.matchElements(elements, expression || '*')[index || 0]; - }, - - findChildElements: function(element, expressions) { - expressions = Selector.split(expressions.join(',')); - var results = [], h = Selector.handlers; - for (var i = 0, l = expressions.length, selector; i < l; i++) { - selector = new Selector(expressions[i].strip()); - h.concat(results, selector.findElements(element)); - } - return (l > 1) ? h.unique(results) : results; - } -}); - -if (Prototype.Browser.IE) { - Object.extend(Selector.handlers, { - // IE returns comment nodes on getElementsByTagName("*"). - // Filter them out. - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - }, - - // IE improperly serializes _countedByPrototype in (inner|outer)HTML. - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node.removeAttribute('_countedByPrototype'); - return nodes; - } - }); -} - -function $$() { - return Selector.findChildElements(document, $A(arguments)); -} -var Form = { - reset: function(form) { - $(form).reset(); - return form; - }, - - serializeElements: function(elements, options) { - if (typeof options != 'object') options = { hash: !!options }; - else if (Object.isUndefined(options.hash)) options.hash = true; - var key, value, submitted = false, submit = options.submit; - - var data = elements.inject({ }, function(result, element) { - if (!element.disabled && element.name) { - key = element.name; value = $(element).getValue(); - if (value != null && (element.type != 'submit' || (!submitted && - submit !== false && (!submit || key == submit) && (submitted = true)))) { - if (key in result) { - // a key is already present; construct an array of values - if (!Object.isArray(result[key])) result[key] = [result[key]]; - result[key].push(value); - } - else result[key] = value; - } - } - return result; - }); - - return options.hash ? data : Object.toQueryString(data); - } -}; - -Form.Methods = { - serialize: function(form, options) { - return Form.serializeElements(Form.getElements(form), options); - }, - - getElements: function(form) { - return $A($(form).getElementsByTagName('*')).inject([], - function(elements, child) { - if (Form.Element.Serializers[child.tagName.toLowerCase()]) - elements.push(Element.extend(child)); - return elements; - } - ); - }, - - getInputs: function(form, typeName, name) { - form = $(form); - var inputs = form.getElementsByTagName('input'); - - if (!typeName && !name) return $A(inputs).map(Element.extend); - - for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { - var input = inputs[i]; - if ((typeName && input.type != typeName) || (name && input.name != name)) - continue; - matchingInputs.push(Element.extend(input)); - } - - return matchingInputs; - }, - - disable: function(form) { - form = $(form); - Form.getElements(form).invoke('disable'); - return form; - }, - - enable: function(form) { - form = $(form); - Form.getElements(form).invoke('enable'); - return form; - }, - - findFirstElement: function(form) { - var elements = $(form).getElements().findAll(function(element) { - return 'hidden' != element.type && !element.disabled; - }); - var firstByIndex = elements.findAll(function(element) { - return element.hasAttribute('tabIndex') && element.tabIndex >= 0; - }).sortBy(function(element) { return element.tabIndex }).first(); - - return firstByIndex ? firstByIndex : elements.find(function(element) { - return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); - }); - }, - - focusFirstElement: function(form) { - form = $(form); - form.findFirstElement().activate(); - return form; - }, - - request: function(form, options) { - form = $(form), options = Object.clone(options || { }); - - var params = options.parameters, action = form.readAttribute('action') || ''; - if (action.blank()) action = window.location.href; - options.parameters = form.serialize(true); - - if (params) { - if (Object.isString(params)) params = params.toQueryParams(); - Object.extend(options.parameters, params); - } - - if (form.hasAttribute('method') && !options.method) - options.method = form.method; - - return new Ajax.Request(action, options); - } -}; - -/*--------------------------------------------------------------------------*/ - -Form.Element = { - focus: function(element) { - $(element).focus(); - return element; - }, - - select: function(element) { - $(element).select(); - return element; - } -}; - -Form.Element.Methods = { - serialize: function(element) { - element = $(element); - if (!element.disabled && element.name) { - var value = element.getValue(); - if (value != undefined) { - var pair = { }; - pair[element.name] = value; - return Object.toQueryString(pair); - } - } - return ''; - }, - - getValue: function(element) { - element = $(element); - var method = element.tagName.toLowerCase(); - return Form.Element.Serializers[method](element); - }, - - setValue: function(element, value) { - element = $(element); - var method = element.tagName.toLowerCase(); - Form.Element.Serializers[method](element, value); - return element; - }, - - clear: function(element) { - $(element).value = ''; - return element; - }, - - present: function(element) { - return $(element).value != ''; - }, - - activate: function(element) { - element = $(element); - try { - element.focus(); - if (element.select && (element.tagName.toLowerCase() != 'input' || - !['button', 'reset', 'submit'].include(element.type))) - element.select(); - } catch (e) { } - return element; - }, - - disable: function(element) { - element = $(element); - element.blur(); - element.disabled = true; - return element; - }, - - enable: function(element) { - element = $(element); - element.disabled = false; - return element; - } -}; - -/*--------------------------------------------------------------------------*/ - -var Field = Form.Element; -var $F = Form.Element.Methods.getValue; - -/*--------------------------------------------------------------------------*/ - -Form.Element.Serializers = { - input: function(element, value) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - return Form.Element.Serializers.inputSelector(element, value); - default: - return Form.Element.Serializers.textarea(element, value); - } - }, - - inputSelector: function(element, value) { - if (Object.isUndefined(value)) return element.checked ? element.value : null; - else element.checked = !!value; - }, - - textarea: function(element, value) { - if (Object.isUndefined(value)) return element.value; - else element.value = value; - }, - - select: function(element, index) { - if (Object.isUndefined(index)) - return this[element.type == 'select-one' ? - 'selectOne' : 'selectMany'](element); - else { - var opt, value, single = !Object.isArray(index); - for (var i = 0, length = element.length; i < length; i++) { - opt = element.options[i]; - value = this.optionValue(opt); - if (single) { - if (value == index) { - opt.selected = true; - return; - } - } - else opt.selected = index.include(value); - } - } - }, - - selectOne: function(element) { - var index = element.selectedIndex; - return index >= 0 ? this.optionValue(element.options[index]) : null; - }, - - selectMany: function(element) { - var values, length = element.length; - if (!length) return null; - - for (var i = 0, values = []; i < length; i++) { - var opt = element.options[i]; - if (opt.selected) values.push(this.optionValue(opt)); - } - return values; - }, - - optionValue: function(opt) { - // extend element because hasAttribute may not be native - return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; - } -}; - -/*--------------------------------------------------------------------------*/ - -Abstract.TimedObserver = Class.create(PeriodicalExecuter, { - initialize: function($super, element, frequency, callback) { - $super(callback, frequency); - this.element = $(element); - this.lastValue = this.getValue(); - }, - - execute: function() { - var value = this.getValue(); - if (Object.isString(this.lastValue) && Object.isString(value) ? - this.lastValue != value : String(this.lastValue) != String(value)) { - this.callback(this.element, value); - this.lastValue = value; - } - } -}); - -Form.Element.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); - -/*--------------------------------------------------------------------------*/ - -Abstract.EventObserver = Class.create({ - initialize: function(element, callback) { - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - if (this.element.tagName.toLowerCase() == 'form') - this.registerFormCallbacks(); - else - this.registerCallback(this.element); - }, - - onElementEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - }, - - registerFormCallbacks: function() { - Form.getElements(this.element).each(this.registerCallback, this); - }, - - registerCallback: function(element) { - if (element.type) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - Event.observe(element, 'click', this.onElementEvent.bind(this)); - break; - default: - Event.observe(element, 'change', this.onElementEvent.bind(this)); - break; - } - } - } -}); - -Form.Element.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); -if (!window.Event) var Event = { }; - -Object.extend(Event, { - KEY_BACKSPACE: 8, - KEY_TAB: 9, - KEY_RETURN: 13, - KEY_ESC: 27, - KEY_LEFT: 37, - KEY_UP: 38, - KEY_RIGHT: 39, - KEY_DOWN: 40, - KEY_DELETE: 46, - KEY_HOME: 36, - KEY_END: 35, - KEY_PAGEUP: 33, - KEY_PAGEDOWN: 34, - KEY_INSERT: 45, - - cache: { }, - - relatedTarget: function(event) { - var element; - switch(event.type) { - case 'mouseover': element = event.fromElement; break; - case 'mouseout': element = event.toElement; break; - default: return null; - } - return Element.extend(element); - } -}); - -Event.Methods = (function() { - var isButton; - - if (Prototype.Browser.IE) { - var buttonMap = { 0: 1, 1: 4, 2: 2 }; - isButton = function(event, code) { - return event.button == buttonMap[code]; - }; - - } else if (Prototype.Browser.WebKit) { - isButton = function(event, code) { - switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 1 && event.metaKey; - default: return false; - } - }; - - } else { - isButton = function(event, code) { - return event.which ? (event.which === code + 1) : (event.button === code); - }; - } - - return { - isLeftClick: function(event) { return isButton(event, 0) }, - isMiddleClick: function(event) { return isButton(event, 1) }, - isRightClick: function(event) { return isButton(event, 2) }, - - element: function(event) { - var node = Event.extend(event).target; - return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); - }, - - findElement: function(event, expression) { - var element = Event.element(event); - if (!expression) return element; - var elements = [element].concat(element.ancestors()); - return Selector.findElement(elements, expression, 0); - }, - - pointer: function(event) { - return { - x: event.pageX || (event.clientX + - (document.documentElement.scrollLeft || document.body.scrollLeft)), - y: event.pageY || (event.clientY + - (document.documentElement.scrollTop || document.body.scrollTop)) - }; - }, - - pointerX: function(event) { return Event.pointer(event).x }, - pointerY: function(event) { return Event.pointer(event).y }, - - stop: function(event) { - Event.extend(event); - event.preventDefault(); - event.stopPropagation(); - event.stopped = true; - } - }; -})(); - -Event.extend = (function() { - var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { - m[name] = Event.Methods[name].methodize(); - return m; - }); - - if (Prototype.Browser.IE) { - Object.extend(methods, { - stopPropagation: function() { this.cancelBubble = true }, - preventDefault: function() { this.returnValue = false }, - inspect: function() { return "[object Event]" } - }); - - return function(event) { - if (!event) return false; - if (event._extendedByPrototype) return event; - - event._extendedByPrototype = Prototype.emptyFunction; - var pointer = Event.pointer(event); - Object.extend(event, { - target: event.srcElement, - relatedTarget: Event.relatedTarget(event), - pageX: pointer.x, - pageY: pointer.y - }); - return Object.extend(event, methods); - }; - - } else { - Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; - Object.extend(Event.prototype, methods); - return Prototype.K; - } -})(); - -Object.extend(Event, (function() { - var cache = Event.cache; - - function getEventID(element) { - if (element._prototypeEventID) return element._prototypeEventID[0]; - arguments.callee.id = arguments.callee.id || 1; - return element._prototypeEventID = [++arguments.callee.id]; - } - - function getDOMEventName(eventName) { - if (eventName && eventName.include(':')) return "dataavailable"; - return eventName; - } - - function getCacheForID(id) { - return cache[id] = cache[id] || { }; - } - - function getWrappersForEventName(id, eventName) { - var c = getCacheForID(id); - return c[eventName] = c[eventName] || []; - } - - function createWrapper(element, eventName, handler) { - var id = getEventID(element); - var c = getWrappersForEventName(id, eventName); - if (c.pluck("handler").include(handler)) return false; - - var wrapper = function(event) { - if (!Event || !Event.extend || - (event.eventName && event.eventName != eventName)) - return false; - - Event.extend(event); - handler.call(element, event); - }; - - wrapper.handler = handler; - c.push(wrapper); - return wrapper; - } - - function findWrapper(id, eventName, handler) { - var c = getWrappersForEventName(id, eventName); - return c.find(function(wrapper) { return wrapper.handler == handler }); - } - - function destroyWrapper(id, eventName, handler) { - var c = getCacheForID(id); - if (!c[eventName]) return false; - c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); - } - - function destroyCache() { - for (var id in cache) - for (var eventName in cache[id]) - cache[id][eventName] = null; - } - - if (window.attachEvent) { - window.attachEvent("onunload", destroyCache); - } - - return { - observe: function(element, eventName, handler) { - element = $(element); - var name = getDOMEventName(eventName); - - var wrapper = createWrapper(element, eventName, handler); - if (!wrapper) return element; - - if (element.addEventListener) { - element.addEventListener(name, wrapper, false); - } else { - element.attachEvent("on" + name, wrapper); - } - - return element; - }, - - stopObserving: function(element, eventName, handler) { - element = $(element); - var id = getEventID(element), name = getDOMEventName(eventName); - - if (!handler && eventName) { - getWrappersForEventName(id, eventName).each(function(wrapper) { - element.stopObserving(eventName, wrapper.handler); - }); - return element; - - } else if (!eventName) { - Object.keys(getCacheForID(id)).each(function(eventName) { - element.stopObserving(eventName); - }); - return element; - } - - var wrapper = findWrapper(id, eventName, handler); - if (!wrapper) return element; - - if (element.removeEventListener) { - element.removeEventListener(name, wrapper, false); - } else { - element.detachEvent("on" + name, wrapper); - } - - destroyWrapper(id, eventName, handler); - - return element; - }, - - fire: function(element, eventName, memo) { - element = $(element); - if (element == document && document.createEvent && !element.dispatchEvent) - element = document.documentElement; - - var event; - if (document.createEvent) { - event = document.createEvent("HTMLEvents"); - event.initEvent("dataavailable", true, true); - } else { - event = document.createEventObject(); - event.eventType = "ondataavailable"; - } - - event.eventName = eventName; - event.memo = memo || { }; - - if (document.createEvent) { - element.dispatchEvent(event); - } else { - element.fireEvent(event.eventType, event); - } - - return Event.extend(event); - } - }; -})()); - -Object.extend(Event, Event.Methods); - -Element.addMethods({ - fire: Event.fire, - observe: Event.observe, - stopObserving: Event.stopObserving -}); - -Object.extend(document, { - fire: Element.Methods.fire.methodize(), - observe: Element.Methods.observe.methodize(), - stopObserving: Element.Methods.stopObserving.methodize(), - loaded: false -}); - -(function() { - /* Support for the DOMContentLoaded event is based on work by Dan Webb, - Matthias Miller, Dean Edwards and John Resig. */ - - var timer; - - function fireContentLoadedEvent() { - if (document.loaded) return; - if (timer) window.clearInterval(timer); - document.fire("dom:loaded"); - document.loaded = true; - } - - if (document.addEventListener) { - if (Prototype.Browser.WebKit) { - timer = window.setInterval(function() { - if (/loaded|complete/.test(document.readyState)) - fireContentLoadedEvent(); - }, 0); - - Event.observe(window, "load", fireContentLoadedEvent); - - } else { - document.addEventListener("DOMContentLoaded", - fireContentLoadedEvent, false); - } - - } else { - document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>"); - $("__onDOMContentLoaded").onreadystatechange = function() { - if (this.readyState == "complete") { - this.onreadystatechange = null; - fireContentLoadedEvent(); - } - }; - } -})(); -/*------------------------------- DEPRECATED -------------------------------*/ - -Hash.toQueryString = Object.toQueryString; - -var Toggle = { display: Element.toggle }; - -Element.Methods.childOf = Element.Methods.descendantOf; - -var Insertion = { - Before: function(element, content) { - return Element.insert(element, {before:content}); - }, - - Top: function(element, content) { - return Element.insert(element, {top:content}); - }, - - Bottom: function(element, content) { - return Element.insert(element, {bottom:content}); - }, - - After: function(element, content) { - return Element.insert(element, {after:content}); - } -}; - -var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); - -// This should be moved to script.aculo.us; notice the deprecated methods -// further below, that map to the newer Element methods. -var Position = { - // set to true if needed, warning: firefox performance problems - // NOT neeeded for page scrolling, only if draggable contained in - // scrollable elements - includeScrollOffsets: false, - - // must be called before calling withinIncludingScrolloffset, every time the - // page is scrolled - prepare: function() { - this.deltaX = window.pageXOffset - || document.documentElement.scrollLeft - || document.body.scrollLeft - || 0; - this.deltaY = window.pageYOffset - || document.documentElement.scrollTop - || document.body.scrollTop - || 0; - }, - - // caches x/y coordinate pair to use with overlap - within: function(element, x, y) { - if (this.includeScrollOffsets) - return this.withinIncludingScrolloffsets(element, x, y); - this.xcomp = x; - this.ycomp = y; - this.offset = Element.cumulativeOffset(element); - - return (y >= this.offset[1] && - y < this.offset[1] + element.offsetHeight && - x >= this.offset[0] && - x < this.offset[0] + element.offsetWidth); - }, - - withinIncludingScrolloffsets: function(element, x, y) { - var offsetcache = Element.cumulativeScrollOffset(element); - - this.xcomp = x + offsetcache[0] - this.deltaX; - this.ycomp = y + offsetcache[1] - this.deltaY; - this.offset = Element.cumulativeOffset(element); - - return (this.ycomp >= this.offset[1] && - this.ycomp < this.offset[1] + element.offsetHeight && - this.xcomp >= this.offset[0] && - this.xcomp < this.offset[0] + element.offsetWidth); - }, - - // within must be called directly before - overlap: function(mode, element) { - if (!mode) return 0; - if (mode == 'vertical') - return ((this.offset[1] + element.offsetHeight) - this.ycomp) / - element.offsetHeight; - if (mode == 'horizontal') - return ((this.offset[0] + element.offsetWidth) - this.xcomp) / - element.offsetWidth; - }, - - // Deprecation layer -- use newer Element methods now (1.5.2). - - cumulativeOffset: Element.Methods.cumulativeOffset, - - positionedOffset: Element.Methods.positionedOffset, - - absolutize: function(element) { - Position.prepare(); - return Element.absolutize(element); - }, - - relativize: function(element) { - Position.prepare(); - return Element.relativize(element); - }, - - realOffset: Element.Methods.cumulativeScrollOffset, - - offsetParent: Element.Methods.getOffsetParent, - - page: Element.Methods.viewportOffset, - - clone: function(source, target, options) { - options = options || { }; - return Element.clonePosition(target, source, options); - } -}; - -/*--------------------------------------------------------------------------*/ - -if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ - function iter(name) { - return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; - } - - instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? - function(element, className) { - className = className.toString().strip(); - var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); - return cond ? document._getElementsByXPath('.//*' + cond, element) : []; - } : function(element, className) { - className = className.toString().strip(); - var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); - if (!classNames && !className) return elements; - - var nodes = $(element).getElementsByTagName('*'); - className = ' ' + className + ' '; - - for (var i = 0, child, cn; child = nodes[i]; i++) { - if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || - (classNames && classNames.all(function(name) { - return !name.toString().blank() && cn.include(' ' + name + ' '); - })))) - elements.push(Element.extend(child)); - } - return elements; - }; - - return function(className, parentElement) { - return $(parentElement || document.body).getElementsByClassName(className); - }; -}(Element.Methods); - -/*--------------------------------------------------------------------------*/ - -Element.ClassNames = Class.create(); -Element.ClassNames.prototype = { - initialize: function(element) { - this.element = $(element); - }, - - _each: function(iterator) { - this.element.className.split(/\s+/).select(function(name) { - return name.length > 0; - })._each(iterator); - }, - - set: function(className) { - this.element.className = className; - }, - - add: function(classNameToAdd) { - if (this.include(classNameToAdd)) return; - this.set($A(this).concat(classNameToAdd).join(' ')); - }, - - remove: function(classNameToRemove) { - if (!this.include(classNameToRemove)) return; - this.set($A(this).without(classNameToRemove).join(' ')); - }, - - toString: function() { - return $A(this).join(' '); - } -}; - -Object.extend(Element.ClassNames.prototype, Enumerable); - -/*--------------------------------------------------------------------------*/ - -Element.addMethods(); \ No newline at end of file diff --git a/fas/fas/static/js/scriptaculous.js b/fas/fas/static/js/scriptaculous.js deleted file mode 100644 index 6cfe36e..0000000 --- a/fas/fas/static/js/scriptaculous.js +++ /dev/null @@ -1,58 +0,0 @@ -// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 - -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// For details, see the script.aculo.us web site: http://script.aculo.us/ - -var Scriptaculous = { - Version: '1.8.1', - require: function(libraryName) { - // inserting via DOM fails in Safari 2.0, so brute force approach - document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>'); - }, - REQUIRED_PROTOTYPE: '1.6.0', - load: function() { - function convertVersionString(versionString){ - var r = versionString.split('.'); - return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]); - } - - if((typeof Prototype=='undefined') || - (typeof Element == 'undefined') || - (typeof Element.Methods=='undefined') || - (convertVersionString(Prototype.Version) < - convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) - throw("script.aculo.us requires the Prototype JavaScript framework >= " + - Scriptaculous.REQUIRED_PROTOTYPE); - - $A(document.getElementsByTagName("script")).findAll( function(s) { - return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/)) - }).each( function(s) { - var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,''); - var includes = s.src.match(/\?.*load=([a-z,]*)/); - (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each( - function(include) { Scriptaculous.require(path+include+'.js') }); - }); - } -} - -Scriptaculous.load(); \ No newline at end of file diff --git a/fas/fas/static/robots.txt b/fas/fas/static/robots.txt deleted file mode 100644 index 1f53798..0000000 --- a/fas/fas/static/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / diff --git a/fas/fas/static/theme/fas/css/style.css b/fas/fas/static/theme/fas/css/style.css deleted file mode 100644 index 9032d67..0000000 --- a/fas/fas/static/theme/fas/css/style.css +++ /dev/null @@ -1,494 +0,0 @@ -* -{ - margin: 0; - padding: 0; -} - -body -{ - font-size: 76%; - background: #FFFFFF; -} - -a -{ - text-decoration: none; -} - -#wrapper -{ - font: normal 2ex/1.5 sans-serif; -} - -#head -{ - overflow: hidden; - margin-top: 35px; - height: 70px; - line-height: 70px; - background: url(../images/head.png) 0 0 repeat-x; -} - -#head h1 -{ - width: 250px; - float: left; - text-indent: -9999px; - overflow: hidden; - background: url(../images/logo.png) 1ex 50% no-repeat; -} - -#searchbox -{ - width: 36ex; - float: right; - text-align: right; - margin-right: 2ex; -} - -#searchbox label -{ - display: none; -} - -#searchbox input -{ - display: inline; - border: 1px solid #CCCCCC; -} - -#searchbox #q -{ - width: 20ex; -} - -#topnav -{ - height: 30px; - line-height: 30px; - background: url(../images/topnav.png) 0 0 repeat-x; - font-size: 1.6ex; -} - -#topnav ul -{ - list-style: none; - text-align: center; -} - -#topnav ul li -{ - display: inline; - background: url(../images/topnav-separator.png) 0 50% no-repeat; - padding-left: 3px; -} - -#topnav ul li.first -{ - background: none; -} - -#topnav a -{ - color: #445566; - margin: 0 2ex; -} - -#topnav a:hover -{ - color: #000000; -} - -#infobar -{ - position: absolute; - top: 0; - left: 0; - right: 0; - height: 35px; - line-height: 35px; - background: url(../images/infobar.png) 0 0 repeat-x; - font-size: 1.6ex; -} - -#authstatus -{ - width: 40ex; - float: left; - color: #FFFFFF; - padding-left: 1.5ex; -} - -#authstatus strong -{ - color: #DED6A1; -} - -#control -{ - width: 40ex; - float: right; - margin-right: 1ex; -} - -#control ul -{ - list-style: none; - text-align: right; -} - -#control ul li -{ - display: inline; - background: url(../images/control-separator.png) 0 50% no-repeat; -} - -#control a -{ - color: #DED6A1; - margin: 0 1.5ex; -} - -#main -{ - background: url(../images/shadow.png) 0 0 repeat-x; -} - -#sidebar -{ - width: 22ex; - float: left; - background: #335F9D url(../images/sidebar.png) 0 0 repeat-x; - border: 1px solid #112233; -} - -#sidebar ul -{ - list-style: none; -} - -#sidebar li -{ - border-top: 1px solid #CCCCCC; -} - -#sidebar li.first -{ - border-top: none; -} - -#sidebar a -{ - display: block; - text-align: center; - color: #FFFFFF; - padding: 0.5ex 0; -} - -#sidebar a:hover -{ - background: #082C59; -} - -#language -{ - padding: 1ex; -} - -#language label -{ - color: #FFFFFF; -} - -#language input -{ - width: 4ex; -} - -#content -{ - margin-left: 22ex; - padding: 2ex 4ex; -} - -#content h2 -{ - /* header icon */ -} - -#content h3 -{ - font-size: 2.2ex; - margin-bottom: 0.25ex; -} - -#content a -{ - color: #0C6ED0; -} - -.userbox -{ -} - -.userbox dt -{ - width: 20ex; - float: left; - text-align: right; - font-weight: bold; -} - -.userbox dd -{ - margin-left: 24ex; -} - -.account -{ - padding-left: 30px; - background: url(../images/account.png) 0 68% no-repeat; -} - -.approved -{ - padding-left: 20px; - background: url(../images/approved.png) 0 68% no-repeat; -} - -.unapproved -{ - padding-left: 20px; - background: url(../images/unapproved.png) 0 68% no-repeat; -} - -.attn -{ - padding-left: 20px; - background: url(../images/attn.png) 0 68% no-repeat; -} - -.roleslist -{ - list-style: none; - margin: 0 2ex; -} - -.roleslist li -{ - margin-left: 0.5ex; -} - -.actions -{ - margin-top: 1.5ex; - list-style: none; -} - -.actions li -{ - /* - * TODO: Add icons - */ -} - -#rolespanel -{ - list-style: none; -} - -#rolespanel li.role -{ - border-top: 2px solid #EEEEEE; - margin-top: 1ex; - padding-top: 1ex; - padding-left: 22px; - background: url(../images/arrow.png) 0 1.6ex no-repeat; -} - -#rolespanel h4 -{ - display: inline; -} - -#rolespanel dt -{ - width: 10ex; - float: left; - text-align: right; - margin-bottom: 1ex; -} - -#rolespanel dd -{ - margin-left: 12ex; - margin-bottom: 1ex; -} - -#rolespanel .tools, #rolespanel .queue -{ - list-style: none; -} - -#rolespanel .tools li -{ - padding-left: 22px; - background: url(../images/tools.png) 0 50% no-repeat; -} - -#rolespanel .queue li -{ - padding-left: 22px; - background: url(../images/queue.png) 0 50% no-repeat; -} - -#rolespanel .queue strong -{ - color: #222222; -} - -#footer -{ - font-size: 1.6ex; - clear: both; - text-align: center; - padding: 15px 0 2.5ex; - background: url(../images/footer-top.png) 0 0 repeat-x; -} - -#footer .copy, #footer .disclaimer -{ - font-size: 1.5ex; -} - -#footlinks -{ - padding-top: 3px; - padding-bottom: 18px; - background: #EEEEEE url(../images/footer-bottom.png) 0 100% repeat-x; - list-style: none; -} - -#footlinks li -{ - display: inline; - border-left: 1px solid #CCCCCC; - padding-left: 1px; -} - -#footlinks li.first -{ - padding-left: 0; - border-left: none; -} - -#footlinks a -{ - margin: 0 2ex; - color: #3465A4; -} - -.flash -{ - background: #DEE6B1 url(../images/success.png) 10px 50% no-repeat; - border: 1px solid #CCBBAA; - padding: 1.5ex 15px 1.5ex 43px; - margin: 1ex 0; -} - -.help -{ - background: #DEE6B1 url(../images/help.png) 10px 50% no-repeat; - border: 1px solid #CCBBAA; - padding: 1.5ex 15px 1.5ex 65px; - margin: 1ex 0; -} - -.letters -{ - list-style: none; -} - -.letters li -{ - display: inline; - margin-right: 1ex; -} - -#content table -{ - border-collapse: collapse; -} - -#content table th, #content table td -{ - padding: 0 2ex; - border: 1px solid #CCCCCC; -} - -pre -{ - font-size: 3ex; -} - -form -{ - list-style: none; - margin: 1ex 0!important; -} - -form .field -{ - margin: 0 0 1ex; - text-align: left; - overflow: hidden; -} - -form .field label -{ - float: left; - clear: left; - width: 16ex; - text-align: right; - padding: 0 2ex 0 0; -} - -form .field input, form .field textarea -{ - margin: 0; -} - -.message p -{ - margin: 1ex 0; - font-size: 3ex; - font-family: monospace; -} - -#cla -{ - border: 1px solid #AAAAAA; - background: #EEEEEE; - padding: 2ex; -} - -#cla p -{ - margin: 2ex 0; -} - -#cla ol { - list-style-type: decimal; - margin-left: 3ex; -} - -#cla ol ol -{ - list-style: upper-alpha; -} - -#cla ol li -{ - margin: 2ex 0; -} - diff --git a/fas/fas/static/theme/fas/images/account.png b/fas/fas/static/theme/fas/images/account.png deleted file mode 100644 index 89b82ee..0000000 Binary files a/fas/fas/static/theme/fas/images/account.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/approved.png b/fas/fas/static/theme/fas/images/approved.png deleted file mode 100644 index 04c1881..0000000 Binary files a/fas/fas/static/theme/fas/images/approved.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/arrow.png b/fas/fas/static/theme/fas/images/arrow.png deleted file mode 100644 index dd5ce6e..0000000 Binary files a/fas/fas/static/theme/fas/images/arrow.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/attn.png b/fas/fas/static/theme/fas/images/attn.png deleted file mode 100644 index ef0888a..0000000 Binary files a/fas/fas/static/theme/fas/images/attn.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/control-separator.png b/fas/fas/static/theme/fas/images/control-separator.png deleted file mode 100644 index 1bf6bba..0000000 Binary files a/fas/fas/static/theme/fas/images/control-separator.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/favicon.ico b/fas/fas/static/theme/fas/images/favicon.ico deleted file mode 100644 index 79d0ba9..0000000 Binary files a/fas/fas/static/theme/fas/images/favicon.ico and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/footer-bottom.png b/fas/fas/static/theme/fas/images/footer-bottom.png deleted file mode 100644 index 88431e0..0000000 Binary files a/fas/fas/static/theme/fas/images/footer-bottom.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/footer-top.png b/fas/fas/static/theme/fas/images/footer-top.png deleted file mode 100644 index 0945c0f..0000000 Binary files a/fas/fas/static/theme/fas/images/footer-top.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/head.png b/fas/fas/static/theme/fas/images/head.png deleted file mode 100644 index 5f8a994..0000000 Binary files a/fas/fas/static/theme/fas/images/head.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/header-icon_account.png b/fas/fas/static/theme/fas/images/header-icon_account.png deleted file mode 100644 index d32d048..0000000 Binary files a/fas/fas/static/theme/fas/images/header-icon_account.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/header_inner.png b/fas/fas/static/theme/fas/images/header_inner.png deleted file mode 100644 index 2b2d87d..0000000 Binary files a/fas/fas/static/theme/fas/images/header_inner.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/help.png b/fas/fas/static/theme/fas/images/help.png deleted file mode 100644 index b484f52..0000000 Binary files a/fas/fas/static/theme/fas/images/help.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/hr.png b/fas/fas/static/theme/fas/images/hr.png deleted file mode 100644 index 66aa5c1..0000000 Binary files a/fas/fas/static/theme/fas/images/hr.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/icon_tool-item.png b/fas/fas/static/theme/fas/images/icon_tool-item.png deleted file mode 100644 index c54189c..0000000 Binary files a/fas/fas/static/theme/fas/images/icon_tool-item.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/icon_warning.png b/fas/fas/static/theme/fas/images/icon_warning.png deleted file mode 100644 index d7f3f55..0000000 Binary files a/fas/fas/static/theme/fas/images/icon_warning.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/info.png b/fas/fas/static/theme/fas/images/info.png deleted file mode 100644 index 329c523..0000000 Binary files a/fas/fas/static/theme/fas/images/info.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/infobar.png b/fas/fas/static/theme/fas/images/infobar.png deleted file mode 100644 index 1a55c86..0000000 Binary files a/fas/fas/static/theme/fas/images/infobar.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/logo.png b/fas/fas/static/theme/fas/images/logo.png deleted file mode 100644 index 00c1d97..0000000 Binary files a/fas/fas/static/theme/fas/images/logo.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/ok.png b/fas/fas/static/theme/fas/images/ok.png deleted file mode 100644 index fee6751..0000000 Binary files a/fas/fas/static/theme/fas/images/ok.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/queue.png b/fas/fas/static/theme/fas/images/queue.png deleted file mode 100644 index c08d990..0000000 Binary files a/fas/fas/static/theme/fas/images/queue.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/shadow.png b/fas/fas/static/theme/fas/images/shadow.png deleted file mode 100644 index 318e0d1..0000000 Binary files a/fas/fas/static/theme/fas/images/shadow.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/sidebar.png b/fas/fas/static/theme/fas/images/sidebar.png deleted file mode 100644 index 765b18d..0000000 Binary files a/fas/fas/static/theme/fas/images/sidebar.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/status_approved.png b/fas/fas/static/theme/fas/images/status_approved.png deleted file mode 100644 index 62b254b..0000000 Binary files a/fas/fas/static/theme/fas/images/status_approved.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/status_incomplete.png b/fas/fas/static/theme/fas/images/status_incomplete.png deleted file mode 100644 index 3169e1d..0000000 Binary files a/fas/fas/static/theme/fas/images/status_incomplete.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/status_rejected.png b/fas/fas/static/theme/fas/images/status_rejected.png deleted file mode 100644 index bedcaea..0000000 Binary files a/fas/fas/static/theme/fas/images/status_rejected.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/success.png b/fas/fas/static/theme/fas/images/success.png deleted file mode 100644 index a2cd3cd..0000000 Binary files a/fas/fas/static/theme/fas/images/success.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/tg_under_the_hood.png b/fas/fas/static/theme/fas/images/tg_under_the_hood.png deleted file mode 100644 index bc9c79c..0000000 Binary files a/fas/fas/static/theme/fas/images/tg_under_the_hood.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/tools.png b/fas/fas/static/theme/fas/images/tools.png deleted file mode 100644 index 795bfec..0000000 Binary files a/fas/fas/static/theme/fas/images/tools.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/topnav-separator.png b/fas/fas/static/theme/fas/images/topnav-separator.png deleted file mode 100644 index 7ee7848..0000000 Binary files a/fas/fas/static/theme/fas/images/topnav-separator.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/topnav.png b/fas/fas/static/theme/fas/images/topnav.png deleted file mode 100644 index 7c71ab1..0000000 Binary files a/fas/fas/static/theme/fas/images/topnav.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/unapproved.png b/fas/fas/static/theme/fas/images/unapproved.png deleted file mode 100644 index 9e98fb8..0000000 Binary files a/fas/fas/static/theme/fas/images/unapproved.png and /dev/null differ diff --git a/fas/fas/static/theme/fas/images/under_the_hood_blue.png b/fas/fas/static/theme/fas/images/under_the_hood_blue.png deleted file mode 100644 index 90e84b7..0000000 Binary files a/fas/fas/static/theme/fas/images/under_the_hood_blue.png and /dev/null differ diff --git a/fas/fas/templates/__init__.py b/fas/fas/templates/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fas/fas/templates/about.html b/fas/fas/templates/about.html deleted file mode 100644 index d6dbd9f..0000000 --- a/fas/fas/templates/about.html +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" - xmlns:py="http://genshi.edgewall.org/" - xmlns:xi="http://www.w3.org/2001/XInclude"> - <xi:include href="master.html" /> - <head> - <title>${_('About FAS')} - - -

${_('FAS - The Open Account System')}

-

${_('''FAS is designed around an open architecture. Unlike the traditional account systems where a single admin or group of admins decide who gets to be in what group, FAS is completely designed to be self operating per team. Every group is given at least one administrator who can then approve other people in the group. Also, unlike traditional account systems. FAS allows people to apply for the groups they want to be in. This paridigm is interesting as it allows anyone to find out who is in what groups and contact them. This openness is brought over from the same philosophies that make Open Source popular.''')}

-

${_('Etiquette')}

-

${_("People shouldn't assume that by applying for a group that they're then in that group. Consider it like applying for another job. It often takes time. For best odds of success, learn about the group you're applying for and get to know someone in the group. Find someone with sponsor or admin access and ask them if they'd have time to mentor you. Plan on spending at least a few days learning about the group, doing a mundain task, participating on the mailing list. Sometimes this process can take weeks depending on the group. It's best to know you will get sponsored before you apply.")}

-

${_('Users, Sponsors, Administrators')}

-

${_('''Once you're in the group, you're in the group. Sponsorship and Administrators typically have special access in the group in questions. Some groups consider sponsorship level to be of a higher involvement, partial ownership of the group for example. But as far as the account system goes the disctinction is easy. Sponsors can approve new users and make people into sponsors. They cannot, however, downgrade or remove other sponsors. They also cannot change administrators in any way. Administrators can do anything to anyone in the group.''')}

- - diff --git a/fas/fas/templates/cla/__init__.py b/fas/fas/templates/cla/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fas/fas/templates/cla/cla.html b/fas/fas/templates/cla/cla.html deleted file mode 100644 index 34643a3..0000000 --- a/fas/fas/templates/cla/cla.html +++ /dev/null @@ -1,82 +0,0 @@ -
-

The Fedora Project - Individual Contributor License Agreement (CLA) -

- http://fedoraproject.org/wiki/Legal/Licenses/CLA -

- Thank you for your interest in The Fedora Project (the "Project"). In order to clarify the intellectual property license granted with Contributions from any person or entity, Red hat, Inc. ("Red Hat"), as maintainer of the Project, must have a Contributor License Agreement (CLA) on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for Your protection as a Contributor as well as the protection of the Project and its users; it does not change your rights to use your own Contributions for any other purpose. -

-

- If you have not already done so, please complete an original signed Agreement. Use black ink, and hand-print or type the items other than the signature. Send the completed Agreement to -

-
- Fedora Project, c/o Red Hat, Inc.,
- Attn: Legal Affairs
- 1801 Varsity Drive
- Raleigh, North Carolina, 27606 U.S.A. -
-

- If necessary, you may send it by facsimile to the Project at +1-919-754-3704 or e-mail a signed pdf copy of the document to fedora-legal@redhat.com. Please read this document carefully before signing and keep a copy for your records. -

-

- Full name: ${person.human_name}
- E-Mail: ${person.email}
- Address: ${person.postal_address}
- Telephone: ${person.telephone} - -

-

- You and the Project hereby accept and agree to the following terms and conditions: -

-
    -
  1. - Contributors and Contributions. -
      -
    1. - The Project and any individual or legal entity that voluntarily submits to the Project a Contribution are collectively addressed herein as "Contributors". For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -
    2. -
    3. - A "Contribution" is any original work, including any modification or addition to an existing work, that has been submitted for inclusion in, or documentation of, any of the products owned or managed by the Project, where such work originates from that particular Contributor or from some entity acting on behalf of that Contributor. -
    4. -
    5. - A Contribution is "submitted" when any form of electronic, verbal, or written communication is sent to the Project, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of discussing or improving software or documentation of the Project, but excluding communication that is conspicuously marked or otherwise designated in writing by you as "Not a Contribution." -
    6. -
    7. - Any Contribution submitted by you to the Project shall be under the terms and conditions of this License, without any additional terms or conditions, unless you explicitly state otherwise in the submission. -
    8. -
    -
  2. -
  3. - Contributor Grant of License. You hereby grant to Red Hat, Inc., on behalf of the Project, and to recipients of software distributed by the Project: -
      -
    1. - a perpetual, non-exclusive, worldwide, fully paid-up, royalty free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute your Contribution and such derivative works; and, -
    2. -
    3. - a perpetual, non-exclusive, worldwide, fully paid-up, royalty free, irrevocable (subject to Section 3) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer your Contribution and derivative works thereof, where such license applies only to those patent claims licensable by you that are necessarily infringed by your Contribution alone or by combination of your Contribution with the work to which you submitted the Contribution. Except for the license granted in this section, you reserve all right, title and interest in and to your Contributions. -
    4. -
    -
  4. -
  5. - Reciprocity. As of the date any such litigation is filed, your patent grant shall immediately terminate with respect to any party that institutes patent litigation against you (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the work to which you have contributed, constitutes direct or contributory patent infringement. -
  6. -
  7. - You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to the Project, or that your employer has executed a separate Corporate CLA with the Project. -
  8. -
  9. - You represent that each of your Contributions is your original creation (see section 7 for submissions on behalf of others). You represent that your Contribution submission(s) include complete details of any third-party license or other restriction (including, but not limited to, related copyright, atents and trademarks) of which you are personally aware and which are associated with any part of your Contribution. -
  10. -
  11. - You are not expected to provide support for your Contributions, except to the extent you desire to provide support. You may provide support for free, for a fee, or not at all. Your Contributions are provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. -
  12. -
  13. - Should you wish to submit work that is not your original creation, you may submit it to the Project separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". -
  14. -
  15. - You agree to notify the Project of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. -
  16. -
  17. - The Project is under no obligations to accept and include every contribution. -
  18. -
-
diff --git a/fas/fas/templates/cla/cla.txt b/fas/fas/templates/cla/cla.txt deleted file mode 100644 index 7d6d482..0000000 --- a/fas/fas/templates/cla/cla.txt +++ /dev/null @@ -1,145 +0,0 @@ - The Fedora Project - Individual Contributor License Agreement (CLA) - http://fedoraproject.org/wiki/Legal/Licenses/CLA - - Thank you for your interest in The Fedora Project (the - "Project"). In order to clarify the intellectual property license - granted with Contributions from any person or entity, Red hat, - Inc. ("Red Hat"), as maintainer of the Project, must have a - Contributor License Agreement (CLA) on file that has been signed - by each Contributor, indicating agreement to the license terms - below. This license is for Your protection as a Contributor as - well as the protection of the Project and its users; it does not - change your rights to use your own Contributions for any other - purpose. - - If you have not already done so, please complete an original signed - Agreement. Use black ink, and hand-print or type the items other than - the signature. Send the completed Agreement to - - Fedora Project, c/o Red Hat, Inc., - Attn: Legal Affairs - 1801 Varsity Drive - Raleigh, North Carolina, 27606 U.S.A. - - If necessary, you may send it by facsimile to the Project at - +1-919-754-3704 or e-mail a signed pdf copy of the document to - fedora-legal@redhat.com. Please read this document carefully before - signing and keep a copy for your records. - - Full name: ${person.human_name} E-Mail: ${person.email} - - Address: -${person.postal_address} - - Telephone: ${person.telephone} - - Facsimile: ${person.facsimile} - - You and the Project hereby accept and agree to the following terms and conditions: - - 1. Contributors and Contributions. - - A. The Project and any individual or legal entity that - voluntarily submits to the Project a Contribution are - collectively addressed herein as "Contributors". For legal - entities, the entity making a Contribution and all other - entities that control, are controlled by, or are under common - control with that entity are considered to be a single - Contributor. For the purposes of this definition, "control" - means (i) the power, direct or indirect, to cause the direction - or management of such entity, whether by contract or otherwise, - or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such - entity. - - B. A "Contribution" is any original work, including any - modification or addition to an existing work, that has been - submitted for inclusion in, or documentation of, any of the - products owned or managed by the Project, where such work - originates from that particular Contributor or from some entity - acting on behalf of that Contributor. - - C. A Contribution is "submitted" when any form of electronic, - verbal, or written communication is sent to the Project, - including but not limited to communication on electronic - mailing lists, source code control systems, and issue tracking - systems that are managed by, or on behalf of, the Project for - the purpose of discussing or improving software or - documentation of the Project, but excluding communication that - is conspicuously marked or otherwise designated in writing by - you as "Not a Contribution." - - D. Any Contribution submitted by you to the Project shall be - under the terms and conditions of this License, without any - additional terms or conditions, unless you explicitly state - otherwise in the submission. - - 2. Contributor Grant of License. You hereby grant to Red Hat, - Inc., on behalf of the Project, and to recipients of software - distributed by the Project: - - (a) a perpetual, non-exclusive, worldwide, fully paid-up, - royalty free, irrevocable copyright license to reproduce, - prepare derivative works of, publicly display, publicly - perform, sublicense, and distribute your Contribution and such - derivative works; and, - - (b) a perpetual, non-exclusive, worldwide, fully paid-up, - royalty free, irrevocable (subject to Section 3) patent license - to make, have made, use, offer to sell, sell, import, and - otherwise transfer your Contribution and derivative works - thereof, where such license applies only to those patent claims - licensable by you that are necessarily infringed by your - Contribution alone or by combination of your Contribution with - the work to which you submitted the Contribution. Except for - the license granted in this section, you reserve all right, - title and interest in and to your Contributions. - - 3. Reciprocity. As of the date any such litigation is filed, your - patent grant shall immediately terminate with respect to any - party that institutes patent litigation against you (including - a cross-claim or counterclaim in a lawsuit) alleging that your - Contribution, or the work to which you have contributed, - constitutes direct or contributory patent infringement. - - 4. You represent that you are legally entitled to grant the above - license. If your employer(s) has rights to intellectual - property that you create that includes your Contributions, you - represent that you have received permission to make - Contributions on behalf of that employer, that your employer - has waived such rights for your Contributions to the Project, - or that your employer has executed a separate Corporate CLA - with the Project. - - 5. You represent that each of your Contributions is your original - creation (see section 7 for submissions on behalf of others). - You represent that your Contribution submission(s) include - complete details of any third-party license or other - restriction (including, but not limited to, related copyright, - atents and trademarks) of which you are personally aware and - which are associated with any part of your Contribution. - - 6. You are not expected to provide support for your Contributions, - except to the extent you desire to provide support. You may - provide support for free, for a fee, or not at all. Your - Contributions are provided on an "AS IS" BASIS, WITHOUT - WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or - conditions of NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR - A PARTICULAR PURPOSE. - - 7. Should you wish to submit work that is not your original - creation, you may submit it to the Project separately from any - Contribution, identifying the complete details of its source - and of any license or other restriction (including, but not - limited to, related patents, trademarks, and license - agreements) of which you are personally aware, and - conspicuously marking the work as "Submitted on behalf of a - third-party: [named here]". - - 8. You agree to notify the Project of any facts or circumstances - of which you become aware that would make these representations - inaccurate in any respect. - - 9. The Project is under no obligations to accept and include every contribution. diff --git a/fas/fas/templates/cla/index.html b/fas/fas/templates/cla/index.html deleted file mode 100644 index ff8f9e6..0000000 --- a/fas/fas/templates/cla/index.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - ${_('Fedora Accounts System')} - - -

${_('Fedora Contributor License Agreement')}

- ${Markup(_('<a href="%(url)s">Text Version</a>') % {'url': tg.url('/cla/text')})} - - ${Markup(_('<a href="%(url)s">Text Version</a>') % {'url': tg.url('/cla/text')})} -

- ${Markup(_('You have already sucessfully complete the CLA.') % {'url': tg.url('/cla/text')})} -

- -
-
- - -
-
-
- - diff --git a/fas/fas/templates/error.html b/fas/fas/templates/error.html deleted file mode 100644 index c72b965..0000000 --- a/fas/fas/templates/error.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - ${_('Fedora Accounts System')} - - - -

${_('Error!')}

-

${_('The following error(s) have occured with your request:')}

-
    -
  • - ${field}: ${str(error)} -
  • -
- - diff --git a/fas/fas/templates/group/__init__.py b/fas/fas/templates/group/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fas/fas/templates/group/dump.txt b/fas/fas/templates/group/dump.txt deleted file mode 100644 index 7219bd4..0000000 --- a/fas/fas/templates/group/dump.txt +++ /dev/null @@ -1,3 +0,0 @@ -#for person in sorted(people) -${person.username},${person.email},${person.human_name},user,0 -#end diff --git a/fas/fas/templates/group/edit.html b/fas/fas/templates/group/edit.html deleted file mode 100644 index 8df9316..0000000 --- a/fas/fas/templates/group/edit.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - ${_('Edit Group')} - - -

${_('Edit Group: %s') % group.name}

-
-
- - - -
-
- - - -
-
- - - -
-
- - - - -
-
- - - -   -
-
- - - -   -
-
- - - -
-
- -
-
- - diff --git a/fas/fas/templates/group/invite.html b/fas/fas/templates/group/invite.html deleted file mode 100644 index 0c537a8..0000000 --- a/fas/fas/templates/group/invite.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - ${_('Invite a new community member!')} - - -

${_('Invite a new community member!')}

-
-
- - ${_('To email:')}
- ${_('From:')} ${person.email}
- ${_('Subject:')} Invitation to join the Fedora Team!
- ${_('Message:')} -
-

- ${person.human_name} <${person.email}> has invited you to join the Fedora - Project! We are a community of users and developers who produce a - complete operating system from entirely free and open source software - (FOSS). ${person.human_name} thinks that you have knowledge and skills - that make you a great fit for the Fedora community, and that you might - be interested in contributing. -

-

- How could you team up with the Fedora community to use and develop your - skills? Check out http://fedoraproject.org/join-fedora for some ideas. - Our community is more than just software developers -- we also have a - place for you whether you're an artist, a web site builder, a writer, or - a people person. You'll grow and learn as you work on a team with other - very smart and talented people. -

-

- Fedora and FOSS are changing the world -- come be a part of it! -

-
- -
-
- - diff --git a/fas/fas/templates/group/list.html b/fas/fas/templates/group/list.html deleted file mode 100644 index 59c147e..0000000 --- a/fas/fas/templates/group/list.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - ${_('Groups List')} - - - - -

Create New Group

- Create Group -
- -

${_('List (%s)') % search}

-

${_('Search Groups')}

-
-

${_('"*" is a wildcard (Ex: "cvs*")')}

-
- - -
-
-

${_('Results')}

- - - - - - - - - - - - - -
${_('Group')}${_('Description')}${_('Status')}
${group.name}${ group.display_name } - - ${_('Approved')} - ${_('Unapproved')} - - ${_('Apply')} - -
- - diff --git a/fas/fas/templates/group/new.html b/fas/fas/templates/group/new.html deleted file mode 100644 index f74862c..0000000 --- a/fas/fas/templates/group/new.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - ${_('Create a new FAS Group')} - - -

${_('Create a new FAS Group')}

-
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- -   - -
-
- -   - -
-
- - - -
-
- - - -
-
- -
-
- - diff --git a/fas/fas/templates/group/view.html b/fas/fas/templates/group/view.html deleted file mode 100644 index 0330eac..0000000 --- a/fas/fas/templates/group/view.html +++ /dev/null @@ -1,123 +0,0 @@ - - - - - ${_('View Group')} - - - -

${group.display_name} (${group.name})

-

- ${_('My Status:')} - ${_('Approved')} - ${_('Unapproved')} - ${_('Not a Member')} -

-
-
- - -
-
- ${_('Remove me')} - -

Group Details ${_('(edit)')}

-
-
-
${_('Name:')}
${group.name} 
-
${_('Description:')}
${group.display_name} 
-
${_('Owner:')}
${group.owner.username} 
-
${_('Type:')}
${group.group_type} 
-
${_('Needs Sponsor:')}
- ${_('Yes')} - ${_('No')} -  
-
${_('Self Removal:')}
- ${_('Yes')} - ${_('No')} -  
-
${_('Join Message:')}
${group.joinmsg} 
-
${_('Prerequisite:')}
-
${group.prerequisite.name} 
-
 
-
${_('Created:')}
${group.creation} 
- -
${_('Add User:')}
-
-
- - - -
-
-
-
-
- -

${_('Members')}

- - - - - - - - - - - - - - - - - - - - - - - - -
${_('Username')}${_('Sponsor')}${_('Date Added')}${_('Date Approved')}${_('Approval')}${_('Role Type')}${_('Action')}
${role.member.username}${role.sponsor.username}${_('None')}${role.creation.astimezone(timezone).strftime('%Y-%m-%d %H:%M:%S %Z')}${role.approval.astimezone(timezone).strftime('%Y-%m-%d %H:%M:%S %Z')}${_('None')}${role.role_status}${role.role_type} -
    -
  • - - ${_('Sponsor')} - - - - ${_('Approve')} - - -
  • -
  • - ${_('Remove')} - -
  • -
  • - ${_('Upgrade')} - -
  • -
  • - ${_('Downgrade')} - -
  • -
-
- - diff --git a/fas/fas/templates/help.html b/fas/fas/templates/help.html deleted file mode 100644 index b3dd861..0000000 --- a/fas/fas/templates/help.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - ${help[0]} - - - ${XML(help[1])} - - diff --git a/fas/fas/templates/home.html b/fas/fas/templates/home.html deleted file mode 100644 index a12d0b9..0000000 --- a/fas/fas/templates/home.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - ${_('Fedora Accounts System')} - - - -

${_('Todo queue:')}

- - -
-
    -
  • - ${Markup(_('<strong>%(user)s</strong> requests approval to join <a href="group/view/%(group)s">%(group)s</a>.') % {'user': role.member.username, 'group': group.name, 'group': group.name})} -
  • -
-
-
-
-
    -
  • ${Markup(_('CLA not completed. To become a full Fedora Contributor please <a href="%s">complete the CLA</a>.') % tg.url('/cla/'))}
  • -
  • ${Markup(_('You have not submitted an SSH key, some Fedora resources require an SSH key. Please submit yours by editing <a href="%s">My Account</a>') % tg.url('/user/edit'))}
  • -
-
- - ${_('Download a client-side certificate')}  - -
- - diff --git a/fas/fas/templates/login.html b/fas/fas/templates/login.html deleted file mode 100644 index 24bf545..0000000 --- a/fas/fas/templates/login.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - ${_('Login to the Fedora Accounts System')} - - - -

${_('Login')}

-

${message}

-
-
-
-
- - - -
-
- - - diff --git a/fas/fas/templates/master.html b/fas/fas/templates/master.html deleted file mode 100644 index 6b46910..0000000 --- a/fas/fas/templates/master.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - -
- - -
-
- - ${_('Logged in:')} ${tg.identity.user.username} - -
- -
-
- -
-
- ${tg_flash} -
-
-
- -
-
- - diff --git a/fas/fas/templates/openid/__init__.py b/fas/fas/templates/openid/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fas/fas/templates/openid/about.html b/fas/fas/templates/openid/about.html deleted file mode 100644 index 2cbe67f..0000000 --- a/fas/fas/templates/openid/about.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - ${_('Fedora Accounts System')} - - -

${_('Fedora Project OpenID Provider')}

-

- ${Markup_('Description goes here, <a href="http://username.fedorapeople.org/">username.fedorapeople.org</a>')} -

- - diff --git a/fas/fas/templates/openid/auth.txt b/fas/fas/templates/openid/auth.txt deleted file mode 100644 index 34accd5..0000000 --- a/fas/fas/templates/openid/auth.txt +++ /dev/null @@ -1 +0,0 @@ -${body} diff --git a/fas/fas/templates/openid/id.html b/fas/fas/templates/openid/id.html deleted file mode 100644 index 01a5220..0000000 --- a/fas/fas/templates/openid/id.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - ${_('Fedora Accounts System')} - - - -

${_('User %s') % person.username}

-
-
-
${_('Username:')}
-
${person.username}
-
${_('Name:')}
-
${person.human_name}
-
-
- - diff --git a/fas/fas/templates/openid/trusted.html b/fas/fas/templates/openid/trusted.html deleted file mode 100644 index 0affc19..0000000 --- a/fas/fas/templates/openid/trusted.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - ${_('Fedora Accounts System')} - - -

${_('Fedora Project OpenID Provider')}

-
-
- - -
- -
-
- - diff --git a/fas/fas/templates/user/__init__.py b/fas/fas/templates/user/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fas/fas/templates/user/cert.txt b/fas/fas/templates/user/cert.txt deleted file mode 100644 index 692f231..0000000 --- a/fas/fas/templates/user/cert.txt +++ /dev/null @@ -1,2 +0,0 @@ -${cert} -${key} diff --git a/fas/fas/templates/user/changepass.html b/fas/fas/templates/user/changepass.html deleted file mode 100644 index 189c024..0000000 --- a/fas/fas/templates/user/changepass.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - ${_('Change Password')} - - -

${_('Change Password')}

-
-
    -
    -
    -
    -
    -
-
- - diff --git a/fas/fas/templates/user/edit.html b/fas/fas/templates/user/edit.html deleted file mode 100644 index ffacbc5..0000000 --- a/fas/fas/templates/user/edit.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - ${_('Edit Account')} - - -

${_('Edit Account (%s)') % target.username}

-
-
- - - -
- -
- - - - ${Markup(_('(pending change to %(email)s - <a href="%(url)s">cancel</a>)') % {'email': target.unverified_email, 'url': tg.url('/user/verifyemail/1/cancel')})} - - -
- -
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - - -
-
- - - -
-
- - - - -
-
- - - -
- -
- - diff --git a/fas/fas/templates/user/list.html b/fas/fas/templates/user/list.html deleted file mode 100644 index d2679b7..0000000 --- a/fas/fas/templates/user/list.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - ${_('Users List')} - - - -

${_('List (%s)') % search}

-
-

${_('"*" is a wildcard (Ex: "ric*")')}

-
- - -
-
-

${_('Results')}

- - - - - - - - - - - - - - -
${_('Username')}${_('Account Status')}
${person.username} - - ${_('CLA Done')} - ${_('Not Done')} -
- - diff --git a/fas/fas/templates/user/new.html b/fas/fas/templates/user/new.html deleted file mode 100644 index 786cd5b..0000000 --- a/fas/fas/templates/user/new.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - ${_('Sign up for a Fedora account')} - - -

${_('Sign up for a Fedora account')}

-
-
- - -
-
- - -
-
- - -
- -
- -
-
- - diff --git a/fas/fas/templates/user/resetpass.html b/fas/fas/templates/user/resetpass.html deleted file mode 100644 index d110add..0000000 --- a/fas/fas/templates/user/resetpass.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - ${_('Reset Password')} - - -

${_('Reset Password')}

-
-
    -
    -
    -
    -
    -
-
- - diff --git a/fas/fas/templates/user/verifyemail.html b/fas/fas/templates/user/verifyemail.html deleted file mode 100644 index 9c5dab7..0000000 --- a/fas/fas/templates/user/verifyemail.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - ${_('Confirm Email Change Request')} - - -

${_('Confirm Email Change Request')}

-
-
-

- ${_('Do you really want to change your email to: %s?') % person.unverified_email} -

- - ${_('Cancel')} -
-
- - diff --git a/fas/fas/templates/user/verifypass.html b/fas/fas/templates/user/verifypass.html deleted file mode 100644 index 763dd54..0000000 --- a/fas/fas/templates/user/verifypass.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - ${_('Reset Password')} - - -

${_('Reset Password')}

-
-
    -
    -
    - -
-
- - diff --git a/fas/fas/templates/user/view.html b/fas/fas/templates/user/view.html deleted file mode 100644 index 51599dc..0000000 --- a/fas/fas/templates/user/view.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - ${_('View Account')} - - - - - -

${_('Account Details')} ${_('(edit)')}

-
-
-
${_('Account Name:')}
${person.username}
-
${_('Real Name:')}
${person.human_name}
-
${_('Email:')}
${person.email} - - ${Markup(_('(pending change to %(email)s - <a href="%(url)s">cancel</a>)') % {'email': person.unverified_email, 'url': tg.url('/user/verifyemail/1/cancel')})} - -
-
${_('Telephone Number:')}
${person.telephone} 
-
${_('Postal Address:')}
${person.postal_address} 
- -
${_('IRC Nick:')}
${person.ircnick} 
-
${_('PGP Key:')}
${person.gpg_keyid} 
-
${_('Public SSH Key:')}
-
${person.ssh_key[:20]}.... 
-
No ssh key provided 
-
-
${_('Comments:')}
${person.comments} 
-
${_('Password:')}
${_('Valid')} (change)
-
${_('Account Status:')}
- ${_('Active')} - ${_('Vacation')} - ${_('Inactive')} - ${_('Pinged')} -
-
${_('CLA:')}
- ${_('CLA Done')} - ${_('Not Done')} (${_('Complete it!')}) -
-
-
-

${_('Your Roles')}

-

${_('%s\'s Roles') % person.human_name}

- - - - - diff --git a/fas/fas/templates/welcome.html b/fas/fas/templates/welcome.html deleted file mode 100644 index 9d58e38..0000000 --- a/fas/fas/templates/welcome.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - ${_('Welcome to FAS2')} - - - -

- ${Markup(_('Welcome to the Fedora Accounts System 2. Please submit bugs to <a href="https://fedorahosted.org/fas2">https://fedorahosted.org/fas2/</a> or stop by #fedora-admin on irc.freenode.net.'))} -

- - - diff --git a/fas/fas/tests/__init__.py b/fas/fas/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fas/fas/tests/test_controllers.py b/fas/fas/tests/test_controllers.py deleted file mode 100644 index b3acfac..0000000 --- a/fas/fas/tests/test_controllers.py +++ /dev/null @@ -1,37 +0,0 @@ -import unittest -import turbogears -from turbogears import testutil -from fas.controllers import Root -import cherrypy - -cherrypy.root = Root() - -class TestPages(unittest.TestCase): - - def setUp(self): - turbogears.startup.startTurboGears() - - def tearDown(self): - """Tests for apps using identity need to stop CP/TG after each test to - stop the VisitManager thread. - See http://trac.turbogears.org/turbogears/ticket/1217 for details. - """ - turbogears.startup.stopTurboGears() - - def test_method(self): - "the index method should return a string called now" - import types - result = testutil.call(cherrypy.root.index) - assert type(result["now"]) == types.StringType - - def test_indextitle(self): - "The indexpage should have the right title" - testutil.createRequest("/") - response = cherrypy.response.body[0].lower() - assert "welcome to turbogears" in response - - def test_logintitle(self): - "login page should have the right title" - testutil.createRequest("/login") - response = cherrypy.response.body[0].lower() - assert "login" in response diff --git a/fas/fas/tests/test_model.py b/fas/fas/tests/test_model.py deleted file mode 100644 index 8d0187f..0000000 --- a/fas/fas/tests/test_model.py +++ /dev/null @@ -1,22 +0,0 @@ -# If your project uses a database, you can set up database tests -# similar to what you see below. Be sure to set the db_uri to -# an appropriate uri for your testing database. sqlite is a good -# choice for testing, because you can use an in-memory database -# which is very fast. - -from turbogears import testutil, database -# from fas.model import YourDataClass, User - -# database.set_db_uri("sqlite:///:memory:") - -# class TestUser(testutil.DBTest): -# def get_model(self): -# return User -# def test_creation(self): -# "Object creation should set the name" -# obj = User(user_name = "creosote", -# email_address = "spam@python.not", -# display_name = "Mr Creosote", -# password = "Wafer-thin Mint") -# assert obj.display_name == "Mr Creosote" - diff --git a/fas/fas/user.py b/fas/fas/user.py deleted file mode 100644 index 5f3b679..0000000 --- a/fas/fas/user.py +++ /dev/null @@ -1,666 +0,0 @@ -import turbogears -from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler, config -from turbogears.database import session -import cherrypy - -import turbomail - -import sqlalchemy - -import os -import re -import gpgme -import StringIO -import crypt -import random -import subprocess -from OpenSSL import crypto - -from fas.model import People -from fas.model import Log - -from fas import openssl_fas -from fas.auth import * -from fas.util import available_languages - -from random import Random -import sha -from base64 import b64encode - -class KnownUser(validators.FancyValidator): - '''Make sure that a user already exists''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - try: - p = People.by_username(value) - except InvalidRequestError: - raise validators.Invalid(_("'%s' does not exist.") % value, value, state) - -class UnknownUser(validators.FancyValidator): - '''Make sure that a user doesn't already exist''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - try: - p = People.by_username(value) - except InvalidRequestError: - return - except: - raise validators.Invalid(_("Error: Could not create - '%s'") % value, value, state) - - raise validators.Invalid(_("'%s' already exists.") % value, value, state) - -class NonFedoraEmail(validators.FancyValidator): - '''Make sure that an email address is not @fedoraproject.org''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - if value.endswith('@fedoraproject.org'): - raise validators.Invalid(_("To prevent email loops, your email address cannot be @fedoraproject.org."), value, state) - -class ValidSSHKey(validators.FancyValidator): - ''' Make sure the ssh key uploaded is valid ''' - def _to_python(self, value, state): - - return value.file.read() - def validate_python(self, value, state): -# value = value.file.read() - keylines = value.split('\n') - for keyline in keylines: - if not keyline: - continue - keyline = keyline.strip() - m = re.match('^(rsa|dsa|ssh-rsa|ssh-dss) [ \t]*[^ \t]+.*$', keyline) - if not m: - raise validators.Invalid(_('Error - Not a valid ssh key: %s') % keyline, value, state) - -class ValidUsername(validators.FancyValidator): - '''Make sure that a username isn't blacklisted''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - username_blacklist = config.get('username_blacklist') - if re.compile(username_blacklist).match(value): - raise validators.Invalid(_("'%s' is an illegal username.") % value, value, state) - -class ValidLanguage(validators.FancyValidator): - '''Make sure that a username isn't blacklisted''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - if value not in available_languages(): - raise validators.Invalid(_('The language \'%s\' is not available.') % value, value, state) - - -class UserSave(validators.Schema): - targetname = KnownUser - human_name = validators.All( - validators.String(not_empty=True, max=42), - validators.Regex(regex='^[^\n:<>]+$'), - ) - ssh_key = ValidSSHKey(max=5000) - email = validators.All( - validators.Email(not_empty=True, strip=True, max=128), - NonFedoraEmail(not_empty=True, strip=True, max=128), - ) - locale = ValidLanguage(not_empty=True, strip=True) - #fedoraPersonBugzillaMail = validators.Email(strip=True, max=128) - #fedoraPersonKeyId- Save this one for later :) - postal_address = validators.String(max=512) - -class UserCreate(validators.Schema): - username = validators.All( - UnknownUser, - ValidUsername(not_empty=True), - validators.String(max=32, min=3), - validators.Regex(regex='^[a-z][a-z0-9]+$'), - ) - human_name = validators.All( - validators.String(not_empty=True, max=42), - validators.Regex(regex='^[^\n:<>]+$'), - ) - email = validators.All( - validators.Email(not_empty=True, strip=True), - NonFedoraEmail(not_empty=True, strip=True), - ) - #fedoraPersonBugzillaMail = validators.Email(strip=True) - postal_address = validators.String(max=512) - -class UserSetPassword(validators.Schema): - currentpassword = validators.String - # TODO (after we're done with most testing): Add complexity requirements? - password = validators.String(min=8) - passwordcheck = validators.String - chained_validators = [validators.FieldsMatch('password', 'passwordcheck')] - -class UserResetPassword(validators.Schema): - # TODO (after we're done with most testing): Add complexity requirements? - password = validators.String(min=8) - passwordcheck = validators.String - chained_validators = [validators.FieldsMatch('password', 'passwordcheck')] - -class UserView(validators.Schema): - username = KnownUser - -class UserEdit(validators.Schema): - targetname = KnownUser - -def generate_password(password=None, length=16): - ''' Generate Password ''' - secret = {} # contains both hash and password - - if not password: - # Exclude 1,l and 0,O - chars = '23456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ' - password = '' - for i in xrange(length): - password += random.choice(chars) - - secret['hash'] = crypt.crypt(password, "$1$%s" % generate_salt(8)) - secret['pass'] = password - - return secret - -def generate_salt(length=8): - chars = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - salt = '' - for i in xrange(length): - salt += random.choice(chars) - return salt - -def generate_token(length=32): - chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - token = '' - for i in xrange(length): - token += random.choice(chars) - return token - -class User(controllers.Controller): - - def __init__(self): - '''Create a User Controller. - ''' - - @identity.require(turbogears.identity.not_anonymous()) - def index(self): - '''Redirect to view - ''' - turbogears.redirect('/user/view/%s' % turbogears.identity.current.user_name) - - def jsonRequest(self): - return 'tg_format' in cherrypy.request.params and \ - cherrypy.request.params['tg_format'] == 'json' - - - @expose(template="fas.templates.error") - def error(self, tg_errors=None): - '''Show a friendly error message''' - if not tg_errors: - turbogears.redirect('/') - return dict(tg_errors=tg_errors) - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=UserView()) - @error_handler(error) - @expose(template="fas.templates.user.view", allow_json=True) - def view(self, username=None): - '''View a User. - ''' - if not username: - username = turbogears.identity.current.user_name - person = People.by_username(username) - if turbogears.identity.current.user_name == username: - personal = True - else: - personal = False - if isAdmin(person): - admin = True - # TODO: Should admins be able to see personal info? If so, enable this. - # Either way, let's enable this after the testing period. - #personal = True - else: - admin = False - cla = CLADone(person) - person.jsonProps = { - 'People': ('approved_memberships', 'unapproved_memberships') - } - return dict(person=person, cla=cla, personal=personal, admin=admin) - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=UserEdit()) - @error_handler(error) - @expose(template="fas.templates.user.edit") - def edit(self, targetname=None): - '''Edit a user - ''' - username = turbogears.identity.current.user_name - person = People.by_username(username) - - if targetname: - target = People.by_username(targetname) - else: - target = person - if not canEditUser(person, target): - turbogears.flash(_('You cannot edit %s') % target.username ) - turbogears.redirect('/user/view/%s', target.username) - return dict() - languages = available_languages() - return dict(target=target, languages=languages) - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=UserSave()) - @error_handler(error) - @expose(template='fas.templates.user.edit') - def save(self, targetname, human_name, telephone, postal_address, email, ssh_key=None, ircnick=None, gpg_keyid=None, comments='', locale='en', timezone='UTC'): - username = turbogears.identity.current.user_name - target = targetname - person = People.by_username(username) - target = People.by_username(target) - emailflash = '' - - if not canEditUser(person, target): - turbogears.flash(_("You do not have permission to edit '%s'") % target.username) - turbogears.redirect('/user/view/%s', target.username) - return dict() - try: - target.human_name = human_name - if target.email != email: - token = generate_token() - target.unverified_email = email - target.emailtoken = token - message = turbomail.Message(config.get('accounts_email'), email, _('Email Change Requested for %s') % person.username) - # TODO: Make this email friendlier. - message.plain = _(''' -You have recently requested to change your Fedora Account System email -to this address. To complete the email change, you must confirm your -ownership of this email by visiting the following URL (you will need to -login with your Fedora account first): - -https://admin.fedoraproject.org/accounts/user/verifyemail/%s -''') % token - emailflash = _(' Before your new email takes effect, you must confirm it. You should receive an email with instructions shortly.') - turbomail.enqueue(message) - target.ircnick = ircnick - target.gpg_keyid = gpg_keyid - target.telephone = telephone - if ssh_key: - target.ssh_key = ssh_key - target.postal_address = postal_address - target.comments = comments - target.locale = locale - target.timezone = timezone - except TypeError: - turbogears.flash(_('Your account details could not be saved: %s') % e) - else: - turbogears.flash(_('Your account details have been saved.') + ' ' + emailflash) - turbogears.redirect("/user/view/%s" % target.username) - return dict(target=target) - - # TODO: This took about 55 seconds for me to load - might want to limit it to the right accounts (systems user, accounts group) - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template="fas.templates.user.list", allow_json=True) - def list(self, search="a*"): - '''List users - ''' - re_search = re.sub(r'\*', r'%', search).lower() - if self.jsonRequest(): - people = [] - peoplesql = sqlalchemy.select([People.c.id, People.c.username, People.c.human_name, People.c.ssh_key, People.c.password]) - persons = peoplesql.execute() - for person in persons: - people.append({ - 'id' : person[0], - 'username' : person[1], - 'human_name' : person[2], - 'ssh_key' : person[3], - 'password' : person[4]}) - else: - people = People.query.filter(People.username.like(re_search)).order_by('username') - if people.count() < 0: - turbogears.flash(_("No users found matching '%s'") % search) - return dict(people=people, search=search) - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(format='json') - def email_list(self, search='*'): - re_search = re.sub(r'\*', r'%', search).lower() - people = People.query.filter(People.username.like(re_search)).order_by('username') - emails = {} - for person in people: - emails[person.username] = person.email - return dict(emails=emails) - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template='fas.templates.user.verifyemail') - def verifyemail(self, token, cancel=False): - username = turbogears.identity.current.user_name - person = People.by_username(username) - if cancel: - person.emailtoken = '' - turbogears.flash(_('Your pending email change has been canceled. The email change token has been invalidated.')) - turbogears.redirect('/user/view/%s' % username) - return dict() - if not person.unverified_email: - turbogears.flash(_('You do not have any pending email changes.')) - turbogears.redirect('/user/view/%s' % username) - return dict() - if person.emailtoken and (person.emailtoken != token): - turbogears.flash(_('Invalid email change token.')) - turbogears.redirect('/user/view/%s' % username) - return dict() - return dict(person=person, token=token) - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose() - def setemail(self, token): - username = turbogears.identity.current.user_name - person = People.by_username(username) - if not (person.unverified_email and person.emailtoken): - turbogears.flash(_('You do not have any pending email changes.')) - turbogears.redirect('/user/view/%s' % username) - return dict() - if person.emailtoken != token: - turbogears.flash(_('Invalid email change token.')) - turbogears.redirect('/user/view/%s' % username) - return dict() - ''' Log this ''' - oldEmail = person.email - person.email = person.unverified_email - Log(author_id=person.id, description='Email changed from %s to %s' % (oldEmail, person.email)) - person.unverified_email = '' - session.flush() - turbogears.flash(_('You have successfully changed your email to \'%s\'') % person.email) - turbogears.redirect('/user/view/%s' % username) - return dict() - - @error_handler(error) - @expose(template='fas.templates.user.new') - def new(self): - if turbogears.identity.not_anonymous(): - turbogears.flash(_('No need to sign up, you have an account!')) - turbogears.redirect('/user/view/%s' % turbogears.identity.current.user_name) - return dict() - - @validate(validators=UserCreate()) - @error_handler(error) - @expose(template='fas.templates.new') - def create(self, username, human_name, email, telephone=None, postal_address=None): - # TODO: Ensure that e-mails are unique? - # Also, perhaps implement a timeout- delete account - # if the e-mail is not verified (i.e. the person changes - # their password) withing X days. - try: - person = People() - person.username = username - person.human_name = human_name - person.telephone = telephone - person.email = email - person.password = '*' - person.status = 'active' - session.flush() - newpass = generate_password() - message = turbomail.Message(config.get('accounts_email'), person.email, _('Welcome to the Fedora Project!')) - message.plain = _(''' -You have created a new Fedora account! -Your new password is: %s - -Please go to https://admin.fedoraproject.org/accounts/ to change it. - -Welcome to the Fedora Project. Now that you've signed up for an -account you're probably desperate to start contributing, and with that -in mind we hope this e-mail might guide you in the right direction to -make this process as easy as possible. - -Fedora is an exciting project with lots going on, and you can -contribute in a huge number of ways, using all sorts of different -skill sets. To find out about the different ways you can contribute to -Fedora, you can visit our join page which provides more information -about all the different roles we have available. - -http://fedoraproject.org/en/join-fedora - -If you already know how you want to contribute to Fedora, and have -found the group already working in the area you're interested in, then -there are a few more steps for you to get going. - -Foremost amongst these is to sign up for the team or project's mailing -list that you're interested in - and if you're interested in more than -one group's work, feel free to sign up for as many mailing lists as -you like! This is because mailing lists are where the majority of work -gets organised and tasks assigned, so to stay in the loop be sure to -keep up with the messages. - -Once this is done, it's probably wise to send a short introduction to -the list letting them know what experience you have and how you'd like -to help. From here, existing members of the team will help you to find -your feet as a Fedora contributor. - -And finally, from all of us here at the Fedora Project, we're looking -forward to working with you! -''') % newpass['pass'] - turbomail.enqueue(message) - person.password = newpass['hash'] - except IntegrityError: - turbogears.flash(_("An account has already been registered with that email address.")) - turbogears.redirect('/user/new') - return dict() - else: - turbogears.flash(_('Your password has been emailed to you. Please log in with it and change your password')) - turbogears.redirect('/user/changepass') - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template="fas.templates.user.changepass") - def changepass(self): - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @validate(validators=UserSetPassword()) - @error_handler(error) - @expose(template="fas.templates.user.changepass") - def setpass(self, currentpassword, password, passwordcheck): - username = turbogears.identity.current.user_name - person = People.by_username(username) - -# current_encrypted = generate_password(currentpassword) -# print "PASS: %s %s" % (current_encrypted, person.password) - if not person.password == crypt.crypt(currentpassword, person.password): - turbogears.flash('Your current password did not match') - return dict() - # TODO: Enable this when we need to. - #if currentpassword == password: - # turbogears.flash('Your new password cannot be the same as your old one.') - # return dict() - newpass = generate_password(password) - try: - person.password = newpass['hash'] - Log(author_id=person.id, description='Password changed') - # TODO: Make this catch something specific. - except: - Log(author_id=person.id, description='Password change failed!') - turbogears.flash(_("Your password could not be changed.")) - return dict() - else: - turbogears.flash(_("Your password has been changed.")) - turbogears.redirect('/user/view/%s' % turbogears.identity.current.user_name) - return dict() - - @error_handler(error) - @expose(template="fas.templates.user.resetpass") - def resetpass(self): - if turbogears.identity.not_anonymous(): - turbogears.flash(_('You are already logged in!')) - turbogears.redirect('/user/view/%s' % turbogears.identity.current.user_name) - return dict() - - #TODO: Validate - @error_handler(error) - @expose(template="fas.templates.user.resetpass") - def sendtoken(self, username, email, encrypted=False): - import turbomail - # Logged in - if turbogears.identity.current.user_name: - turbogears.flash(_("You are already logged in.")) - turbogears.redirect('/user/view/%s', turbogears.identity.current.user_name) - return dict() - try: - person = People.by_username(username) - except InvalidRequestError: - turbogears.flash(_('Username email combo does not exist!')) - turbogears.redirect('/user/resetpass') - if email != person.email: - turbogears.flash(_("username + email combo unknown.")) - return dict() - token = generate_token() - message = turbomail.Message(config.get('accounts_email'), email, _('Fedora Project Password Reset')) - mail = _(''' -Somebody (hopefully you) has requested a password reset for your account! -To change your password (or to cancel the request), please visit -https://admin.fedoraproject.org/accounts/user/verifypass/%(user)s/%(token)s -''') % {'user': username, 'token': token} - if encrypted: - # TODO: Move this out to a single function (same as - # CLA one), think of how to make sure this doesn't get - # full of random keys (keep a clean Fedora keyring) - # TODO: MIME stuff? - keyid = re.sub('\s', '', person.gpg_keyid) - if not keyid: - turbogears.flash(_("This user does not have a GPG Key ID set, so an encrypted email cannot be sent.")) - return dict() - ret = subprocess.call([config.get('gpgexec'), '--keyserver', config.get('gpg_keyserver'), '--recv-keys', keyid]) - if ret != 0: - turbogears.flash(_("Your key could not be retrieved from subkeys.pgp.net")) - turbogears.redirect('/user/resetpass') - return dict() - else: - try: - # This may not be the neatest fix, but gpgme gave an error when mail was unicode. - plaintext = StringIO.StringIO(mail.encode('utf-8')) - ciphertext = StringIO.StringIO() - ctx = gpgme.Context() - ctx.armor = True - signer = ctx.get_key(re.sub('\s', '', config.get('gpg_fingerprint'))) - ctx.signers = [signer] - recipient = ctx.get_key(keyid) - def passphrase_cb(uid_hint, passphrase_info, prev_was_bad, fd): - os.write(fd, '%s\n' % config.get('gpg_passphrase')) - ctx.passphrase_cb = passphrase_cb - ctx.encrypt_sign([recipient], - gpgme.ENCRYPT_ALWAYS_TRUST, - plaintext, - ciphertext) - message.plain = ciphertext.getvalue() - except: - turbogears.flash(_('Your password reset email could not be encrypted.')) - return dict() - else: - message.plain = mail; - turbomail.enqueue(message) - person.passwordtoken = token - turbogears.flash(_('A password reset URL has been emailed to you.')) - turbogears.redirect('/login') - return dict() - - @error_handler(error) - @expose(template="fas.templates.user.newpass") - # TODO: Validator - def newpass(self, username, token, password=None, passwordcheck=None): - person = People.by_username(username) - if not person.passwordtoken: - turbogears.flash(_('You do not have any pending password changes.')) - turbogears.redirect('/login') - return dict() - if person.passwordtoken != token: - person.emailtoken = '' - turbogears.flash(_('Invalid password change token.')) - turbogears.redirect('/login') - return dict() - return dict(person=person, token=token) - - @error_handler(error) - @expose(template="fas.templates.user.verifypass") - # TODO: Validator - def verifypass(self, username, token, cancel=False): - person = People.by_username(username) - if not person.passwordtoken: - turbogears.flash(_('You do not have any pending password changes.')) - turbogears.redirect('/login') - return dict() - if person.passwordtoken != token: - turbogears.flash(_('Invalid password change token.')) - turbogears.redirect('/login') - return dict() - if cancel: - person.passwordtoken = '' - turbogears.flash(_('Your password reset has been canceled. The password change token has been invalidated.')) - turbogears.redirect('/login') - return dict() - return dict(person=person, token=token) - - @error_handler(error) - @expose() - @validate(validators=UserResetPassword()) - def setnewpass(self, username, token, password, passwordcheck): - person = People.by_username(username) - if not person.passwordtoken: - turbogears.flash(_('You do not have any pending password changes.')) - turbogears.redirect('/login') - return dict() - if person.passwordtoken != token: - person.emailtoken = '' - turbogears.flash(_('Invalid password change token.')) - turbogears.redirect('/login') - return dict() - ''' Log this ''' - newpass = generate_password(password) - person.password = newpass['hash'] - person.passwordtoken = '' - Log(author_id=person.id, description='Password changed') - session.flush() - turbogears.flash(_('You have successfully reset your password. You should now be able to login below.')) - turbogears.redirect('/login') - return dict() - - @identity.require(turbogears.identity.not_anonymous()) - @error_handler(error) - @expose(template="genshi-text:fas.templates.user.cert", format="text", content_type='text/plain; charset=utf-8') - def gencert(self): - username = turbogears.identity.current.user_name - person = People.by_username(username) - if CLADone(person): - person.certificate_serial = person.certificate_serial + 1 - - pkey = openssl_fas.createKeyPair(openssl_fas.TYPE_RSA, 1024); - - digest = config.get('openssl_digest') - expire = config.get('openssl_expire') - cafile = config.get('openssl_ca_file') - - cakey = openssl_fas.retrieve_key_from_file(cafile) - cacert = openssl_fas.retrieve_cert_from_file(cafile) - - req = openssl_fas.createCertRequest(pkey, digest=digest, - C=config.get('openssl_c'), - ST=config.get('openssl_st'), - L=config.get('openssl_l'), - O=config.get('openssl_o'), - OU=config.get('openssl_ou'), - CN=person.username, - emailAddress=person.email, - ) - - cert = openssl_fas.createCertificate(req, (cacert, cakey), person.certificate_serial, (0, expire), digest='md5') - certdump = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) - keydump = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey) - return dict(cert=certdump, key=keydump) - else: - turbogears.flash(_('Before generating a certificate, you must first complete the CLA.')) - turbogears.redirect('/cla/') - - diff --git a/fas/fas/util.py b/fas/fas/util.py deleted file mode 100644 index 5886cf2..0000000 --- a/fas/fas/util.py +++ /dev/null @@ -1,16 +0,0 @@ -import os -import codecs -from turbogears import config -from turbogears.i18n.tg_gettext import get_locale_dir - -def available_languages(): - """Return available languages for a given domain.""" - available_languages = [] - localedir = get_locale_dir() - linguas = codecs.open(os.path.join(localedir, 'LINGUAS'), 'r') - for lang in linguas.readlines(): - lang = lang.strip() - if lang and not lang.startswith('#'): - available_languages.append(lang) - return available_languages - diff --git a/fas/fas2.sql b/fas/fas2.sql deleted file mode 100644 index 2846398..0000000 --- a/fas/fas2.sql +++ /dev/null @@ -1,357 +0,0 @@ --- Copyright © 2008 Red Hat, Inc. All rights reserved. --- --- This copyrighted material is made available to anyone wishing to use, modify, --- copy, or redistribute it subject to the terms and conditions of the GNU --- General Public License v.2. This program is distributed in the hope that it --- will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the --- implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. --- See the GNU General Public License for more details. You should have --- received a copy of the GNU General Public License along with this program; --- if not, write to the Free Software Foundation, Inc., 51 Franklin Street, --- Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are --- incorporated in the source code or documentation are not subject to the GNU --- General Public License and may only be used or replicated with the express --- permission of Red Hat, Inc. --- --- Author(s): Toshio Kuratomi --- Ricky Zhou --- Mike McGrath --- -create database fas2 encoding = 'UTF8'; -\c fas2 - -create procedural language plpythonu - handler plpythonu_call_handler - validator plpythonu_validator; - -CREATE SEQUENCE person_seq; --- TODO: Set this to start where our last person_id is -SELECT setval('person_seq', 1111); - -CREATE TABLE people ( - -- tg_user::user_id - id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('person_seq'), - -- tg_user::user_name - username VARCHAR(32) UNIQUE NOT NULL, - -- tg_user::display_name - human_name TEXT NOT NULL, - -- TODO: Switch to this? - -- Also, app would be responsible for eliminating spaces and - -- uppercasing - -- gpg_fingerprint varchar(40), - gpg_keyid VARCHAR(64), - ssh_key TEXT, - -- tg_user::password - password VARCHAR(127) NOT NULL, - passwordtoken text null, - password_changed TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - email TEXT not null unique, - emailtoken TEXT, - unverified_email TEXT, - comments TEXT, - postal_address TEXT, - telephone TEXT, - facsimile TEXT, - affiliation TEXT, - certificate_serial INTEGER DEFAULT 1, - -- tg_user::created - creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - --approval_status TEXT DEFAULT 'unapproved', - internal_comments TEXT, - ircnick TEXT, - last_seen TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - status TEXT DEFAULT 'active', - status_change TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - locale TEXT not null DEFAULT 'C', - timezone TEXT null DEFAULT 'UTC', - latitude numeric, - longitude numeric, - check (status in ('active', 'vacation', 'inactive', 'pinged')) - --check (gpg_keyid ~ '^[0-9A-F]{17}$') -); - -create index people_status_idx on people(status); -cluster people_status_idx on people; - -CREATE TABLE configs ( - id SERIAL PRIMARY KEY, - person_id integer references people(id), - application TEXT not null, - attribute TEXT not null, - -- The value should be a simple value or a json string. - -- Please create more config keys rather than abusing this with - -- large datastructures. - value TEXT, - check (application in ('asterisk', 'moin', 'myfedora' ,'openid')) - -- Might end up removing openid, depending on how far we take the provider -); - -create index configs_person_id_idx on configs(person_id); -create index configs_application_idx on configs(application); -cluster configs_person_id_idx on configs; - -CREATE TABLE groups ( - -- tg_group::group_id - id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('person_seq'), - -- tg_group::group_name - name VARCHAR(32) UNIQUE NOT NULL, - -- tg_group::display_name - display_name TEXT, - -- Unlike users, groups can share email addresses - email TEXT, - emailtoken TEXT, - unverified_email TEXT, - owner_id INTEGER NOT NULL REFERENCES people(id), - group_type VARCHAR(16), - needs_sponsor BOOLEAN DEFAULT FALSE, - user_can_remove BOOLEAN DEFAULT TRUE, - prerequisite_id INTEGER REFERENCES groups(id), - joinmsg TEXT NULL DEFAULT '', - -- tg_group::created - creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - check (group_type in ('system', 'bugzilla','cvs', 'bzr', 'git', 'hg', 'mtn', - 'svn', 'shell', 'torrent', 'tracker', 'tracking', 'user')) -); - -create index groups_group_type_idx on groups(group_type); -create index groups_email_idx on groups(email); -cluster groups_group_type_idx on groups; - -CREATE TABLE person_roles ( - person_id INTEGER NOT NULL REFERENCES people(id), - group_id INTEGER NOT NULL REFERENCES groups(id), - -- role_type is something like "user", "administrator", etc. - -- role_status tells us whether this has been approved or not - role_type text NOT NULL, - role_status text DEFAULT 'unapproved', - internal_comments text, - sponsor_id INTEGER REFERENCES people(id), - creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - approval TIMESTAMP WITH TIME ZONE DEFAULT NULL, - primary key (person_id, group_id), - check (role_status in ('approved', 'unapproved')), - check (role_type in ('user', 'administrator', 'sponsor')) -); - -create index person_roles_person_id_idx on person_roles(person_id); -create index person_roles_group_id_idx on person_roles(group_id); --- We could cluster on either person or group. The choice of group is because --- groups are larger and therefore will take more memory if guessed wrong. --- Open to reevaluation. -cluster person_roles_group_id_idx on person_roles; - -CREATE TABLE group_roles ( - member_id INTEGER NOT NULL REFERENCES groups(id), - group_id INTEGER NOT NULL REFERENCES groups(id), - role_type text NOT NULL, - role_status text DEFAULT 'unapproved', - internal_comments text, - sponsor_id INTEGER REFERENCES people(id), - creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - approval TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - primary key (member_id, group_id), - check (role_status in ('approved', 'unapproved')), - check (role_type in ('user', 'administrator', 'sponsor')) -); - -create index group_roles_member_id_idx on group_roles(member_id); -create index group_roles_group_id_idx on group_roles(group_id); --- We could cluster on either member or group. The choice of member is --- because member pages will be viewed more frequently. --- Open to reevaluation. -cluster group_roles_group_id_idx on group_roles; - --- action r == remove --- action a == add -create table bugzilla_queue ( - email text not null, - group_id INTEGER references groups(id) not null, - person_id INTEGER references people(id) not null, - action CHAR(1) not null, - primary key (email, group_id), - check (action ~ '[ar]') -); - --- Log changes to the account system -create table log ( - id serial primary key, - author_id INTEGER references people(id) not null, - changetime TIMESTAMP WITH TIME ZONE default NOW(), - description TEXT -); - -create index log_changetime_idx on log(changetime); -cluster log_changetime_idx on log; - --- --- This table allows certain services to be restricted by hostname/ip/person. --- --- Any time a request for a restricted action is requested, the FAS server --- consults this table to see if the user@(hostname/ip) is allowed to access --- the resource. If approved is true, the request is granted. If false or --- null, the request is denied. --- --- New records are created when a request is first made by a specific --- username@(hostname/id) --- -create table requests ( - id serial primary key, - person_id INTEGER not null references people(id), - hostname TEXT not null, - ip TEXT not null, - action TEXT not null default 'trust_all', - last_request TIMESTAMP WITH TIME ZONE default now() not null, - approved boolean, - unique (person_id, hostname, ip, action) -); - -create index requests_last_request_idx on requests(last_request); -create index hostname_idx on requests(hostname); -create index ip_idx on requests(ip); -create index person_id_idx on requests(person_id); -cluster requests_last_request_idx on requests; - --- --- turbogears session tables --- -create table visit ( - visit_key CHAR(40) primary key, - created TIMESTAMP WITH TIME ZONE not null default now(), - expiry TIMESTAMP WITH TIME ZONE -); - -create index visit_expiry_idx on visit(expiry); -cluster visit_expiry_idx on visit; - -create table visit_identity ( - visit_key CHAR(40) primary key references visit(visit_key), - user_id INTEGER references people(id) -); - --- --- When the fedorabugs role is updated for a person, add them to bugzilla queue. --- -create or replace function bugzilla_sync() returns trigger as $bz_sync$ - # Decide which row we are operating on and the action to take - if TD['event'] == 'DELETE': - # 'r' for removing an entry from bugzilla - newaction = 'r' - row = TD['old'] - else: - # insert or update - row = TD['new'] - if row['role_status'] == 'approved': - # approved so add an entry to bugzilla - newaction = 'a' - else: - # no longer approved so remove the entry from bugzilla - newaction = 'r' - - # Get the group id for fedorabugs - result = plpy.execute("select id from groups where name = 'fedorabugs'", 1) - if not result: - # Danger Will Robinson! A basic FAS group does not exist! - plpy.error('Basic FAS group fedorabugs does not exist') - # If this is not a fedorabugs role, no change needed - if row['group_id'] != result[0]['id']: - return None - - # Retrieve the bugzilla email address - ### FIXME: Once we implement it, we will want to add a check for an email - # address in configs::application='bugzilla',person_id=person_id, - # attribute='login' - plan = plpy.prepare("select email from people where id = $1", ('int4',)) - result = plpy.execute(plan, (row['person_id'],)) - if not result: - # No email address so nothing can be done - return None - email = result[0]['email'] - - # If there is already a row in bugzilla_queue update, otherwise insert - plan = plpy.prepare("select email from bugzilla_queue where email = $1", - ('text',)) - result = plpy.execute(plan, (email,), 1) - if result: - plan = plpy.prepare("update bugzilla_queue set action = $1" - " where email = $2", ('char', 'text')) - plpy.execute(plan, (newaction, email)) - else: - plan = plpy.prepare("insert into bugzilla_queue (email, group_id" - ", person_id, action) values ($1, $2, $3, $4)", - ('text', 'int4', 'int4', 'char')) - plpy.execute(plan, (email, row['group_id'], row['person_id'], newaction)) - return None -$bz_sync$ language plpythonu; - -create trigger role_bugzilla_sync before update or insert or delete - on person_roles - for each row execute procedure bugzilla_sync(); - --- --- When an email address changes, check whether it needs to be changed in --- bugzilla as well. --- -create or replace function bugzilla_sync_email() returns trigger AS $bz_sync_e$ - if TD['old']['email'] == TD['new']['email']: - # We only care if the email has been changed - return None; - - # Get the group id for fedorabugs - result = plpy.execute("select id from groups where name = 'fedorabugs'", 1) - if not result: - # Danger Will Robinson! A basic FAS group does not exist! - plpy.error('Basic FAS group fedorabugs does not exist') - fedorabugsId = result[0]['id'] - - plan = plpy.prepare("select person_id from person_roles where" - " role_status = 'approved' and group_id = $1 " - " and person_id = $2", ('int4', 'int4')) - result = plpy.execute(plan, (fedorabugsId, TD['old']['id']), 1) - if not result: - # We only care if Person belongs to fedorabugs - return None; - - # Remove the old Email and add the new one - changes = [] - changes.append((TD['old']['email'], fedorabugsId, TD['old']['id'], 'r')) - changes.append((TD['new']['email'], fedorabugsId, TD['new']['id'], 'a')) - - for change in changes: - # Check if we already have a pending change - plan = plpy.prepare("select b.email from bugzilla_queue as b where" - " b.email = $1", ('text',)) - result = plpy.execute(plan, (change[0],), 1) - if result: - # Yes, update that change - plan = plpy.prepare("update bugzilla_queue set email = $1," - " group_id = $2, person_id = $3, action = $4 where " - " email = $1", ('text', 'int4', 'int4', 'char')) - plpy.execute(plan, change) - else: - # No, add a new change - plan = plpy.prepare("insert into bugzilla_queue" - " (email, group_id, person_id, action)" - " values ($1, $2, $3, $4)", ('text', 'int4', 'int4', 'char')) - plpy.execute(plan, change) - - return None -$bz_sync_e$ language plpythonu; - -create trigger email_bugzilla_sync before update on people - for each row execute procedure bugzilla_sync_email(); - --- For Fas to connect to the database -GRANT ALL ON TABLE people, groups, person_roles, group_roles, bugzilla_queue, configs, person_seq, visit, visit_identity, log, log_id_seq TO GROUP fedora; - --- Create default admin user - Default Password "admin" -INSERT INTO people (id, username, human_name, password, email) VALUES (100001, 'admin', 'Admin User', '$1$djFfnacd$b6NFqFlac743Lb4sKWXj4/', 'root@localhost'); - --- Create default groups and populate -INSERT INTO groups (id, name, display_name, owner_id, group_type, user_can_remove) VALUES (100002, 'cla_done', 'CLA Done Group', (SELECT id from people where username='admin'), 'tracking', false); -INSERT INTO groups (id, name, display_name, owner_id, group_type, user_can_remove) VALUES (101441, 'cla_fedora', 'Fedora CLA Group', (SELECT id from people where username='admin'), 'tracking', false); -INSERT INTO groups (id, name, display_name, owner_id, group_type) VALUES (100006, 'accounts', 'Account System Admins', (SELECT id from people where username='admin'), 'tracking'); -INSERT INTO groups (id, name, display_name, owner_id, group_type) VALUES (100148, 'fedorabugs', 'Fedora Bugs Group', (SELECT id from people where username='admin'), 'tracking'); -INSERT INTO groups (name, display_name, owner_id, group_type) VALUES ('fas-system', 'System users allowed to get password and key information', (SELECT id from people where username='admin'), 'system'); - - -INSERT INTO person_roles (person_id, group_id, role_type, role_status, internal_comments, sponsor_id) VALUES ((SELECT id from people where username='admin'), (select id from groups where name='accounts'), 'administrator', 'approved', 'created at install time', (SELECT id from people where username='admin')); diff --git a/fas/po/LINGUAS b/fas/po/LINGUAS deleted file mode 100644 index 074a8ba..0000000 --- a/fas/po/LINGUAS +++ /dev/null @@ -1,2 +0,0 @@ -en -pl diff --git a/fas/po/de.po b/fas/po/de.po deleted file mode 100644 index 6cb0886..0000000 --- a/fas/po/de.po +++ /dev/null @@ -1,1710 +0,0 @@ -# Translations template for PROJECT. -# Copyright (C) 2008 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# Fabian Affolter , 2008. -# -msgid "" -msgstr "" -"Project-Id-Version: FAS2\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2008-03-18 10:39-0400\n" -"PO-Revision-Date: 2008-03-16 01:16+0100\n" -"Last-Translator: Fabian Affolter \n" -"Language-Team: German \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.1\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Poedit-Language: German\n" - -#: fas/auth.py:149 fas/auth.py:157 -#, python-format -msgid "%s membership required before application to this group is allowed" -msgstr "" - -#: fas/cla.py:70 -#, fuzzy -msgid "You have already completed the CLA." -msgstr "Es existiert bereits ein Profil namens »%s«" - -#: fas/cla.py:74 -#, fuzzy -msgid "You have not completed the CLA." -msgstr "%s: kann Sie nicht identifizieren!\n" - -#: fas/cla.py:78 -msgid "" -"To complete the CLA, we must have your telephone number and postal address. " -"Please ensure they have been filled out." -msgstr "" - -#: fas/cla.py:91 -#, python-format -msgid "You could not be added to the '%s' group." -msgstr "" - -#: fas/cla.py:117 -#, python-format -msgid "" -"You have successfully completed the CLA. You are now in the '%s' group." -msgstr "" - -#: fas/controllers.py:100 -#, python-format -msgid "Welcome, %s" -msgstr "Willkommen, %s" - -#: fas/controllers.py:115 -msgid "" -"The credentials you supplied were not correct or did not grant access to " -"this resource." -msgstr "" - -#: fas/controllers.py:118 -msgid "You must provide your credentials before accessing this resource." -msgstr "" - -#: fas/controllers.py:121 -msgid "Please log in." -msgstr "Bitte anmelden." - -#: fas/controllers.py:132 -#, fuzzy -msgid "You have successfully logged out." -msgstr "Sie haben einen Qun angelegt" - -#: fas/controllers.py:144 fas/user.py:93 -#, fuzzy, python-format -msgid "The language '%s' is not available." -msgstr "Die Gruppe %s existiert nicht." - -#: fas/group.py:23 -#, python-format -msgid "The group '%s' does not exist." -msgstr "Die Gruppe %s existiert nicht." - -#: fas/group.py:35 -#, python-format -msgid "The group '%s' already exists." -msgstr "Die Gruppe %s existiert bereits." - -#: fas/group.py:131 fas/group.py:481 -#, fuzzy, python-format -msgid "You cannot view '%s'" -msgstr "Betrachten: Kann Datei %s nicht öffnen: %s\n" - -#: fas/group.py:145 fas/group.py:161 -msgid "Only FAS adminstrators can create groups." -msgstr "Nur FAS-Adminstrators können Gruppen anlegen." - -#: fas/group.py:179 -#, fuzzy, python-format -msgid "The group: '%s' could not be created." -msgstr "eine Verknüpfung konnte nicht angelegt werden (%s)" - -#: fas/group.py:189 -#, python-format -msgid "" -"The group: '%(group)s' has been created, but '%(user)s' could not be added " -"as a group administrator." -msgstr "" - -#: fas/group.py:191 -#, fuzzy, python-format -msgid "The group: '%s' has been created." -msgstr "wurde erstellt für das Gerät" - -#: fas/group.py:206 fas/group.py:221 -#, fuzzy, python-format -msgid "You cannot edit '%s'." -msgstr "" -"Sie können diese Partition nicht bearbeiten:\n" -"\n" - -#: fas/group.py:238 -#, fuzzy -msgid "The group details could not be saved." -msgstr "Die Datei konnte nicht als »%s« gespeichert werden." - -#: fas/group.py:240 -#, fuzzy -msgid "The group details have been saved." -msgstr "Datei wurde gespeichert" - -#: fas/group.py:266 -#, fuzzy, python-format -msgid "No Groups found matching '%s'" -msgstr "Keine entsprechenden Benutzer gefunden" - -#: fas/group.py:284 -#, python-format -msgid "%(user)s can not apply to %(group)s." -msgstr "" - -#: fas/group.py:292 -#, python-format -msgid "%(user)s could not apply to %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:303 -#, python-format -msgid "" -"\n" -"Fedora user %(user)s, aka %(name)s <%(email)s> has requested\n" -"membership for %(applicant)s (%(applicant_name)s) in the %(group)s group and " -"needs a sponsor.\n" -"\n" -"Please go to %(url)s to take action. \n" -msgstr "" - -#: fas/group.py:310 -#, fuzzy, python-format -msgid "%(user)s has applied to %(group)s!" -msgstr "Füge Benutzer zur Gruppe \"wheel\" hinzu" - -#: fas/group.py:327 -#, fuzzy, python-format -msgid "You cannot sponsor '%s'" -msgstr "Sie können keinen freien Speicherplatz löschen." - -#: fas/group.py:334 -#, python-format -msgid "%(user)s could not be sponsored in %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:340 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has sponsored you for membership in the %(group)s\n" -"group of the Fedora account system. If applicable, this change should\n" -"propagate into the e-mail aliases and CVS repository within an hour.\n" -"\n" -"%(joinmsg)s\n" -msgstr "" - -#: fas/group.py:348 -#, fuzzy, python-format -msgid "'%s' has been sponsored!" -msgstr "wurde gelöscht --]\n" - -#: fas/group.py:365 -#, fuzzy, python-format -msgid "You cannot remove '%(user)s' from '%(group)s'." -msgstr "" -"Sie können den Benutzer '%s' nicht aus dessen primären Gruppe entfernen." - -#: fas/group.py:372 -#, python-format -msgid "%(user)s could not be removed from %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:377 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has removed you from the '%(group)s'\n" -"group of the Fedora Accounts System This change is effective\n" -"immediately for new operations, and should propagate into the e-mail\n" -"aliases within an hour.\n" -msgstr "" - -#: fas/group.py:384 -#, python-format -msgid "%(name)s has been removed from %(group)s" -msgstr "" - -#: fas/group.py:401 -#, fuzzy, python-format -msgid "You cannot upgrade '%s'" -msgstr "Sie können keinen freien Speicherplatz löschen." - -#: fas/group.py:408 -#, python-format -msgid "%(name)s could not be upgraded in %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:417 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has upgraded you to %(status)s status in the\n" -"'%(group)s' group of the Fedora Accounts System This change is\n" -"effective immediately for new operations, and should propagate\n" -"into the e-mail aliases within an hour.\n" -msgstr "" - -#: fas/group.py:424 -#, fuzzy, python-format -msgid "%s has been upgraded!" -msgstr "wurde gelöscht --]\n" - -#: fas/group.py:440 -#, fuzzy, python-format -msgid "You cannot downgrade '%s'" -msgstr "Sie können keinen freien Speicherplatz löschen." - -#: fas/group.py:447 -#, python-format -msgid "%(name)s could not be downgraded in %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:455 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has downgraded you to %(status)s status in the\n" -"'%(group)s' group of the Fedora Accounts System This change is\n" -"effective immediately for new operations, and should propagate\n" -"into the e-mail aliases within an hour.\n" -msgstr "" - -#: fas/group.py:462 -#, fuzzy, python-format -msgid "%s has been downgraded!" -msgstr "wurde gelöscht --]\n" - -#: fas/group.py:509 -#, fuzzy -msgid "Come join The Fedora Project!" -msgstr "Beim Fedora-Übersetzungsprojekt beitreten" - -#: fas/group.py:510 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has invited you to join the Fedora\n" -"Project! We are a community of users and developers who produce a\n" -"complete operating system from entirely free and open source software\n" -"(FOSS). %(name)s thinks that you have knowledge and skills\n" -"that make you a great fit for the Fedora community, and that you might\n" -"be interested in contributing.\n" -"\n" -"How could you team up with the Fedora community to use and develop your\n" -"skills? Check out http://fedoraproject.org/join-fedora for some ideas.\n" -"Our community is more than just software developers -- we also have a\n" -"place for you whether you're an artist, a web site builder, a writer, or\n" -"a people person. You'll grow and learn as you work on a team with other\n" -"very smart and talented people.\n" -"\n" -"Fedora and FOSS are changing the world -- come be a part of it!" -msgstr "" - -#: fas/group.py:527 -#, fuzzy, python-format -msgid "Message sent to: %s" -msgstr "zum Drucker `%s' geschickt" - -#: fas/group.py:530 -#, python-format -msgid "You are not in the '%s' group." -msgstr "Sie sind nicht in der '%s'-Gruppe." - -#: fas/help.py:9 fas/help.py:49 -#, fuzzy -msgid "Error" -msgstr "Fehler!" - -#: fas/help.py:9 fas/help.py:49 -msgid "

We could not find that help item

" -msgstr "" - -#: fas/help.py:10 -msgid "IRC Nick (Optional)" -msgstr "" - -#: fas/help.py:10 -msgid "" -"

IRC Nick is used to identify yourself on irc.freenode.net. Please " -"register your nick on irc.freenode.net first, then fill this in so people " -"can find you online when they need to

" -msgstr "" - -#: fas/help.py:11 -msgid "Email (Required)" -msgstr "" - -#: fas/help.py:11 -msgid "" -"

This email address should be your prefered email contact and will be used " -"to send various official emails to. This is also where your @fedoraproject." -"org email will get forwarded

" -msgstr "" - -#: fas/help.py:12 -#, fuzzy -msgid "Full Name (Required)" -msgstr "Vollständiger Name:" - -#: fas/help.py:12 -msgid "

Your Human Name or \"real life\" name

" -msgstr "" - -#: fas/help.py:13 -#, fuzzy -msgid "GPG Key" -msgstr "PGP-Schlüssel" - -#: fas/help.py:13 -msgid "" -"

A GPG key is generally used to prove that a message or email came from " -"you or to encrypt information so that only the recipients can read it. This " -"can be used when a password reset is sent to your email.

" -msgstr "" - -#: fas/help.py:14 -#, fuzzy -msgid "Telephone" -msgstr "Telefonnummer" - -#: fas/help.py:14 -msgid "" -"

Required in order to complete the CLA. Sometimes during a time of emergency someone " -"from the Fedora Project may need to contact you. For more information see " -"our Privacy " -"Policy

" -msgstr "" - -#: fas/help.py:15 fas/templates/user/edit.html:36 -msgid "Postal Address" -msgstr "Post-Adresse" - -#: fas/help.py:15 -msgid "" -"

Required in order to complete the CLA. This should be a mailing address where you " -"can be contacted. See our Privacy Policy about any concerns.

" -msgstr "" - -#: fas/help.py:16 -msgid "Timezone (Optional)" -msgstr "" - -#: fas/help.py:16 -msgid "

Please specify the time zone you are in.

" -msgstr "" - -#: fas/help.py:17 -msgid "Comments (Optional)" -msgstr "" - -#: fas/help.py:17 -msgid "

Misc comments about yourself.

" -msgstr "" - -#: fas/help.py:18 fas/templates/user/list.html:28 -msgid "Account Status" -msgstr "Konto-Status" - -#: fas/help.py:18 -msgid "" -"

Shows account status, possible values include

  • Valid
  • Disabled
  • Expired

" -msgstr "" - -#: fas/help.py:19 -#, fuzzy -msgid "CLA" -msgstr "CLA:" - -#: fas/help.py:19 -msgid "" -"

In order to become a full Fedora contributor you must complete the Contributor " -"License Agreement. This license is a legal agreement between you and " -"Red Hat. Full status allows people to contribute content and code and is " -"recommended for anyone interested in getting involved in the Fedora Project." -"

" -msgstr "" - -#: fas/help.py:20 fas/templates/user/edit.html:51 -msgid "Public SSH Key" -msgstr "Öffentlicher SSH-Schlüssel" - -#: fas/help.py:20 -msgid "" -"

Many resources require public key authentiaction to work. By uploading " -"your public key to us, you can then log in to our servers. Type \"man ssh-" -"keygen\" for more information on creating your key. Once created you will " -"want to upload ~/.ssh/id_dsa.pub or ~/.ssh/id_rsa.pub

" -msgstr "" - -#: fas/help.py:21 fas/templates/user/edit.html:67 -msgid "Locale" -msgstr "Gebietsschema" - -#: fas/help.py:21 -msgid "" -"

For non-english speaking peoples this allows individuals to select which " -"locale they are in.

" -msgstr "" - -#: fas/help.py:23 fas/templates/group/list.html:47 -msgid "Apply" -msgstr "Anwenden" - -#: fas/help.py:23 -msgid "" -"

Applying for a group is like applying for a job and it can certainly take " -"a while to get in. Many groups have their own rules about how to actually " -"get approved or sponsored. For more information on how the account system " -"works see the about page.

" -msgstr "" - -#: fas/help.py:24 fas/templates/group/view.html:107 -msgid "Remove" -msgstr "Entfernen" - -#: fas/help.py:24 -msgid "" -"

Removing a person from a group will cause that user to no longer be in " -"the group. They will need to re-apply to get in. Admins can remove anyone, " -"Sponsors can remove users, users can't remove anyone.

" -msgstr "" - -#: fas/help.py:25 fas/templates/group/view.html:111 -#, fuzzy -msgid "Upgrade" -msgstr "Hochrüsten" - -#: fas/help.py:25 -msgid "" -"

Upgrade a persons status in this group.

  • from user -> to sponsor
  • From sponsor -> administrator
  • administrators cannot be " -"upgraded beyond administrator

" -msgstr "" - -#: fas/help.py:26 fas/templates/group/view.html:115 -msgid "Downgrade" -msgstr "" - -#: fas/help.py:26 -msgid "" -"

Downgrade a persons status in the group.

  • from administrator -> to " -"sponsor
  • From sponsor -> user
  • users cannot be downgraded " -"below user, you may want to remove them

" -msgstr "" - -#: fas/help.py:27 fas/templates/group/view.html:102 -#, fuzzy -msgid "Approve" -msgstr "Akzeptieren" - -#: fas/help.py:27 -msgid "" -"

A sponsor or administrator can approve users to be in a group. Once the " -"user has applied for the group, go to the group's page and click approve to " -"approve the user.

" -msgstr "" - -#: fas/help.py:28 fas/templates/group/view.html:76 -#: fas/templates/group/view.html:98 -msgid "Sponsor" -msgstr "Sponsor" - -#: fas/help.py:28 -msgid "" -"

A sponsor or administrator can sponsor users to be in a gruop. Once the " -"user has applied for the group, go to the group's page and click approve to " -"sponsor the user. Sponsorship of a user implies that you are approving a " -"user and may mentor and answer their questions as they come up.

" -msgstr "" - -#: fas/help.py:29 -#, fuzzy -msgid "Add User" -msgstr "Benutzer hinzufügen:" - -#: fas/help.py:29 -msgid "" -"

Manually add a user to a group. Place their username in this field and " -"click 'Add'

" -msgstr "" - -#: fas/help.py:30 -#, fuzzy -msgid "Group Name" -msgstr "Gruppenname:" - -#: fas/help.py:30 -msgid "" -"

The name of the group you'd like to create. It should be alphanumeric " -"though '-'s are allowed

" -msgstr "" - -#: fas/help.py:31 -#, fuzzy -msgid "Display Name" -msgstr "Angezeigter Name:" - -#: fas/help.py:31 -msgid "

More human readable name of the group

" -msgstr "" - -#: fas/help.py:32 -#, fuzzy -msgid "Group Owner" -msgstr "Gruppe-Eigentümer:" - -#: fas/help.py:32 -msgid "

The name of the owner who will run this group

" -msgstr "" - -#: fas/help.py:33 -#, fuzzy -msgid "Group Type" -msgstr "Gruppentyp:" - -#: fas/help.py:33 -msgid "" -"

Normally it is safe to leave this blank. Though some values include " -"'tracking', 'shell', 'cvs', 'git', 'hg', 'svn', and 'mtn'. This value only " -"really matters if the group is to end up getting shell access or commit " -"access somewhere like fedorahosted.

" -msgstr "" - -#: fas/help.py:34 -#, fuzzy -msgid "Needs Sponsor" -msgstr "Benötigt Spnsor:" - -#: fas/help.py:34 -msgid "" -"

If your group requires sponsorship (recommended), this means that when a " -"user is approved by a sponsor. That relationship is recorded in the account " -"system. If user A sponsors user N, then in viewing the members of this " -"group, people will know to contact user A about user N if something goes " -"wrong. If this box is unchecked, this means that only approval is needed " -"and no relationship is recorded about who did the approving

" -msgstr "" - -#: fas/help.py:35 -#, fuzzy -msgid "Self Removal" -msgstr "Entfernung von Benachrichtigungen" - -#: fas/help.py:35 -msgid "" -"

Should users be able to remove themselves from this group without " -"sponsor / admin intervention? (recommended yes)

" -msgstr "" - -#: fas/help.py:36 -#, fuzzy -msgid "Must Belong To" -msgstr "Objekt(e) gehören in folgende _Kategorien:" - -#: fas/help.py:36 -msgid "" -"

Before a user can join this group, they must belong to the group listed " -"in this box. This value cannot be removed without administrative " -"intervention, only changed. Recommended values are for the 'cla_done' " -"group.

" -msgstr "" - -#: fas/help.py:37 -#, fuzzy -msgid "Join Message" -msgstr "Nachrichtentransportsystem D-BUS" - -#: fas/help.py:37 -msgid "" -"

This message will go out to users when they join the group. It should be " -"informative and offer tips about what to do next. A description of the " -"group would also be valuable here

" -msgstr "" - -#: fas/help.py:38 -msgid "Client Side Cert" -msgstr "" - -#: fas/help.py:38 -msgid "" -"

The client side cert is generally used to grant access to upload packages " -"to Fedora or for other authentication purposes like with koji. If you are " -"not a package maintainer there is no need to worry about the client side " -"cert

" -msgstr "" - -#: fas/model.py:133 -msgid "user is already in this group" -msgstr "Benutzer ist schon in dieser Gruppe" - -#: fas/model.py:146 fas/model.py:161 fas/model.py:215 -msgid "user is not a member" -msgstr "Benutzer ist kein Mitglied" - -#: fas/model.py:150 -msgid "administrators cannot be upgraded any further" -msgstr "" - -#: fas/model.py:165 -msgid "users cannot be downgraded any further" -msgstr "" - -#: fas/model.py:174 -msgid "user is not an unapproved member" -msgstr "Benutzer ist ein unbestätigtes Mitglied" - -#: fas/openid_fas.py:55 -#, fuzzy -msgid "The OpenID request could not be decoded." -msgstr "Die Anforderung konnte von diesem Server nicht verstanden werden." - -#: fas/safasprovider.py:126 -#, fuzzy, python-format -msgid "Loading: %(visitmod)s" -msgstr "Daten laden ..." - -#: fas/user.py:38 -#, python-format -msgid "'%s' does not exist." -msgstr "'%s' existiert nicht." - -#: fas/user.py:50 -#, fuzzy, python-format -msgid "Error: Could not create - '%s'" -msgstr "" -"[-- Fehler: Konnte PGP-Subprozess nicht erzeugen! --]\n" -"\n" - -#: fas/user.py:52 -#, python-format -msgid "'%s' already exists." -msgstr "'%s' existiert bereits." - -#: fas/user.py:60 -msgid "" -"To prevent email loops, your email address cannot be @fedoraproject.org." -msgstr "" - -#: fas/user.py:76 -#, fuzzy, python-format -msgid "Error - Not a valid ssh key: %s" -msgstr "Die Adresse des Schlüsselservers ist ungültig." - -#: fas/user.py:85 -#, fuzzy, python-format -msgid "'%s' is an illegal username." -msgstr "Der angegebene SRP-Benutzername ist illegal." - -#: fas/user.py:245 -#, fuzzy, python-format -msgid "You cannot edit %s" -msgstr "" -"Sie können diese Partition nicht bearbeiten:\n" -"\n" - -#: fas/user.py:263 -#, fuzzy, python-format -msgid "You do not have permission to edit '%s'" -msgstr "Sie haben keine Berechtigung, %s auszuführen." - -#: fas/user.py:272 -#, fuzzy, python-format -msgid "Email Change Requested for %s" -msgstr "URI zu Änderungsgrundangabe" - -#: fas/user.py:274 -#, python-format -msgid "" -"\n" -"You have recently requested to change your Fedora Account System email\n" -"to this address. To complete the email change, you must confirm your\n" -"ownership of this email by visiting the following URL (you will need to\n" -"login with your Fedora account first):\n" -"\n" -"https://admin.fedoraproject.org/accounts/user/verifyemail/%s\n" -msgstr "" - -#: fas/user.py:282 -msgid "" -" Before your new email takes effect, you must confirm it. You should " -"receive an email with instructions shortly." -msgstr "" - -#: fas/user.py:294 -#, python-format -msgid "Your account details could not be saved: %s" -msgstr "" - -#: fas/user.py:296 -#, fuzzy -msgid "Your account details have been saved." -msgstr "Ihre Sitzung wurde abgespeichert." - -#: fas/user.py:322 -#, fuzzy, python-format -msgid "No users found matching '%s'" -msgstr "Keine entsprechenden Benutzer gefunden" - -#: fas/user.py:344 -msgid "" -"Your pending email change has been canceled. The email change token has " -"been invalidated." -msgstr "" - -#: fas/user.py:348 fas/user.py:364 -msgid "You do not have any pending email changes." -msgstr "" - -#: fas/user.py:352 fas/user.py:368 -#, fuzzy -msgid "Invalid email change token." -msgstr "ungültiger Wert »%s« für Symbol %s" - -#: fas/user.py:377 -#, python-format -msgid "You have successfully changed your email to '%s'" -msgstr "" - -#: fas/user.py:385 -msgid "No need to sign up, you have an account!" -msgstr "" - -#: fas/user.py:407 -#, fuzzy -msgid "Welcome to the Fedora Project!" -msgstr "Willkommen beim Fedora-Überetzungsprojekt!" - -#: fas/user.py:408 -#, python-format -msgid "" -"\n" -"You have created a new Fedora account!\n" -"Your new password is: %s\n" -"\n" -"Please go to https://admin.fedoraproject.org/accounts/ to change it.\n" -"\n" -"Welcome to the Fedora Project. Now that you've signed up for an\n" -"account you're probably desperate to start contributing, and with that\n" -"in mind we hope this e-mail might guide you in the right direction to\n" -"make this process as easy as possible.\n" -"\n" -"Fedora is an exciting project with lots going on, and you can\n" -"contribute in a huge number of ways, using all sorts of different\n" -"skill sets. To find out about the different ways you can contribute to\n" -"Fedora, you can visit our join page which provides more information\n" -"about all the different roles we have available.\n" -"\n" -"http://fedoraproject.org/en/join-fedora\n" -"\n" -"If you already know how you want to contribute to Fedora, and have\n" -"found the group already working in the area you're interested in, then\n" -"there are a few more steps for you to get going.\n" -"\n" -"Foremost amongst these is to sign up for the team or project's mailing\n" -"list that you're interested in - and if you're interested in more than\n" -"one group's work, feel free to sign up for as many mailing lists as\n" -"you like! This is because mailing lists are where the majority of work\n" -"gets organised and tasks assigned, so to stay in the loop be sure to\n" -"keep up with the messages.\n" -"\n" -"Once this is done, it's probably wise to send a short introduction to\n" -"the list letting them know what experience you have and how you'd like\n" -"to help. From here, existing members of the team will help you to find\n" -"your feet as a Fedora contributor.\n" -"\n" -"And finally, from all of us here at the Fedora Project, we're looking\n" -"forward to working with you!\n" -msgstr "" - -#: fas/user.py:449 -msgid "An account has already been registered with that email address." -msgstr "" - -#: fas/user.py:453 -msgid "" -"Your password has been emailed to you. Please log in with it and change " -"your password" -msgstr "" - -#: fas/user.py:487 -#, fuzzy -msgid "Your password could not be changed." -msgstr "Änderung des NIS-Passworts nicht möglich." - -#: fas/user.py:490 -#, fuzzy -msgid "Your password has been changed." -msgstr "Ihr Passwort wurde geändert." - -#: fas/user.py:498 -msgid "You are already logged in!" -msgstr "Sie sind schon angemeldet!" - -#: fas/user.py:509 -msgid "You are already logged in." -msgstr "Sie sind schon angemeldet." - -#: fas/user.py:515 -msgid "Username email combo does not exist!" -msgstr "" - -#: fas/user.py:518 -msgid "username + email combo unknown." -msgstr "" - -#: fas/user.py:521 -msgid "Fedora Project Password Reset" -msgstr "Fedora-Projekt Passwort-Rücksetzung" - -#: fas/user.py:522 -#, python-format -msgid "" -"\n" -"Somebody (hopefully you) has requested a password reset for your account!\n" -"To change your password (or to cancel the request), please visit\n" -"https://admin.fedoraproject.org/accounts/user/verifypass/%(user)s/%(token)s\n" -msgstr "" - -#: fas/user.py:534 -msgid "" -"This user does not have a GPG Key ID set, so an encrypted email cannot be " -"sent." -msgstr "" - -#: fas/user.py:538 -msgid "Your key could not be retrieved from subkeys.pgp.net" -msgstr "" - -#: fas/user.py:560 -msgid "Your password reset email could not be encrypted." -msgstr "" - -#: fas/user.py:566 -msgid "A password reset URL has been emailed to you." -msgstr "" - -#: fas/user.py:576 fas/user.py:592 fas/user.py:612 -msgid "You do not have any pending password changes." -msgstr "" - -#: fas/user.py:581 fas/user.py:596 fas/user.py:617 -#, fuzzy -msgid "Invalid password change token." -msgstr "Das Passwort des Tokens »%s« ändern." - -#: fas/user.py:601 -msgid "" -"Your password reset has been canceled. The password change token has been " -"invalidated." -msgstr "" - -#: fas/user.py:626 -msgid "" -"You have successfully reset your password. You should now be able to login " -"below." -msgstr "" - -#: fas/user.py:663 -msgid "Before generating a certificate, you must first complete the CLA." -msgstr "" - -#: fas/templates/about.html:7 -msgid "About FAS" -msgstr "Über FAS" - -#: fas/templates/about.html:10 -msgid "FAS - The Open Account System" -msgstr "FAS - Das Open Account System" - -#: fas/templates/about.html:11 -msgid "" -"FAS is designed around an open architecture. Unlike the traditional account " -"systems where a single admin or group of admins decide who gets to be in " -"what group, FAS is completely designed to be self operating per team. Every " -"group is given at least one administrator who can then approve other people " -"in the group. Also, unlike traditional account systems. FAS allows people " -"to apply for the groups they want to be in. This paridigm is interesting as " -"it allows anyone to find out who is in what groups and contact them. This " -"openness is brought over from the same philosophies that make Open Source " -"popular." -msgstr "" - -#: fas/templates/about.html:12 -msgid "Etiquette" -msgstr "Etiquette" - -#: fas/templates/about.html:13 -msgid "" -"People shouldn't assume that by applying for a group that they're then in " -"that group. Consider it like applying for another job. It often takes " -"time. For best odds of success, learn about the group you're applying for " -"and get to know someone in the group. Find someone with sponsor or admin " -"access and ask them if they'd have time to mentor you. Plan on spending at " -"least a few days learning about the group, doing a mundain task, " -"participating on the mailing list. Sometimes this process can take weeks " -"depending on the group. It's best to know you will get sponsored before you " -"apply." -msgstr "" - -#: fas/templates/about.html:14 -#, fuzzy -msgid "Users, Sponsors, Administrators" -msgstr "Wenden Sie sich an die Netzwerkadministatoren" - -#: fas/templates/about.html:15 -msgid "" -"Once you're in the group, you're in the group. Sponsorship and " -"Administrators typically have special access in the group in questions. " -"Some groups consider sponsorship level to be of a higher involvement, " -"partial ownership of the group for example. But as far as the account " -"system goes the disctinction is easy. Sponsors can approve new users and " -"make people into sponsors. They cannot, however, downgrade or remove other " -"sponsors. They also cannot change administrators in any way. " -"Administrators can do anything to anyone in the group." -msgstr "" - -#: fas/templates/error.html:7 fas/templates/home.html:7 -#: fas/templates/cla/index.html:7 fas/templates/openid/about.html:7 -#: fas/templates/openid/id.html:7 fas/templates/openid/trusted.html:7 -msgid "Fedora Accounts System" -msgstr "Fedora-Kontosystem" - -#: fas/templates/error.html:17 -msgid "Error!" -msgstr "Fehler!" - -#: fas/templates/error.html:18 -msgid "The following error(s) have occured with your request:" -msgstr "" - -#: fas/templates/home.html:11 -msgid "Todo queue:" -msgstr "" - -#: fas/templates/home.html:17 -#, python-format -msgid "" -"%(user)s requests approval to join %(group)s." -msgstr "" - -#: fas/templates/home.html:24 -#, python-format -msgid "" -"CLA not completed. To become a full Fedora Contributor please complete the CLA." -msgstr "" - -#: fas/templates/home.html:25 -#, python-format -msgid "" -"You have not submitted an SSH key, some Fedora resources require an SSH " -"key. Please submit yours by editing My Account" -msgstr "" - -#: fas/templates/home.html:29 -msgid "Download a client-side certificate" -msgstr "" - -#: fas/templates/login.html:7 -msgid "Login to the Fedora Accounts System" -msgstr "Anmelden am Fedora-Konto-System" - -#: fas/templates/login.html:17 fas/templates/login.html:23 -msgid "Login" -msgstr "Anmelden" - -#: fas/templates/login.html:20 -msgid "User Name:" -msgstr "Benutzername:" - -#: fas/templates/login.html:21 fas/templates/user/view.html:37 -msgid "Password:" -msgstr "Passwort:" - -#: fas/templates/login.html:29 -msgid "Forgot Password?" -msgstr "Passwort vergessen?" - -#: fas/templates/login.html:30 -#, fuzzy -msgid "Sign Up" -msgstr "Nach oben verschieben" - -#: fas/templates/master.html:22 -msgid "Fedora" -msgstr "Fedora" - -#: fas/templates/master.html:35 -#, fuzzy -msgid "Learn about Fedora" -msgstr "Mehr über Fedora erfahren" - -#: fas/templates/master.html:36 -msgid "Download Fedora" -msgstr "Fedora herunterladen" - -#: fas/templates/master.html:37 -msgid "Projects" -msgstr "Projekte" - -#: fas/templates/master.html:38 -#, fuzzy -msgid "Join Fedora" -msgstr "Mithelfen" - -#: fas/templates/master.html:39 -msgid "Communicate" -msgstr "Kommunikation" - -#: fas/templates/master.html:40 -msgid "Help/Documentation" -msgstr "Hilfe/Dokumentation" - -#: fas/templates/master.html:46 -msgid "Logged in:" -msgstr "Angemeldet:" - -#: fas/templates/master.html:52 -msgid "My Account" -msgstr "Mein Konto" - -#: fas/templates/master.html:53 fas/templates/master.html:94 -msgid "Log Out" -msgstr "Abmelden" - -#: fas/templates/master.html:54 fas/templates/welcome.html:21 -msgid "Log In" -msgstr "Anmelden" - -#: fas/templates/master.html:61 -msgid "Home" -msgstr "Home" - -#: fas/templates/master.html:64 -msgid "New Group" -msgstr "Neue Gruppe" - -#: fas/templates/master.html:65 -msgid "User List" -msgstr "Benutzerliste" - -#: fas/templates/master.html:67 -msgid "Group List" -msgstr "Gruppenliste" - -#: fas/templates/master.html:68 -#, fuzzy -msgid "Apply For a new Group" -msgstr "" -"Neue Datenträger-\n" -"gruppe erzeugen" - -#: fas/templates/master.html:69 -msgid "News" -msgstr "Neues" - -#: fas/templates/master.html:74 -#, fuzzy -msgid "Locale:" -msgstr "Gebietsschema" - -#: fas/templates/master.html:78 -msgid "OK" -msgstr "" - -#: fas/templates/master.html:90 -msgid "About" -msgstr "Über" - -#: fas/templates/master.html:91 -#, fuzzy -msgid "Contact Us" -msgstr "Sie erreichen uns unter:" - -#: fas/templates/master.html:92 -#, fuzzy -msgid "Legal & Privacy" -msgstr "Datenschutzerklärung" - -#: fas/templates/master.html:97 -msgid "" -"Copyright © 2007 Red Hat, Inc. and others. All Rights Reserved. Please " -"send any comments or corrections to the websites team." -msgstr "" -"Copyright © 2007 Red Hat, Inc. und andere. Alle Rechte vorbehalten. Bitte " -"senden Sie alle Kommentare oder Korrekturen an das Webseiten-Team." - -#: fas/templates/master.html:100 -#, fuzzy -msgid "" -"The Fedora Project is maintained and driven by the community and sponsored " -"by Red Hat. This is a community maintained site. Red Hat is not " -"responsible for content." -msgstr "" -"Das Fedora Project wird durch die Community verwaltet und von Red Hat " -"unterstützt. Diese Seite wird von der Community gepflegt, Red Hat ist für " -"den Inhalt nicht verantwortlich." - -#: fas/templates/welcome.html:7 -msgid "Welcome to FAS2" -msgstr "Willkommen zu FAS2" - -#: fas/templates/welcome.html:18 -msgid "" -"Welcome to the Fedora Accounts System 2. Please submit bugs to https://fedorahosted.org/fas2/ or stop " -"by #fedora-admin on irc.freenode.net." -msgstr "" - -#: fas/templates/welcome.html:22 -msgid "New Account" -msgstr "Neues Konto" - -#: fas/templates/welcome.html:23 -#, fuzzy -msgid "Why Join?" -msgstr "Fedora unterstützen" - -#: fas/templates/cla/index.html:10 -msgid "Fedora Contributor License Agreement" -msgstr "Fedora Contributor License Agreement" - -#: fas/templates/cla/index.html:11 fas/templates/cla/index.html:13 -#, fuzzy, python-format -msgid "Text Version" -msgstr "Die URL lautet %s, der alternative Text lautet %s" - -#: fas/templates/cla/index.html:15 -#, fuzzy -msgid "You have already sucessfully complete the CLA." -msgstr "Es existiert bereits ein Profil namens »%s«" - -#: fas/templates/cla/index.html:20 -msgid "I agree" -msgstr "I agree" - -#: fas/templates/cla/index.html:21 -msgid "I do not agree" -msgstr "I do not agree" - -#: fas/templates/group/edit.html:7 -msgid "Edit Group" -msgstr "Gruppe bearbeiten" - -#: fas/templates/group/edit.html:10 -#, python-format -msgid "Edit Group: %s" -msgstr "Gruppebearbeiten: %s" - -#: fas/templates/group/edit.html:13 fas/templates/group/new.html:18 -msgid "Display Name:" -msgstr "Angezeigter Name:" - -#: fas/templates/group/edit.html:18 fas/templates/group/new.html:28 -msgid "Group Type:" -msgstr "Gruppentyp:" - -#: fas/templates/group/edit.html:23 fas/templates/group/new.html:23 -msgid "Group Owner:" -msgstr "Gruppe-Eigentümer:" - -#: fas/templates/group/edit.html:28 fas/templates/group/new.html:33 -#: fas/templates/group/view.html:41 -msgid "Needs Sponsor:" -msgstr "Benötigt Spnsor:" - -#: fas/templates/group/edit.html:34 fas/templates/group/new.html:38 -#: fas/templates/group/view.html:45 -#, fuzzy -msgid "Self Removal:" -msgstr "Entfernung von Benachrichtigungen" - -#: fas/templates/group/edit.html:40 -#, fuzzy -msgid "Group Prerequisite:" -msgstr "_Gruppe hinzufügen" - -#: fas/templates/group/edit.html:46 -#, fuzzy -msgid "Group Join Message:" -msgstr "Private Gruppe betreten" - -#: fas/templates/group/edit.html:51 fas/templates/user/edit.html:80 -msgid "Save!" -msgstr "Speichern!" - -#: fas/templates/group/invite.html:7 fas/templates/group/invite.html:10 -msgid "Invite a new community member!" -msgstr "" - -#: fas/templates/group/invite.html:14 -#, fuzzy -msgid "To email:" -msgstr "Nachricht an die Liste senden" - -#: fas/templates/group/invite.html:15 -msgid "From:" -msgstr "Von:" - -#: fas/templates/group/invite.html:16 -msgid "Subject:" -msgstr "Betreff:" - -#: fas/templates/group/invite.html:17 -msgid "Message:" -msgstr "Nachricht:" - -#: fas/templates/group/invite.html:39 -#, fuzzy -msgid "Send!" -msgstr "Senden" - -#: fas/templates/group/list.html:7 -#, fuzzy -msgid "Groups List" -msgstr "Gruppen anzeigen nach Muster (z.B. de*tv*): " - -#: fas/templates/group/list.html:19 fas/templates/user/list.html:11 -#, python-format -msgid "List (%s)" -msgstr "Liste (%s)" - -#: fas/templates/group/list.html:20 -msgid "Search Groups" -msgstr "Suche Gruppen" - -#: fas/templates/group/list.html:22 -msgid "\"*\" is a wildcard (Ex: \"cvs*\")" -msgstr "\"*\" ist eine Wildcard (ex: \"cvs*\")" - -#: fas/templates/group/list.html:25 -msgid "Search" -msgstr "Suche" - -#: fas/templates/group/list.html:28 fas/templates/user/list.html:19 -msgid "Results" -msgstr "Ergebnisse" - -#: fas/templates/group/list.html:31 fas/templates/user/list.html:22 -msgid "All" -msgstr "Alle" - -#: fas/templates/group/list.html:36 -msgid "Group" -msgstr "Gruppe" - -#: fas/templates/group/list.html:36 -msgid "Description" -msgstr "Beschreibung" - -#: fas/templates/group/list.html:36 -msgid "Status" -msgstr "Status" - -#: fas/templates/group/list.html:44 fas/templates/group/view.html:22 -#: fas/templates/user/view.html:70 -msgid "Approved" -msgstr "" - -#: fas/templates/group/list.html:45 fas/templates/group/view.html:23 -msgid "Unapproved" -msgstr "" - -#: fas/templates/group/new.html:7 fas/templates/group/new.html:10 -msgid "Create a new FAS Group" -msgstr "Erzeuge neue FAS-Gruppe" - -#: fas/templates/group/new.html:13 -msgid "Group Name:" -msgstr "Gruppenname:" - -#: fas/templates/group/new.html:43 -#, fuzzy -msgid "Must Belong To:" -msgstr "Objekt(e) gehören in folgende _Kategorien:" - -#: fas/templates/group/new.html:48 fas/templates/group/view.html:49 -#, fuzzy -msgid "Join Message:" -msgstr "Nachrichtentransportsystem D-BUS" - -#: fas/templates/group/new.html:53 -msgid "Create!" -msgstr "Erstellen!" - -#: fas/templates/group/view.html:7 fas/templates/user/view.html:77 -msgid "View Group" -msgstr "Gruppen ansehen" - -#: fas/templates/group/view.html:21 -msgid "My Status:" -msgstr "Mein Status:" - -#: fas/templates/group/view.html:24 -msgid "Not a Member" -msgstr "IKein Mitglied" - -#: fas/templates/group/view.html:32 -msgid "Remove me" -msgstr "Entferne mich" - -#: fas/templates/group/view.html:34 fas/templates/user/view.html:17 -msgid "(edit)" -msgstr "(bearbeiten)" - -#: fas/templates/group/view.html:37 fas/templates/openid/id.html:16 -msgid "Name:" -msgstr "Name:" - -#: fas/templates/group/view.html:38 -msgid "Description:" -msgstr "Beschreibung:" - -#: fas/templates/group/view.html:39 -msgid "Owner:" -msgstr "Besitzer:" - -#: fas/templates/group/view.html:40 -msgid "Type:" -msgstr "Typ:" - -#: fas/templates/group/view.html:42 fas/templates/group/view.html:46 -msgid "Yes" -msgstr "Ja" - -#: fas/templates/group/view.html:43 fas/templates/group/view.html:47 -msgid "No" -msgstr "Nein" - -#: fas/templates/group/view.html:50 -#, fuzzy -msgid "Prerequisite:" -msgstr "# Vorgetäuschtes Ziel (Voraussetzung von .PHONY)." - -#: fas/templates/group/view.html:53 -msgid "Created:" -msgstr "Erzeugt:" - -#: fas/templates/group/view.html:55 -msgid "Add User:" -msgstr "Benutzer hinzufügen:" - -#: fas/templates/group/view.html:71 -msgid "Members" -msgstr "Mitglieder" - -#: fas/templates/group/view.html:75 fas/templates/user/list.html:27 -msgid "Username" -msgstr "Benutzername" - -#: fas/templates/group/view.html:77 -#, fuzzy -msgid "Date Added" -msgstr "Hinzufügedatum" - -#: fas/templates/group/view.html:78 -#, fuzzy -msgid "Date Approved" -msgstr "" -"\n" -"Erstellungsdatum: " - -#: fas/templates/group/view.html:79 -msgid "Approval" -msgstr "" - -#: fas/templates/group/view.html:80 -msgid "Role Type" -msgstr "Rollentyp" - -#: fas/templates/group/view.html:81 -msgid "Action" -msgstr "Aktion" - -#: fas/templates/group/view.html:87 fas/templates/group/view.html:90 -#: fas/templates/user/view.html:71 -msgid "None" -msgstr "Nichts" - -#: fas/templates/openid/about.html:10 fas/templates/openid/trusted.html:10 -#, fuzzy -msgid "Fedora Project OpenID Provider" -msgstr "Fedora-Übersetzungsprojekt Home" - -#: fas/templates/openid/id.html:11 -#, python-format -msgid "User %s" -msgstr "Benutzer %s" - -#: fas/templates/openid/id.html:14 fas/templates/user/new.html:13 -#: fas/templates/user/resetpass.html:13 -msgid "Username:" -msgstr "Benutzername:" - -#: fas/templates/openid/trusted.html:15 -#, python-format -msgid "Allow %s to authenticate to your OpenID identity?" -msgstr "" - -#: fas/templates/openid/trusted.html:16 -msgid "Submit" -msgstr "Abschicken" - -#: fas/templates/user/changepass.html:7 fas/templates/user/changepass.html:10 -#: fas/templates/user/changepass.html:16 fas/templates/user/verifypass.html:16 -msgid "Change Password" -msgstr "Passwort ändern" - -#: fas/templates/user/changepass.html:13 -msgid "Current Password:" -msgstr "Aktuelles Passwort:" - -#: fas/templates/user/changepass.html:14 fas/templates/user/verifypass.html:13 -msgid "New Password:" -msgstr "Neues Passwort:" - -#: fas/templates/user/changepass.html:15 fas/templates/user/verifypass.html:14 -msgid "Confirm Password:" -msgstr "Bestätige Passwort:" - -#: fas/templates/user/edit.html:7 -msgid "Edit Account" -msgstr "Konto bearbeiten" - -#: fas/templates/user/edit.html:10 -#, python-format -msgid "Edit Account (%s)" -msgstr "Konto bearbeiten (%s)" - -#: fas/templates/user/edit.html:13 -msgid "Human Name" -msgstr "Wahrer Name" - -#: fas/templates/user/edit.html:19 -msgid "Email" -msgstr "E-Mail" - -#: fas/templates/user/edit.html:22 fas/templates/user/view.html:24 -#, python-format -msgid "(pending change to %(email)s - cancel)" -msgstr "" - -#: fas/templates/user/edit.html:31 -msgid "Telephone Number" -msgstr "Telefonnummer" - -#: fas/templates/user/edit.html:41 -msgid "IRC Nick" -msgstr "IRC-Nick" - -#: fas/templates/user/edit.html:46 -msgid "PGP Key" -msgstr "PGP-Schlüssel" - -#: fas/templates/user/edit.html:57 -msgid "Time Zone" -msgstr "Zeitzone" - -#: fas/templates/user/edit.html:75 -msgid "Comments" -msgstr "Kommentare" - -#: fas/templates/user/edit.html:81 fas/templates/user/verifyemail.html:17 -#: fas/templates/user/verifypass.html:17 -msgid "Cancel" -msgstr "Abbruch" - -#: fas/templates/user/list.html:7 -msgid "Users List" -msgstr "Benutzer-Liste" - -#: fas/templates/user/list.html:13 -msgid "\"*\" is a wildcard (Ex: \"ric*\")" -msgstr "\"*\" ist eine Wildcard (Ex: \"ric*\")" - -#: fas/templates/user/list.html:38 fas/templates/user/view.html:45 -#, fuzzy -msgid "CLA Done" -msgstr "Test %d abgeschlossen" - -#: fas/templates/user/list.html:39 fas/templates/user/view.html:46 -#, fuzzy -msgid "Not Done" -msgstr "Test %d abgeschlossen" - -#: fas/templates/user/new.html:7 fas/templates/user/new.html:10 -msgid "Sign up for a Fedora account" -msgstr "" - -#: fas/templates/user/new.html:17 -msgid "Full Name:" -msgstr "Vollständiger Name:" - -#: fas/templates/user/new.html:21 fas/templates/user/resetpass.html:14 -#: fas/templates/user/view.html:22 -msgid "Email:" -msgstr "E-Mail:" - -#: fas/templates/user/new.html:38 -#, fuzzy -msgid "Sign up!" -msgstr "Nach oben verschieben" - -#: fas/templates/user/resetpass.html:7 fas/templates/user/resetpass.html:10 -#: fas/templates/user/resetpass.html:16 fas/templates/user/verifypass.html:7 -#: fas/templates/user/verifypass.html:10 -msgid "Reset Password" -msgstr "Passw_ort zurücksetzen" - -#: fas/templates/user/resetpass.html:15 -msgid "Encrypt/Sign password reset email" -msgstr "" - -#: fas/templates/user/verifyemail.html:7 -#: fas/templates/user/verifyemail.html:10 -#, fuzzy -msgid "Confirm Email Change Request" -msgstr "" -"Grössenänderung der physischen Einteilungseinheiten (Extent) bestätigen" - -#: fas/templates/user/verifyemail.html:14 -#, python-format -msgid "Do you really want to change your email to: %s?" -msgstr "" - -#: fas/templates/user/verifyemail.html:16 -msgid "Confirm" -msgstr "Bestätigen" - -#: fas/templates/user/view.html:7 -msgid "View Account" -msgstr "Konto ansehen" - -#: fas/templates/user/view.html:15 -msgid "Your Fedora Account" -msgstr "Ihr Fedora-Konto" - -#: fas/templates/user/view.html:16 -#, python-format -msgid "%s's Fedora Account" -msgstr "%s's Fedora-Konto" - -#: fas/templates/user/view.html:17 -msgid "Account Details" -msgstr "Konto-Details" - -#: fas/templates/user/view.html:20 -msgid "Account Name:" -msgstr "Konto-Bezeichnung:" - -#: fas/templates/user/view.html:21 -msgid "Real Name:" -msgstr "Wahrer Name:" - -#: fas/templates/user/view.html:27 -msgid "Telephone Number:" -msgstr "Telefonnummer:" - -#: fas/templates/user/view.html:28 -msgid "Postal Address:" -msgstr "Post-Adresse:" - -#: fas/templates/user/view.html:30 -msgid "IRC Nick:" -msgstr "IRC-Nick:" - -#: fas/templates/user/view.html:31 -msgid "PGP Key:" -msgstr "PGP-Schlüssel:" - -#: fas/templates/user/view.html:32 -msgid "Public SSH Key:" -msgstr "Öffentlicher SSH-Schlüssel:" - -#: fas/templates/user/view.html:36 -msgid "Comments:" -msgstr "Kommentare:" - -#: fas/templates/user/view.html:37 -msgid "Valid" -msgstr "Gültig" - -#: fas/templates/user/view.html:38 -msgid "Account Status:" -msgstr "Konto-Status:" - -#: fas/templates/user/view.html:39 -msgid "Active" -msgstr "Aktiv" - -#: fas/templates/user/view.html:40 -msgid "Vacation" -msgstr "Urlaub" - -#: fas/templates/user/view.html:41 -msgid "Inactive" -msgstr "Inaktiv" - -#: fas/templates/user/view.html:42 -msgid "Pinged" -msgstr "" - -#: fas/templates/user/view.html:44 -msgid "CLA:" -msgstr "CLA:" - -#: fas/templates/user/view.html:46 -msgid "Complete it!" -msgstr "" - -#: fas/templates/user/view.html:50 -msgid "Your Roles" -msgstr "Ihre Rolle" - -#: fas/templates/user/view.html:51 -#, python-format -msgid "%s's Roles" -msgstr "%s's Rollen" - -#: fas/templates/user/view.html:68 -msgid "Status:" -msgstr "Status:" - -#: fas/templates/user/view.html:74 -msgid "Tools:" -msgstr "Werkzeuge:" - -#: fas/templates/user/view.html:78 -msgid "Invite a New Member..." -msgstr "Ein neues Mitglied einladen..." - -#: fas/templates/user/view.html:79 -#, fuzzy -msgid "Manage Group Membership..." -msgstr "Mitgliedschaft in der Gruppe cvsl10n" - -#: fas/templates/user/view.html:80 -#, fuzzy -msgid "Manage Group Details..." -msgstr "Details der Paketgruppen" - -#: fas/templates/user/view.html:84 -msgid "Queue:" -msgstr "Warteschlange:" - -#: fas/templates/user/view.html:88 -#, python-format -msgid "" -"%(user)s requests approval to join %" -"(unapproved_role.group)s." -msgstr "" - -#, fuzzy -#~ msgid "Sign the CLA" -#~ msgstr "Unterschreiben des CLA" - -#~ msgid "Welcome to the Fedora Accounts System 2. Please submit bugs to " -#~ msgstr "Willkommen beim Fedora-Konto-System 2. Bitte Fehler melden an" - -#, fuzzy -#~ msgid "Sign it!" -#~ msgstr "Wirklich unterschreiben? (j/N) " diff --git a/fas/po/fas.pot b/fas/po/fas.pot deleted file mode 100644 index 3a08ace..0000000 --- a/fas/po/fas.pot +++ /dev/null @@ -1,1627 +0,0 @@ -# Translations template for PROJECT. -# Copyright (C) 2008 ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2008. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2008-03-18 10:39-0400\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.1\n" - -#: fas/auth.py:149 fas/auth.py:157 -#, python-format -msgid "%s membership required before application to this group is allowed" -msgstr "" - -#: fas/cla.py:70 -msgid "You have already completed the CLA." -msgstr "" - -#: fas/cla.py:74 -msgid "You have not completed the CLA." -msgstr "" - -#: fas/cla.py:78 -msgid "" -"To complete the CLA, we must have your telephone number and postal " -"address. Please ensure they have been filled out." -msgstr "" - -#: fas/cla.py:91 -#, python-format -msgid "You could not be added to the '%s' group." -msgstr "" - -#: fas/cla.py:117 -#, python-format -msgid "You have successfully completed the CLA. You are now in the '%s' group." -msgstr "" - -#: fas/controllers.py:100 -#, python-format -msgid "Welcome, %s" -msgstr "" - -#: fas/controllers.py:115 -msgid "" -"The credentials you supplied were not correct or did not grant access to " -"this resource." -msgstr "" - -#: fas/controllers.py:118 -msgid "You must provide your credentials before accessing this resource." -msgstr "" - -#: fas/controllers.py:121 -msgid "Please log in." -msgstr "" - -#: fas/controllers.py:132 -msgid "You have successfully logged out." -msgstr "" - -#: fas/controllers.py:144 fas/user.py:93 -#, python-format -msgid "The language '%s' is not available." -msgstr "" - -#: fas/group.py:23 -#, python-format -msgid "The group '%s' does not exist." -msgstr "" - -#: fas/group.py:35 -#, python-format -msgid "The group '%s' already exists." -msgstr "" - -#: fas/group.py:131 fas/group.py:481 -#, python-format -msgid "You cannot view '%s'" -msgstr "" - -#: fas/group.py:145 fas/group.py:161 -msgid "Only FAS adminstrators can create groups." -msgstr "" - -#: fas/group.py:179 -#, python-format -msgid "The group: '%s' could not be created." -msgstr "" - -#: fas/group.py:189 -#, python-format -msgid "" -"The group: '%(group)s' has been created, but '%(user)s' could not be " -"added as a group administrator." -msgstr "" - -#: fas/group.py:191 -#, python-format -msgid "The group: '%s' has been created." -msgstr "" - -#: fas/group.py:206 fas/group.py:221 -#, python-format -msgid "You cannot edit '%s'." -msgstr "" - -#: fas/group.py:238 -msgid "The group details could not be saved." -msgstr "" - -#: fas/group.py:240 -msgid "The group details have been saved." -msgstr "" - -#: fas/group.py:266 -#, python-format -msgid "No Groups found matching '%s'" -msgstr "" - -#: fas/group.py:284 -#, python-format -msgid "%(user)s can not apply to %(group)s." -msgstr "" - -#: fas/group.py:292 -#, python-format -msgid "%(user)s could not apply to %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:303 -#, python-format -msgid "" -"\n" -"Fedora user %(user)s, aka %(name)s <%(email)s> has requested\n" -"membership for %(applicant)s (%(applicant_name)s) in the %(group)s group " -"and needs a sponsor.\n" -"\n" -"Please go to %(url)s to take action. \n" -msgstr "" - -#: fas/group.py:310 -#, python-format -msgid "%(user)s has applied to %(group)s!" -msgstr "" - -#: fas/group.py:327 -#, python-format -msgid "You cannot sponsor '%s'" -msgstr "" - -#: fas/group.py:334 -#, python-format -msgid "%(user)s could not be sponsored in %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:340 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has sponsored you for membership in the %(group)s\n" -"group of the Fedora account system. If applicable, this change should\n" -"propagate into the e-mail aliases and CVS repository within an hour.\n" -"\n" -"%(joinmsg)s\n" -msgstr "" - -#: fas/group.py:348 -#, python-format -msgid "'%s' has been sponsored!" -msgstr "" - -#: fas/group.py:365 -#, python-format -msgid "You cannot remove '%(user)s' from '%(group)s'." -msgstr "" - -#: fas/group.py:372 -#, python-format -msgid "%(user)s could not be removed from %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:377 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has removed you from the '%(group)s'\n" -"group of the Fedora Accounts System This change is effective\n" -"immediately for new operations, and should propagate into the e-mail\n" -"aliases within an hour.\n" -msgstr "" - -#: fas/group.py:384 -#, python-format -msgid "%(name)s has been removed from %(group)s" -msgstr "" - -#: fas/group.py:401 -#, python-format -msgid "You cannot upgrade '%s'" -msgstr "" - -#: fas/group.py:408 -#, python-format -msgid "%(name)s could not be upgraded in %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:417 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has upgraded you to %(status)s status in the\n" -"'%(group)s' group of the Fedora Accounts System This change is\n" -"effective immediately for new operations, and should propagate\n" -"into the e-mail aliases within an hour.\n" -msgstr "" - -#: fas/group.py:424 -#, python-format -msgid "%s has been upgraded!" -msgstr "" - -#: fas/group.py:440 -#, python-format -msgid "You cannot downgrade '%s'" -msgstr "" - -#: fas/group.py:447 -#, python-format -msgid "%(name)s could not be downgraded in %(group)s: %(error)s" -msgstr "" - -#: fas/group.py:455 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has downgraded you to %(status)s status in the\n" -"'%(group)s' group of the Fedora Accounts System This change is\n" -"effective immediately for new operations, and should propagate\n" -"into the e-mail aliases within an hour.\n" -msgstr "" - -#: fas/group.py:462 -#, python-format -msgid "%s has been downgraded!" -msgstr "" - -#: fas/group.py:509 -msgid "Come join The Fedora Project!" -msgstr "" - -#: fas/group.py:510 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has invited you to join the Fedora\n" -"Project! We are a community of users and developers who produce a\n" -"complete operating system from entirely free and open source software\n" -"(FOSS). %(name)s thinks that you have knowledge and skills\n" -"that make you a great fit for the Fedora community, and that you might\n" -"be interested in contributing.\n" -"\n" -"How could you team up with the Fedora community to use and develop your\n" -"skills? Check out http://fedoraproject.org/join-fedora for some ideas.\n" -"Our community is more than just software developers -- we also have a\n" -"place for you whether you're an artist, a web site builder, a writer, or\n" -"a people person. You'll grow and learn as you work on a team with other\n" -"very smart and talented people.\n" -"\n" -"Fedora and FOSS are changing the world -- come be a part of it!" -msgstr "" - -#: fas/group.py:527 -#, python-format -msgid "Message sent to: %s" -msgstr "" - -#: fas/group.py:530 -#, python-format -msgid "You are not in the '%s' group." -msgstr "" - -#: fas/help.py:9 fas/help.py:49 -msgid "Error" -msgstr "" - -#: fas/help.py:9 fas/help.py:49 -msgid "

We could not find that help item

" -msgstr "" - -#: fas/help.py:10 -msgid "IRC Nick (Optional)" -msgstr "" - -#: fas/help.py:10 -msgid "" -"

IRC Nick is used to identify yourself on irc.freenode.net. Please " -"register your nick on irc.freenode.net first, then fill this in so people" -" can find you online when they need to

" -msgstr "" - -#: fas/help.py:11 -msgid "Email (Required)" -msgstr "" - -#: fas/help.py:11 -msgid "" -"

This email address should be your prefered email contact and will be " -"used to send various official emails to. This is also where your " -"@fedoraproject.org email will get forwarded

" -msgstr "" - -#: fas/help.py:12 -msgid "Full Name (Required)" -msgstr "" - -#: fas/help.py:12 -msgid "

Your Human Name or \"real life\" name

" -msgstr "" - -#: fas/help.py:13 -msgid "GPG Key" -msgstr "" - -#: fas/help.py:13 -msgid "" -"

A GPG key is generally used to prove that a message or email came from" -" you or to encrypt information so that only the recipients can read it. " -"This can be used when a password reset is sent to your email.

" -msgstr "" - -#: fas/help.py:14 -msgid "Telephone" -msgstr "" - -#: fas/help.py:14 -msgid "" -"

Required in order to complete the CLA. " -"Sometimes during a time of emergency someone from the Fedora Project may " -"need to contact you. For more information see our Privacy " -"Policy

" -msgstr "" - -#: fas/help.py:15 fas/templates/user/edit.html:36 -msgid "Postal Address" -msgstr "" - -#: fas/help.py:15 -msgid "" -"

Required in order to complete the CLA. This " -"should be a mailing address where you can be contacted. See our Privacy " -"Policy about any concerns.

" -msgstr "" - -#: fas/help.py:16 -msgid "Timezone (Optional)" -msgstr "" - -#: fas/help.py:16 -msgid "

Please specify the time zone you are in.

" -msgstr "" - -#: fas/help.py:17 -msgid "Comments (Optional)" -msgstr "" - -#: fas/help.py:17 -msgid "

Misc comments about yourself.

" -msgstr "" - -#: fas/help.py:18 fas/templates/user/list.html:28 -msgid "Account Status" -msgstr "" - -#: fas/help.py:18 -msgid "" -"

Shows account status, possible values " -"include

  • Valid
  • Disabled
  • Expired

" -msgstr "" - -#: fas/help.py:19 -msgid "CLA" -msgstr "" - -#: fas/help.py:19 -msgid "" -"

In order to become a full Fedora contributor you must complete the Contributor " -"License Agreement. This license is a legal agreement between you and" -" Red Hat. Full status allows people to contribute content and code and " -"is recommended for anyone interested in getting involved in the Fedora " -"Project.

" -msgstr "" - -#: fas/help.py:20 fas/templates/user/edit.html:51 -msgid "Public SSH Key" -msgstr "" - -#: fas/help.py:20 -msgid "" -"

Many resources require public key authentiaction to work. By " -"uploading your public key to us, you can then log in to our servers. " -"Type \"man ssh-keygen\" for more information on creating your key. Once " -"created you will want to upload ~/.ssh/id_dsa.pub or " -"~/.ssh/id_rsa.pub

" -msgstr "" - -#: fas/help.py:21 fas/templates/user/edit.html:67 -msgid "Locale" -msgstr "" - -#: fas/help.py:21 -msgid "" -"

For non-english speaking peoples this allows individuals to select " -"which locale they are in.

" -msgstr "" - -#: fas/help.py:23 fas/templates/group/list.html:47 -msgid "Apply" -msgstr "" - -#: fas/help.py:23 -msgid "" -"

Applying for a group is like applying for a job and it can certainly " -"take a while to get in. Many groups have their own rules about how to " -"actually get approved or sponsored. For more information on how the " -"account system works see the about page.

" -msgstr "" - -#: fas/help.py:24 fas/templates/group/view.html:107 -msgid "Remove" -msgstr "" - -#: fas/help.py:24 -msgid "" -"

Removing a person from a group will cause that user to no longer be in" -" the group. They will need to re-apply to get in. Admins can remove " -"anyone, Sponsors can remove users, users can't remove anyone.

" -msgstr "" - -#: fas/help.py:25 fas/templates/group/view.html:111 -msgid "Upgrade" -msgstr "" - -#: fas/help.py:25 -msgid "" -"

Upgrade a persons status in this group.

  • from user -> to " -"sponsor
  • From sponsor -> administrator
  • administrators " -"cannot be upgraded beyond administrator

" -msgstr "" - -#: fas/help.py:26 fas/templates/group/view.html:115 -msgid "Downgrade" -msgstr "" - -#: fas/help.py:26 -msgid "" -"

Downgrade a persons status in the group.

  • from administrator -> " -"to sponsor
  • From sponsor -> user
  • users cannot be " -"downgraded below user, you may want to remove them

" -msgstr "" - -#: fas/help.py:27 fas/templates/group/view.html:102 -msgid "Approve" -msgstr "" - -#: fas/help.py:27 -msgid "" -"

A sponsor or administrator can approve users to be in a group. Once " -"the user has applied for the group, go to the group's page and click " -"approve to approve the user.

" -msgstr "" - -#: fas/help.py:28 fas/templates/group/view.html:76 -#: fas/templates/group/view.html:98 -msgid "Sponsor" -msgstr "" - -#: fas/help.py:28 -msgid "" -"

A sponsor or administrator can sponsor users to be in a gruop. Once " -"the user has applied for the group, go to the group's page and click " -"approve to sponsor the user. Sponsorship of a user implies that you are " -"approving a user and may mentor and answer their questions as they come " -"up.

" -msgstr "" - -#: fas/help.py:29 -msgid "Add User" -msgstr "" - -#: fas/help.py:29 -msgid "" -"

Manually add a user to a group. Place their username in this field " -"and click 'Add'

" -msgstr "" - -#: fas/help.py:30 -msgid "Group Name" -msgstr "" - -#: fas/help.py:30 -msgid "" -"

The name of the group you'd like to create. It should be alphanumeric" -" though '-'s are allowed

" -msgstr "" - -#: fas/help.py:31 -msgid "Display Name" -msgstr "" - -#: fas/help.py:31 -msgid "

More human readable name of the group

" -msgstr "" - -#: fas/help.py:32 -msgid "Group Owner" -msgstr "" - -#: fas/help.py:32 -msgid "

The name of the owner who will run this group

" -msgstr "" - -#: fas/help.py:33 -msgid "Group Type" -msgstr "" - -#: fas/help.py:33 -msgid "" -"

Normally it is safe to leave this blank. Though some values include " -"'tracking', 'shell', 'cvs', 'git', 'hg', 'svn', and 'mtn'. This value " -"only really matters if the group is to end up getting shell access or " -"commit access somewhere like fedorahosted.

" -msgstr "" - -#: fas/help.py:34 -msgid "Needs Sponsor" -msgstr "" - -#: fas/help.py:34 -msgid "" -"

If your group requires sponsorship (recommended), this means that when" -" a user is approved by a sponsor. That relationship is recorded in the " -"account system. If user A sponsors user N, then in viewing the members " -"of this group, people will know to contact user A about user N if " -"something goes wrong. If this box is unchecked, this means that only " -"approval is needed and no relationship is recorded about who did the " -"approving

" -msgstr "" - -#: fas/help.py:35 -msgid "Self Removal" -msgstr "" - -#: fas/help.py:35 -msgid "" -"

Should users be able to remove themselves from this group without " -"sponsor / admin intervention? (recommended yes)

" -msgstr "" - -#: fas/help.py:36 -msgid "Must Belong To" -msgstr "" - -#: fas/help.py:36 -msgid "" -"

Before a user can join this group, they must belong to the group " -"listed in this box. This value cannot be removed without " -"administrative intervention, only changed. Recommended values are " -"for the 'cla_done' group.

" -msgstr "" - -#: fas/help.py:37 -msgid "Join Message" -msgstr "" - -#: fas/help.py:37 -msgid "" -"

This message will go out to users when they join the group. It should" -" be informative and offer tips about what to do next. A description of " -"the group would also be valuable here

" -msgstr "" - -#: fas/help.py:38 -msgid "Client Side Cert" -msgstr "" - -#: fas/help.py:38 -msgid "" -"

The client side cert is generally used to grant access to upload " -"packages to Fedora or for other authentication purposes like with koji. " -"If you are not a package maintainer there is no need to worry about the " -"client side cert

" -msgstr "" - -#: fas/model.py:133 -msgid "user is already in this group" -msgstr "" - -#: fas/model.py:146 fas/model.py:161 fas/model.py:215 -msgid "user is not a member" -msgstr "" - -#: fas/model.py:150 -msgid "administrators cannot be upgraded any further" -msgstr "" - -#: fas/model.py:165 -msgid "users cannot be downgraded any further" -msgstr "" - -#: fas/model.py:174 -msgid "user is not an unapproved member" -msgstr "" - -#: fas/openid_fas.py:55 -msgid "The OpenID request could not be decoded." -msgstr "" - -#: fas/safasprovider.py:126 -#, python-format -msgid "Loading: %(visitmod)s" -msgstr "" - -#: fas/user.py:38 -#, python-format -msgid "'%s' does not exist." -msgstr "" - -#: fas/user.py:50 -#, python-format -msgid "Error: Could not create - '%s'" -msgstr "" - -#: fas/user.py:52 -#, python-format -msgid "'%s' already exists." -msgstr "" - -#: fas/user.py:60 -msgid "To prevent email loops, your email address cannot be @fedoraproject.org." -msgstr "" - -#: fas/user.py:76 -#, python-format -msgid "Error - Not a valid ssh key: %s" -msgstr "" - -#: fas/user.py:85 -#, python-format -msgid "'%s' is an illegal username." -msgstr "" - -#: fas/user.py:245 -#, python-format -msgid "You cannot edit %s" -msgstr "" - -#: fas/user.py:263 -#, python-format -msgid "You do not have permission to edit '%s'" -msgstr "" - -#: fas/user.py:272 -#, python-format -msgid "Email Change Requested for %s" -msgstr "" - -#: fas/user.py:274 -#, python-format -msgid "" -"\n" -"You have recently requested to change your Fedora Account System email\n" -"to this address. To complete the email change, you must confirm your\n" -"ownership of this email by visiting the following URL (you will need to\n" -"login with your Fedora account first):\n" -"\n" -"https://admin.fedoraproject.org/accounts/user/verifyemail/%s\n" -msgstr "" - -#: fas/user.py:282 -msgid "" -" Before your new email takes effect, you must confirm it. You should " -"receive an email with instructions shortly." -msgstr "" - -#: fas/user.py:294 -#, python-format -msgid "Your account details could not be saved: %s" -msgstr "" - -#: fas/user.py:296 -msgid "Your account details have been saved." -msgstr "" - -#: fas/user.py:322 -#, python-format -msgid "No users found matching '%s'" -msgstr "" - -#: fas/user.py:344 -msgid "" -"Your pending email change has been canceled. The email change token has " -"been invalidated." -msgstr "" - -#: fas/user.py:348 fas/user.py:364 -msgid "You do not have any pending email changes." -msgstr "" - -#: fas/user.py:352 fas/user.py:368 -msgid "Invalid email change token." -msgstr "" - -#: fas/user.py:377 -#, python-format -msgid "You have successfully changed your email to '%s'" -msgstr "" - -#: fas/user.py:385 -msgid "No need to sign up, you have an account!" -msgstr "" - -#: fas/user.py:407 -msgid "Welcome to the Fedora Project!" -msgstr "" - -#: fas/user.py:408 -#, python-format -msgid "" -"\n" -"You have created a new Fedora account!\n" -"Your new password is: %s\n" -"\n" -"Please go to https://admin.fedoraproject.org/accounts/ to change it.\n" -"\n" -"Welcome to the Fedora Project. Now that you've signed up for an\n" -"account you're probably desperate to start contributing, and with that\n" -"in mind we hope this e-mail might guide you in the right direction to\n" -"make this process as easy as possible.\n" -"\n" -"Fedora is an exciting project with lots going on, and you can\n" -"contribute in a huge number of ways, using all sorts of different\n" -"skill sets. To find out about the different ways you can contribute to\n" -"Fedora, you can visit our join page which provides more information\n" -"about all the different roles we have available.\n" -"\n" -"http://fedoraproject.org/en/join-fedora\n" -"\n" -"If you already know how you want to contribute to Fedora, and have\n" -"found the group already working in the area you're interested in, then\n" -"there are a few more steps for you to get going.\n" -"\n" -"Foremost amongst these is to sign up for the team or project's mailing\n" -"list that you're interested in - and if you're interested in more than\n" -"one group's work, feel free to sign up for as many mailing lists as\n" -"you like! This is because mailing lists are where the majority of work\n" -"gets organised and tasks assigned, so to stay in the loop be sure to\n" -"keep up with the messages.\n" -"\n" -"Once this is done, it's probably wise to send a short introduction to\n" -"the list letting them know what experience you have and how you'd like\n" -"to help. From here, existing members of the team will help you to find\n" -"your feet as a Fedora contributor.\n" -"\n" -"And finally, from all of us here at the Fedora Project, we're looking\n" -"forward to working with you!\n" -msgstr "" - -#: fas/user.py:449 -msgid "An account has already been registered with that email address." -msgstr "" - -#: fas/user.py:453 -msgid "" -"Your password has been emailed to you. Please log in with it and change " -"your password" -msgstr "" - -#: fas/user.py:487 -msgid "Your password could not be changed." -msgstr "" - -#: fas/user.py:490 -msgid "Your password has been changed." -msgstr "" - -#: fas/user.py:498 -msgid "You are already logged in!" -msgstr "" - -#: fas/user.py:509 -msgid "You are already logged in." -msgstr "" - -#: fas/user.py:515 -msgid "Username email combo does not exist!" -msgstr "" - -#: fas/user.py:518 -msgid "username + email combo unknown." -msgstr "" - -#: fas/user.py:521 -msgid "Fedora Project Password Reset" -msgstr "" - -#: fas/user.py:522 -#, python-format -msgid "" -"\n" -"Somebody (hopefully you) has requested a password reset for your account!" -"\n" -"To change your password (or to cancel the request), please visit\n" -"https://admin.fedoraproject.org/accounts/user/verifypass/%(user)s/%(token)s" -"\n" -msgstr "" - -#: fas/user.py:534 -msgid "" -"This user does not have a GPG Key ID set, so an encrypted email cannot be" -" sent." -msgstr "" - -#: fas/user.py:538 -msgid "Your key could not be retrieved from subkeys.pgp.net" -msgstr "" - -#: fas/user.py:560 -msgid "Your password reset email could not be encrypted." -msgstr "" - -#: fas/user.py:566 -msgid "A password reset URL has been emailed to you." -msgstr "" - -#: fas/user.py:576 fas/user.py:592 fas/user.py:612 -msgid "You do not have any pending password changes." -msgstr "" - -#: fas/user.py:581 fas/user.py:596 fas/user.py:617 -msgid "Invalid password change token." -msgstr "" - -#: fas/user.py:601 -msgid "" -"Your password reset has been canceled. The password change token has " -"been invalidated." -msgstr "" - -#: fas/user.py:626 -msgid "" -"You have successfully reset your password. You should now be able to " -"login below." -msgstr "" - -#: fas/user.py:663 -msgid "Before generating a certificate, you must first complete the CLA." -msgstr "" - -#: fas/templates/about.html:7 -msgid "About FAS" -msgstr "" - -#: fas/templates/about.html:10 -msgid "FAS - The Open Account System" -msgstr "" - -#: fas/templates/about.html:11 -msgid "" -"FAS is designed around an open architecture. Unlike the traditional " -"account systems where a single admin or group of admins decide who gets " -"to be in what group, FAS is completely designed to be self operating per " -"team. Every group is given at least one administrator who can then " -"approve other people in the group. Also, unlike traditional account " -"systems. FAS allows people to apply for the groups they want to be in. " -"This paridigm is interesting as it allows anyone to find out who is in " -"what groups and contact them. This openness is brought over from the " -"same philosophies that make Open Source popular." -msgstr "" - -#: fas/templates/about.html:12 -msgid "Etiquette" -msgstr "" - -#: fas/templates/about.html:13 -msgid "" -"People shouldn't assume that by applying for a group that they're then in" -" that group. Consider it like applying for another job. It often takes " -"time. For best odds of success, learn about the group you're applying " -"for and get to know someone in the group. Find someone with sponsor or " -"admin access and ask them if they'd have time to mentor you. Plan on " -"spending at least a few days learning about the group, doing a mundain " -"task, participating on the mailing list. Sometimes this process can take" -" weeks depending on the group. It's best to know you will get sponsored " -"before you apply." -msgstr "" - -#: fas/templates/about.html:14 -msgid "Users, Sponsors, Administrators" -msgstr "" - -#: fas/templates/about.html:15 -msgid "" -"Once you're in the group, you're in the group. Sponsorship and " -"Administrators typically have special access in the group in questions. " -"Some groups consider sponsorship level to be of a higher involvement, " -"partial ownership of the group for example. But as far as the account " -"system goes the disctinction is easy. Sponsors can approve new users and" -" make people into sponsors. They cannot, however, downgrade or remove " -"other sponsors. They also cannot change administrators in any way. " -"Administrators can do anything to anyone in the group." -msgstr "" - -#: fas/templates/error.html:7 fas/templates/home.html:7 -#: fas/templates/cla/index.html:7 fas/templates/openid/about.html:7 -#: fas/templates/openid/id.html:7 fas/templates/openid/trusted.html:7 -msgid "Fedora Accounts System" -msgstr "" - -#: fas/templates/error.html:17 -msgid "Error!" -msgstr "" - -#: fas/templates/error.html:18 -msgid "The following error(s) have occured with your request:" -msgstr "" - -#: fas/templates/home.html:11 -msgid "Todo queue:" -msgstr "" - -#: fas/templates/home.html:17 -#, python-format -msgid "" -"%(user)s requests approval to join %(group)s." -msgstr "" - -#: fas/templates/home.html:24 -#, python-format -msgid "" -"CLA not completed. To become a full Fedora Contributor please complete the CLA." -msgstr "" - -#: fas/templates/home.html:25 -#, python-format -msgid "" -"You have not submitted an SSH key, some Fedora resources require an SSH " -"key. Please submit yours by editing My Account" -msgstr "" - -#: fas/templates/home.html:29 -msgid "Download a client-side certificate" -msgstr "" - -#: fas/templates/login.html:7 -msgid "Login to the Fedora Accounts System" -msgstr "" - -#: fas/templates/login.html:17 fas/templates/login.html:23 -msgid "Login" -msgstr "" - -#: fas/templates/login.html:20 -msgid "User Name:" -msgstr "" - -#: fas/templates/login.html:21 fas/templates/user/view.html:37 -msgid "Password:" -msgstr "" - -#: fas/templates/login.html:29 -msgid "Forgot Password?" -msgstr "" - -#: fas/templates/login.html:30 -msgid "Sign Up" -msgstr "" - -#: fas/templates/master.html:22 -msgid "Fedora" -msgstr "" - -#: fas/templates/master.html:35 -msgid "Learn about Fedora" -msgstr "" - -#: fas/templates/master.html:36 -msgid "Download Fedora" -msgstr "" - -#: fas/templates/master.html:37 -msgid "Projects" -msgstr "" - -#: fas/templates/master.html:38 -msgid "Join Fedora" -msgstr "" - -#: fas/templates/master.html:39 -msgid "Communicate" -msgstr "" - -#: fas/templates/master.html:40 -msgid "Help/Documentation" -msgstr "" - -#: fas/templates/master.html:46 -msgid "Logged in:" -msgstr "" - -#: fas/templates/master.html:52 -msgid "My Account" -msgstr "" - -#: fas/templates/master.html:53 fas/templates/master.html:94 -msgid "Log Out" -msgstr "" - -#: fas/templates/master.html:54 fas/templates/welcome.html:21 -msgid "Log In" -msgstr "" - -#: fas/templates/master.html:61 -msgid "Home" -msgstr "" - -#: fas/templates/master.html:64 -msgid "New Group" -msgstr "" - -#: fas/templates/master.html:65 -msgid "User List" -msgstr "" - -#: fas/templates/master.html:67 -msgid "Group List" -msgstr "" - -#: fas/templates/master.html:68 -msgid "Apply For a new Group" -msgstr "" - -#: fas/templates/master.html:69 -msgid "News" -msgstr "" - -#: fas/templates/master.html:74 -msgid "Locale:" -msgstr "" - -#: fas/templates/master.html:78 -msgid "OK" -msgstr "" - -#: fas/templates/master.html:90 -msgid "About" -msgstr "" - -#: fas/templates/master.html:91 -msgid "Contact Us" -msgstr "" - -#: fas/templates/master.html:92 -msgid "Legal & Privacy" -msgstr "" - -#: fas/templates/master.html:97 -msgid "" -"Copyright © 2007 Red Hat, Inc. and others. All Rights Reserved. Please " -"send any comments or corrections to the websites team." -msgstr "" - -#: fas/templates/master.html:100 -msgid "" -"The Fedora Project is maintained and driven by the community and " -"sponsored by Red Hat. This is a community maintained site. Red Hat is " -"not responsible for content." -msgstr "" - -#: fas/templates/welcome.html:7 -msgid "Welcome to FAS2" -msgstr "" - -#: fas/templates/welcome.html:18 -msgid "" -"Welcome to the Fedora Accounts System 2. Please submit bugs to https://fedorahosted.org/fas2/" -" or stop by #fedora-admin on irc.freenode.net." -msgstr "" - -#: fas/templates/welcome.html:22 -msgid "New Account" -msgstr "" - -#: fas/templates/welcome.html:23 -msgid "Why Join?" -msgstr "" - -#: fas/templates/cla/index.html:10 -msgid "Fedora Contributor License Agreement" -msgstr "" - -#: fas/templates/cla/index.html:11 fas/templates/cla/index.html:13 -#, python-format -msgid "Text Version" -msgstr "" - -#: fas/templates/cla/index.html:15 -msgid "You have already sucessfully complete the CLA." -msgstr "" - -#: fas/templates/cla/index.html:20 -msgid "I agree" -msgstr "" - -#: fas/templates/cla/index.html:21 -msgid "I do not agree" -msgstr "" - -#: fas/templates/group/edit.html:7 -msgid "Edit Group" -msgstr "" - -#: fas/templates/group/edit.html:10 -#, python-format -msgid "Edit Group: %s" -msgstr "" - -#: fas/templates/group/edit.html:13 fas/templates/group/new.html:18 -msgid "Display Name:" -msgstr "" - -#: fas/templates/group/edit.html:18 fas/templates/group/new.html:28 -msgid "Group Type:" -msgstr "" - -#: fas/templates/group/edit.html:23 fas/templates/group/new.html:23 -msgid "Group Owner:" -msgstr "" - -#: fas/templates/group/edit.html:28 fas/templates/group/new.html:33 -#: fas/templates/group/view.html:41 -msgid "Needs Sponsor:" -msgstr "" - -#: fas/templates/group/edit.html:34 fas/templates/group/new.html:38 -#: fas/templates/group/view.html:45 -msgid "Self Removal:" -msgstr "" - -#: fas/templates/group/edit.html:40 -msgid "Group Prerequisite:" -msgstr "" - -#: fas/templates/group/edit.html:46 -msgid "Group Join Message:" -msgstr "" - -#: fas/templates/group/edit.html:51 fas/templates/user/edit.html:80 -msgid "Save!" -msgstr "" - -#: fas/templates/group/invite.html:7 fas/templates/group/invite.html:10 -msgid "Invite a new community member!" -msgstr "" - -#: fas/templates/group/invite.html:14 -msgid "To email:" -msgstr "" - -#: fas/templates/group/invite.html:15 -msgid "From:" -msgstr "" - -#: fas/templates/group/invite.html:16 -msgid "Subject:" -msgstr "" - -#: fas/templates/group/invite.html:17 -msgid "Message:" -msgstr "" - -#: fas/templates/group/invite.html:39 -msgid "Send!" -msgstr "" - -#: fas/templates/group/list.html:7 -msgid "Groups List" -msgstr "" - -#: fas/templates/group/list.html:19 fas/templates/user/list.html:11 -#, python-format -msgid "List (%s)" -msgstr "" - -#: fas/templates/group/list.html:20 -msgid "Search Groups" -msgstr "" - -#: fas/templates/group/list.html:22 -msgid "\"*\" is a wildcard (Ex: \"cvs*\")" -msgstr "" - -#: fas/templates/group/list.html:25 -msgid "Search" -msgstr "" - -#: fas/templates/group/list.html:28 fas/templates/user/list.html:19 -msgid "Results" -msgstr "" - -#: fas/templates/group/list.html:31 fas/templates/user/list.html:22 -msgid "All" -msgstr "" - -#: fas/templates/group/list.html:36 -msgid "Group" -msgstr "" - -#: fas/templates/group/list.html:36 -msgid "Description" -msgstr "" - -#: fas/templates/group/list.html:36 -msgid "Status" -msgstr "" - -#: fas/templates/group/list.html:44 fas/templates/group/view.html:22 -#: fas/templates/user/view.html:70 -msgid "Approved" -msgstr "" - -#: fas/templates/group/list.html:45 fas/templates/group/view.html:23 -msgid "Unapproved" -msgstr "" - -#: fas/templates/group/new.html:7 fas/templates/group/new.html:10 -msgid "Create a new FAS Group" -msgstr "" - -#: fas/templates/group/new.html:13 -msgid "Group Name:" -msgstr "" - -#: fas/templates/group/new.html:43 -msgid "Must Belong To:" -msgstr "" - -#: fas/templates/group/new.html:48 fas/templates/group/view.html:49 -msgid "Join Message:" -msgstr "" - -#: fas/templates/group/new.html:53 -msgid "Create!" -msgstr "" - -#: fas/templates/group/view.html:7 fas/templates/user/view.html:77 -msgid "View Group" -msgstr "" - -#: fas/templates/group/view.html:21 -msgid "My Status:" -msgstr "" - -#: fas/templates/group/view.html:24 -msgid "Not a Member" -msgstr "" - -#: fas/templates/group/view.html:32 -msgid "Remove me" -msgstr "" - -#: fas/templates/group/view.html:34 fas/templates/user/view.html:17 -msgid "(edit)" -msgstr "" - -#: fas/templates/group/view.html:37 fas/templates/openid/id.html:16 -msgid "Name:" -msgstr "" - -#: fas/templates/group/view.html:38 -msgid "Description:" -msgstr "" - -#: fas/templates/group/view.html:39 -msgid "Owner:" -msgstr "" - -#: fas/templates/group/view.html:40 -msgid "Type:" -msgstr "" - -#: fas/templates/group/view.html:42 fas/templates/group/view.html:46 -msgid "Yes" -msgstr "" - -#: fas/templates/group/view.html:43 fas/templates/group/view.html:47 -msgid "No" -msgstr "" - -#: fas/templates/group/view.html:50 -msgid "Prerequisite:" -msgstr "" - -#: fas/templates/group/view.html:53 -msgid "Created:" -msgstr "" - -#: fas/templates/group/view.html:55 -msgid "Add User:" -msgstr "" - -#: fas/templates/group/view.html:71 -msgid "Members" -msgstr "" - -#: fas/templates/group/view.html:75 fas/templates/user/list.html:27 -msgid "Username" -msgstr "" - -#: fas/templates/group/view.html:77 -msgid "Date Added" -msgstr "" - -#: fas/templates/group/view.html:78 -msgid "Date Approved" -msgstr "" - -#: fas/templates/group/view.html:79 -msgid "Approval" -msgstr "" - -#: fas/templates/group/view.html:80 -msgid "Role Type" -msgstr "" - -#: fas/templates/group/view.html:81 -msgid "Action" -msgstr "" - -#: fas/templates/group/view.html:87 fas/templates/group/view.html:90 -#: fas/templates/user/view.html:71 -msgid "None" -msgstr "" - -#: fas/templates/openid/about.html:10 fas/templates/openid/trusted.html:10 -msgid "Fedora Project OpenID Provider" -msgstr "" - -#: fas/templates/openid/id.html:11 -#, python-format -msgid "User %s" -msgstr "" - -#: fas/templates/openid/id.html:14 fas/templates/user/new.html:13 -#: fas/templates/user/resetpass.html:13 -msgid "Username:" -msgstr "" - -#: fas/templates/openid/trusted.html:15 -#, python-format -msgid "Allow %s to authenticate to your OpenID identity?" -msgstr "" - -#: fas/templates/openid/trusted.html:16 -msgid "Submit" -msgstr "" - -#: fas/templates/user/changepass.html:7 fas/templates/user/changepass.html:10 -#: fas/templates/user/changepass.html:16 fas/templates/user/verifypass.html:16 -msgid "Change Password" -msgstr "" - -#: fas/templates/user/changepass.html:13 -msgid "Current Password:" -msgstr "" - -#: fas/templates/user/changepass.html:14 fas/templates/user/verifypass.html:13 -msgid "New Password:" -msgstr "" - -#: fas/templates/user/changepass.html:15 fas/templates/user/verifypass.html:14 -msgid "Confirm Password:" -msgstr "" - -#: fas/templates/user/edit.html:7 -msgid "Edit Account" -msgstr "" - -#: fas/templates/user/edit.html:10 -#, python-format -msgid "Edit Account (%s)" -msgstr "" - -#: fas/templates/user/edit.html:13 -msgid "Human Name" -msgstr "" - -#: fas/templates/user/edit.html:19 -msgid "Email" -msgstr "" - -#: fas/templates/user/edit.html:22 fas/templates/user/view.html:24 -#, python-format -msgid "(pending change to %(email)s - cancel)" -msgstr "" - -#: fas/templates/user/edit.html:31 -msgid "Telephone Number" -msgstr "" - -#: fas/templates/user/edit.html:41 -msgid "IRC Nick" -msgstr "" - -#: fas/templates/user/edit.html:46 -msgid "PGP Key" -msgstr "" - -#: fas/templates/user/edit.html:57 -msgid "Time Zone" -msgstr "" - -#: fas/templates/user/edit.html:75 -msgid "Comments" -msgstr "" - -#: fas/templates/user/edit.html:81 fas/templates/user/verifyemail.html:17 -#: fas/templates/user/verifypass.html:17 -msgid "Cancel" -msgstr "" - -#: fas/templates/user/list.html:7 -msgid "Users List" -msgstr "" - -#: fas/templates/user/list.html:13 -msgid "\"*\" is a wildcard (Ex: \"ric*\")" -msgstr "" - -#: fas/templates/user/list.html:38 fas/templates/user/view.html:45 -msgid "CLA Done" -msgstr "" - -#: fas/templates/user/list.html:39 fas/templates/user/view.html:46 -msgid "Not Done" -msgstr "" - -#: fas/templates/user/new.html:7 fas/templates/user/new.html:10 -msgid "Sign up for a Fedora account" -msgstr "" - -#: fas/templates/user/new.html:17 -msgid "Full Name:" -msgstr "" - -#: fas/templates/user/new.html:21 fas/templates/user/resetpass.html:14 -#: fas/templates/user/view.html:22 -msgid "Email:" -msgstr "" - -#: fas/templates/user/new.html:38 -msgid "Sign up!" -msgstr "" - -#: fas/templates/user/resetpass.html:7 fas/templates/user/resetpass.html:10 -#: fas/templates/user/resetpass.html:16 fas/templates/user/verifypass.html:7 -#: fas/templates/user/verifypass.html:10 -msgid "Reset Password" -msgstr "" - -#: fas/templates/user/resetpass.html:15 -msgid "Encrypt/Sign password reset email" -msgstr "" - -#: fas/templates/user/verifyemail.html:7 fas/templates/user/verifyemail.html:10 -msgid "Confirm Email Change Request" -msgstr "" - -#: fas/templates/user/verifyemail.html:14 -#, python-format -msgid "Do you really want to change your email to: %s?" -msgstr "" - -#: fas/templates/user/verifyemail.html:16 -msgid "Confirm" -msgstr "" - -#: fas/templates/user/view.html:7 -msgid "View Account" -msgstr "" - -#: fas/templates/user/view.html:15 -msgid "Your Fedora Account" -msgstr "" - -#: fas/templates/user/view.html:16 -#, python-format -msgid "%s's Fedora Account" -msgstr "" - -#: fas/templates/user/view.html:17 -msgid "Account Details" -msgstr "" - -#: fas/templates/user/view.html:20 -msgid "Account Name:" -msgstr "" - -#: fas/templates/user/view.html:21 -msgid "Real Name:" -msgstr "" - -#: fas/templates/user/view.html:27 -msgid "Telephone Number:" -msgstr "" - -#: fas/templates/user/view.html:28 -msgid "Postal Address:" -msgstr "" - -#: fas/templates/user/view.html:30 -msgid "IRC Nick:" -msgstr "" - -#: fas/templates/user/view.html:31 -msgid "PGP Key:" -msgstr "" - -#: fas/templates/user/view.html:32 -msgid "Public SSH Key:" -msgstr "" - -#: fas/templates/user/view.html:36 -msgid "Comments:" -msgstr "" - -#: fas/templates/user/view.html:37 -msgid "Valid" -msgstr "" - -#: fas/templates/user/view.html:38 -msgid "Account Status:" -msgstr "" - -#: fas/templates/user/view.html:39 -msgid "Active" -msgstr "" - -#: fas/templates/user/view.html:40 -msgid "Vacation" -msgstr "" - -#: fas/templates/user/view.html:41 -msgid "Inactive" -msgstr "" - -#: fas/templates/user/view.html:42 -msgid "Pinged" -msgstr "" - -#: fas/templates/user/view.html:44 -msgid "CLA:" -msgstr "" - -#: fas/templates/user/view.html:46 -msgid "Complete it!" -msgstr "" - -#: fas/templates/user/view.html:50 -msgid "Your Roles" -msgstr "" - -#: fas/templates/user/view.html:51 -#, python-format -msgid "%s's Roles" -msgstr "" - -#: fas/templates/user/view.html:68 -msgid "Status:" -msgstr "" - -#: fas/templates/user/view.html:74 -msgid "Tools:" -msgstr "" - -#: fas/templates/user/view.html:78 -msgid "Invite a New Member..." -msgstr "" - -#: fas/templates/user/view.html:79 -msgid "Manage Group Membership..." -msgstr "" - -#: fas/templates/user/view.html:80 -msgid "Manage Group Details..." -msgstr "" - -#: fas/templates/user/view.html:84 -msgid "Queue:" -msgstr "" - -#: fas/templates/user/view.html:88 -#, python-format -msgid "" -"%(user)s requests approval to join " -"%(unapproved_role.group)s." -msgstr "" - diff --git a/fas/po/pl.po b/fas/po/pl.po deleted file mode 100644 index 1fcbfc1..0000000 --- a/fas/po/pl.po +++ /dev/null @@ -1,1786 +0,0 @@ -# translation of pl.po to Polish -# Piotr Drąg , 2008. -# -msgid "" -msgstr "" -"Project-Id-Version: pl\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2008-03-18 10:39-0400\n" -"PO-Revision-Date: 2008-03-15 15:10+0200\n" -"Last-Translator: Piotr Drąg \n" -"Language-Team: Polish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: fas/auth.py:149 fas/auth.py:157 -#, python-format -msgid "%s membership required before application to this group is allowed" -msgstr "" -"Członkostwo w %s jest wymagane przed pozwoleniem na aplikację do tej grupy" - -#: fas/cla.py:70 -#, fuzzy -msgid "You have already completed the CLA." -msgstr "Już podpisałeś CLA." - -#: fas/cla.py:74 -#, fuzzy -msgid "You have not completed the CLA." -msgstr "Nie podpisałeś CLA." - -#: fas/cla.py:78 -#, fuzzy -msgid "" -"To complete the CLA, we must have your telephone number and postal address. " -"Please ensure they have been filled out." -msgstr "" -"Aby podpisać CLA, musimy znać twój numer telefonu i adres pocztowy. Upewnij " -"się, że je podałeś." - -#: fas/cla.py:91 -#, python-format -msgid "You could not be added to the '%s' group." -msgstr "Nie można dodać do grupy \"%s\"." - -#: fas/cla.py:117 -#, fuzzy, python-format -msgid "" -"You have successfully completed the CLA. You are now in the '%s' group." -msgstr "Pomyślnie podpisałeś CLA. Jesteś teraz w grupie \"%s\"." - -#: fas/controllers.py:100 -#, python-format -msgid "Welcome, %s" -msgstr "Witaj, %s" - -#: fas/controllers.py:115 -msgid "" -"The credentials you supplied were not correct or did not grant access to " -"this resource." -msgstr "" -"Podane dane uwierzytelniające nie są poprawne lub nie gwarantują dostępu do " -"tego zasobu." - -#: fas/controllers.py:118 -msgid "You must provide your credentials before accessing this resource." -msgstr "" -"Musisz podać dane uwierzytelniające zanim uzyskasz dostęp do tego zasobu." - -#: fas/controllers.py:121 -msgid "Please log in." -msgstr "Zaloguj się." - -#: fas/controllers.py:132 -msgid "You have successfully logged out." -msgstr "Wylogowano pomyślnie." - -#: fas/controllers.py:144 fas/user.py:93 -#, fuzzy, python-format -msgid "The language '%s' is not available." -msgstr "Grupa \"%s\" nie istnieje." - -#: fas/group.py:23 -#, python-format -msgid "The group '%s' does not exist." -msgstr "Grupa \"%s\" nie istnieje." - -#: fas/group.py:35 -#, python-format -msgid "The group '%s' already exists." -msgstr "Grupa \"%s\" już istnieje." - -#: fas/group.py:131 fas/group.py:481 -#, python-format -msgid "You cannot view '%s'" -msgstr "Nie można przeglądać \"%s\"" - -#: fas/group.py:145 fas/group.py:161 -msgid "Only FAS adminstrators can create groups." -msgstr "Tylko administratorzy FAS mogą tworzyć grupy." - -#: fas/group.py:179 -#, python-format -msgid "The group: '%s' could not be created." -msgstr "Nie można utworzyć grupy \"%s\"." - -#: fas/group.py:189 -#, python-format -msgid "" -"The group: '%(group)s' has been created, but '%(user)s' could not be added " -"as a group administrator." -msgstr "" -"Utworzono grupę \"%(group)s\", ale nie można dodać \"%(user)s\" jako " -"administratora grupy." - -#: fas/group.py:191 -#, python-format -msgid "The group: '%s' has been created." -msgstr "Utworzono grupę \"%s\"." - -#: fas/group.py:206 fas/group.py:221 -#, python-format -msgid "You cannot edit '%s'." -msgstr "Nie można modyfikować \"%s\"." - -#: fas/group.py:238 -msgid "The group details could not be saved." -msgstr "Nie można zapisać szczegółów grupy." - -#: fas/group.py:240 -msgid "The group details have been saved." -msgstr "Zapisano szczegóły grupy." - -#: fas/group.py:266 -#, python-format -msgid "No Groups found matching '%s'" -msgstr "Nie znaleziono grup pasujących do \"%s\"" - -#: fas/group.py:284 -#, python-format -msgid "%(user)s can not apply to %(group)s." -msgstr "%(user)s nie może wysłać aplikacji do %(group)s." - -#: fas/group.py:292 -#, python-format -msgid "%(user)s could not apply to %(group)s: %(error)s" -msgstr "%(user)s nie może wysłać aplikacji do %(group)s: %(error)s" - -#: fas/group.py:303 -#, python-format -msgid "" -"\n" -"Fedora user %(user)s, aka %(name)s <%(email)s> has requested\n" -"membership for %(applicant)s (%(applicant_name)s) in the %(group)s group and " -"needs a sponsor.\n" -"\n" -"Please go to %(url)s to take action. \n" -msgstr "" -"\n" -"Użytkownik Fedory %(user)s, znany też jako %(name)s <%(email)s> poprosił o\n" -"członkostwo dla %(applicant)s (%(applicant_name)s) w grupie %(group)s i " -"potrzebuje sponsora.\n" -"\n" -"Przejdź do %(url)s, aby go zasponsorować. \n" - -#: fas/group.py:310 -#, python-format -msgid "%(user)s has applied to %(group)s!" -msgstr "%(user)s wysłał aplikację do %(group)s!" - -#: fas/group.py:327 -#, python-format -msgid "You cannot sponsor '%s'" -msgstr "Nie można zasponsorować \"%s\"" - -#: fas/group.py:334 -#, python-format -msgid "%(user)s could not be sponsored in %(group)s: %(error)s" -msgstr "Nie można zasponsorować %(user)s w %(group)s: %(error)s" - -#: fas/group.py:340 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has sponsored you for membership in the %(group)s\n" -"group of the Fedora account system. If applicable, this change should\n" -"propagate into the e-mail aliases and CVS repository within an hour.\n" -"\n" -"%(joinmsg)s\n" -msgstr "" -"\n" -"%(name)s <%(email)s> zasponsorował twoje członkostwo w grupie %(group)s\n" -"systemu kont Fedory. Jeśli to potrzebne, ta zmiana powinna zostać zgłoszona\n" -"aliasom e-mail i repozytorium CVS w ciągu godziny.\n" -"\n" -"%(joinmsg)s\n" - -#: fas/group.py:348 -#, python-format -msgid "'%s' has been sponsored!" -msgstr "\"%s\" został zasponsorowany!" - -#: fas/group.py:365 -#, python-format -msgid "You cannot remove '%(user)s' from '%(group)s'." -msgstr "Nie możesz usunąć \"%(user)s\" z \"%(group)s\"." - -#: fas/group.py:372 -#, python-format -msgid "%(user)s could not be removed from %(group)s: %(error)s" -msgstr "Nie można usunąć %(user)s z %(group)s: %(error)s" - -#: fas/group.py:377 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has removed you from the '%(group)s'\n" -"group of the Fedora Accounts System This change is effective\n" -"immediately for new operations, and should propagate into the e-mail\n" -"aliases within an hour.\n" -msgstr "" -"\n" -"%(name)s <%(email)s> usunął cię z grupy \"%(group)s\"\n" -"systemu kont Fedory. Ta zmiana jest natychmiastowa dla nowych operacji i \n" -"powinna zostać zgłoszona aliasom e-mail w ciągu godziny.\n" - -#: fas/group.py:384 -#, python-format -msgid "%(name)s has been removed from %(group)s" -msgstr "%(name)s został usunięty z %(group)s" - -#: fas/group.py:401 -#, python-format -msgid "You cannot upgrade '%s'" -msgstr "Nie można awansować \"%s\"" - -#: fas/group.py:408 -#, python-format -msgid "%(name)s could not be upgraded in %(group)s: %(error)s" -msgstr "Nie można awansować %(name)s w %(group)s: %(error)s" - -#: fas/group.py:417 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has upgraded you to %(status)s status in the\n" -"'%(group)s' group of the Fedora Accounts System This change is\n" -"effective immediately for new operations, and should propagate\n" -"into the e-mail aliases within an hour.\n" -msgstr "" -"\n" -"%(name)s <%(email)s> awansował cię do stanu %(status)s w grupie\n" -"\"%(group)s\" systemu kont Fedory. Ta zmiana jest natychmiastowa dla nowych\n" -"operacji i powinna zostać zgłoszona aliasom e-mail w ciągu godziny.\n" - -#: fas/group.py:424 -#, python-format -msgid "%s has been upgraded!" -msgstr "%s został awansowany!" - -#: fas/group.py:440 -#, python-format -msgid "You cannot downgrade '%s'" -msgstr "Nie można zdegradować \"%s\"" - -#: fas/group.py:447 -#, python-format -msgid "%(name)s could not be downgraded in %(group)s: %(error)s" -msgstr "Nie można degradować %(name)s w %(group)s: %(error)s" - -#: fas/group.py:455 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has downgraded you to %(status)s status in the\n" -"'%(group)s' group of the Fedora Accounts System This change is\n" -"effective immediately for new operations, and should propagate\n" -"into the e-mail aliases within an hour.\n" -msgstr "" -"\n" -"%(name)s <%(email)s> zdegradował cię do stanu %(status)s w grupie\n" -"\"%(group)s\" systemu kont Fedory. Ta zmiana jest natychmiastowa dla nowych\n" -"operacji i powinna zostać zgłoszona aliasom e-mail w ciągu godziny.\n" - -#: fas/group.py:462 -#, python-format -msgid "%s has been downgraded!" -msgstr "%s został zdegradowany!" - -#: fas/group.py:509 -msgid "Come join The Fedora Project!" -msgstr "Przyłącz się do Projektu Fedora!" - -#: fas/group.py:510 -#, python-format -msgid "" -"\n" -"%(name)s <%(email)s> has invited you to join the Fedora\n" -"Project! We are a community of users and developers who produce a\n" -"complete operating system from entirely free and open source software\n" -"(FOSS). %(name)s thinks that you have knowledge and skills\n" -"that make you a great fit for the Fedora community, and that you might\n" -"be interested in contributing.\n" -"\n" -"How could you team up with the Fedora community to use and develop your\n" -"skills? Check out http://fedoraproject.org/join-fedora for some ideas.\n" -"Our community is more than just software developers -- we also have a\n" -"place for you whether you're an artist, a web site builder, a writer, or\n" -"a people person. You'll grow and learn as you work on a team with other\n" -"very smart and talented people.\n" -"\n" -"Fedora and FOSS are changing the world -- come be a part of it!" -msgstr "" -"\n" -"%(name)s <%(email)s> zaprosił cię do Projektu Fedora! Jesteśmy\n" -"społecznością użytkowników i programistów, którzy tworzą kompletny system\n" -"operacyjny wyłącznie z wolnego oprogramowania i oprogramowania open source\n" -"(FOSS). %(name)s uważa, że posiadasz wiedzę i umiejętności, które\n" -"sprawiają, że idealnie pasujesz do społeczności Fedory i możesz być\n" -"zainteresowany współtworzeniem Fedory.\n" -"\n" -"Jak dołączyć do społeczności Fedory, aby używać i rozwijać swoje\n" -"umiejętności? Zobacz http://fedoraproject.org/join-fedora.\n" -"Nasz społeczność to coś więcej niż tylko programiści - znajdzie się dla\n" -"ciebie miejsce niezależnie od tego, czy jesteś artystą, twórcą stron WWW,\n" -"chcesz coś pisać lub pracować z ludźmi. Rozwiniesz się i nauczysz pracować\n" -"w zespole z innymi inteligentnymi i utalentowanymi ludźmi.\n" -"\n" -"Fedora i FOSS zmieniają świat - zostań częścią nas!" - -#: fas/group.py:527 -#, python-format -msgid "Message sent to: %s" -msgstr "Wysłano wiadomość do %s" - -#: fas/group.py:530 -#, python-format -msgid "You are not in the '%s' group." -msgstr "Nie jesteś w grupie \"%s\"." - -#: fas/help.py:9 fas/help.py:49 -#, fuzzy -msgid "Error" -msgstr "Błąd!" - -#: fas/help.py:9 fas/help.py:49 -msgid "

We could not find that help item

" -msgstr "" - -#: fas/help.py:10 -msgid "IRC Nick (Optional)" -msgstr "" - -#: fas/help.py:10 -msgid "" -"

IRC Nick is used to identify yourself on irc.freenode.net. Please " -"register your nick on irc.freenode.net first, then fill this in so people " -"can find you online when they need to

" -msgstr "" - -#: fas/help.py:11 -msgid "Email (Required)" -msgstr "" - -#: fas/help.py:11 -msgid "" -"

This email address should be your prefered email contact and will be used " -"to send various official emails to. This is also where your @fedoraproject." -"org email will get forwarded

" -msgstr "" - -#: fas/help.py:12 -#, fuzzy -msgid "Full Name (Required)" -msgstr "Imię i nazwisko:" - -#: fas/help.py:12 -msgid "

Your Human Name or \"real life\" name

" -msgstr "" - -#: fas/help.py:13 -#, fuzzy -msgid "GPG Key" -msgstr "Klucz PGP" - -#: fas/help.py:13 -msgid "" -"

A GPG key is generally used to prove that a message or email came from " -"you or to encrypt information so that only the recipients can read it. This " -"can be used when a password reset is sent to your email.

" -msgstr "" - -#: fas/help.py:14 -#, fuzzy -msgid "Telephone" -msgstr "Numer telefonu" - -#: fas/help.py:14 -msgid "" -"

Required in order to complete the CLA. Sometimes during a time of emergency someone " -"from the Fedora Project may need to contact you. For more information see " -"our Privacy " -"Policy

" -msgstr "" - -#: fas/help.py:15 fas/templates/user/edit.html:36 -msgid "Postal Address" -msgstr "Adres pocztowy" - -#: fas/help.py:15 -msgid "" -"

Required in order to complete the CLA. This should be a mailing address where you " -"can be contacted. See our Privacy Policy about any concerns.

" -msgstr "" - -#: fas/help.py:16 -msgid "Timezone (Optional)" -msgstr "" - -#: fas/help.py:16 -msgid "

Please specify the time zone you are in.

" -msgstr "" - -#: fas/help.py:17 -msgid "Comments (Optional)" -msgstr "" - -#: fas/help.py:17 -msgid "

Misc comments about yourself.

" -msgstr "" - -#: fas/help.py:18 fas/templates/user/list.html:28 -msgid "Account Status" -msgstr "Stan konta" - -#: fas/help.py:18 -msgid "" -"

Shows account status, possible values include

  • Valid
  • Disabled
  • Expired

" -msgstr "" - -#: fas/help.py:19 -#, fuzzy -msgid "CLA" -msgstr "CLA:" - -#: fas/help.py:19 -msgid "" -"

In order to become a full Fedora contributor you must complete the Contributor " -"License Agreement. This license is a legal agreement between you and " -"Red Hat. Full status allows people to contribute content and code and is " -"recommended for anyone interested in getting involved in the Fedora Project." -"

" -msgstr "" - -#: fas/help.py:20 fas/templates/user/edit.html:51 -msgid "Public SSH Key" -msgstr "Publiczny klucz SSH" - -#: fas/help.py:20 -msgid "" -"

Many resources require public key authentiaction to work. By uploading " -"your public key to us, you can then log in to our servers. Type \"man ssh-" -"keygen\" for more information on creating your key. Once created you will " -"want to upload ~/.ssh/id_dsa.pub or ~/.ssh/id_rsa.pub

" -msgstr "" - -#: fas/help.py:21 fas/templates/user/edit.html:67 -msgid "Locale" -msgstr "Język" - -#: fas/help.py:21 -msgid "" -"

For non-english speaking peoples this allows individuals to select which " -"locale they are in.

" -msgstr "" - -#: fas/help.py:23 fas/templates/group/list.html:47 -msgid "Apply" -msgstr "Zastosuj" - -#: fas/help.py:23 -msgid "" -"

Applying for a group is like applying for a job and it can certainly take " -"a while to get in. Many groups have their own rules about how to actually " -"get approved or sponsored. For more information on how the account system " -"works see the about page.

" -msgstr "" - -#: fas/help.py:24 fas/templates/group/view.html:107 -msgid "Remove" -msgstr "Usuń" - -#: fas/help.py:24 -msgid "" -"

Removing a person from a group will cause that user to no longer be in " -"the group. They will need to re-apply to get in. Admins can remove anyone, " -"Sponsors can remove users, users can't remove anyone.

" -msgstr "" - -#: fas/help.py:25 fas/templates/group/view.html:111 -msgid "Upgrade" -msgstr "Awansuj" - -#: fas/help.py:25 -msgid "" -"

Upgrade a persons status in this group.

  • from user -> to sponsor
  • From sponsor -> administrator
  • administrators cannot be " -"upgraded beyond administrator

" -msgstr "" - -#: fas/help.py:26 fas/templates/group/view.html:115 -msgid "Downgrade" -msgstr "Zdegraduj" - -#: fas/help.py:26 -msgid "" -"

Downgrade a persons status in the group.

  • from administrator -> to " -"sponsor
  • From sponsor -> user
  • users cannot be downgraded " -"below user, you may want to remove them

" -msgstr "" - -#: fas/help.py:27 fas/templates/group/view.html:102 -msgid "Approve" -msgstr "Zaakceptuj" - -#: fas/help.py:27 -msgid "" -"

A sponsor or administrator can approve users to be in a group. Once the " -"user has applied for the group, go to the group's page and click approve to " -"approve the user.

" -msgstr "" - -#: fas/help.py:28 fas/templates/group/view.html:76 -#: fas/templates/group/view.html:98 -msgid "Sponsor" -msgstr "Sponsor" - -#: fas/help.py:28 -msgid "" -"

A sponsor or administrator can sponsor users to be in a gruop. Once the " -"user has applied for the group, go to the group's page and click approve to " -"sponsor the user. Sponsorship of a user implies that you are approving a " -"user and may mentor and answer their questions as they come up.

" -msgstr "" - -#: fas/help.py:29 -#, fuzzy -msgid "Add User" -msgstr "Dodaj użytkownika:" - -#: fas/help.py:29 -msgid "" -"

Manually add a user to a group. Place their username in this field and " -"click 'Add'

" -msgstr "" - -#: fas/help.py:30 -#, fuzzy -msgid "Group Name" -msgstr "Nazwa grupy:" - -#: fas/help.py:30 -msgid "" -"

The name of the group you'd like to create. It should be alphanumeric " -"though '-'s are allowed

" -msgstr "" - -#: fas/help.py:31 -#, fuzzy -msgid "Display Name" -msgstr "Wyświetlana nazwa:" - -#: fas/help.py:31 -msgid "

More human readable name of the group

" -msgstr "" - -#: fas/help.py:32 -#, fuzzy -msgid "Group Owner" -msgstr "Właściciel grupy:" - -#: fas/help.py:32 -msgid "

The name of the owner who will run this group

" -msgstr "" - -#: fas/help.py:33 -#, fuzzy -msgid "Group Type" -msgstr "Typ grupy:" - -#: fas/help.py:33 -msgid "" -"

Normally it is safe to leave this blank. Though some values include " -"'tracking', 'shell', 'cvs', 'git', 'hg', 'svn', and 'mtn'. This value only " -"really matters if the group is to end up getting shell access or commit " -"access somewhere like fedorahosted.

" -msgstr "" - -#: fas/help.py:34 -#, fuzzy -msgid "Needs Sponsor" -msgstr "Wymaga sponsora:" - -#: fas/help.py:34 -msgid "" -"

If your group requires sponsorship (recommended), this means that when a " -"user is approved by a sponsor. That relationship is recorded in the account " -"system. If user A sponsors user N, then in viewing the members of this " -"group, people will know to contact user A about user N if something goes " -"wrong. If this box is unchecked, this means that only approval is needed " -"and no relationship is recorded about who did the approving

" -msgstr "" - -#: fas/help.py:35 -#, fuzzy -msgid "Self Removal" -msgstr "Można ją samodzielnie usunąć:" - -#: fas/help.py:35 -msgid "" -"

Should users be able to remove themselves from this group without " -"sponsor / admin intervention? (recommended yes)

" -msgstr "" - -#: fas/help.py:36 -#, fuzzy -msgid "Must Belong To" -msgstr "Musi należeć do:" - -#: fas/help.py:36 -msgid "" -"

Before a user can join this group, they must belong to the group listed " -"in this box. This value cannot be removed without administrative " -"intervention, only changed. Recommended values are for the 'cla_done' " -"group.

" -msgstr "" - -#: fas/help.py:37 -#, fuzzy -msgid "Join Message" -msgstr "Wiadomość o dołączeniu:" - -#: fas/help.py:37 -msgid "" -"

This message will go out to users when they join the group. It should be " -"informative and offer tips about what to do next. A description of the " -"group would also be valuable here

" -msgstr "" - -#: fas/help.py:38 -msgid "Client Side Cert" -msgstr "" - -#: fas/help.py:38 -msgid "" -"

The client side cert is generally used to grant access to upload packages " -"to Fedora or for other authentication purposes like with koji. If you are " -"not a package maintainer there is no need to worry about the client side " -"cert

" -msgstr "" - -#: fas/model.py:133 -msgid "user is already in this group" -msgstr "użytkownik jest już w tej grupie" - -#: fas/model.py:146 fas/model.py:161 fas/model.py:215 -msgid "user is not a member" -msgstr "użytkownik nie jest członkiem" - -#: fas/model.py:150 -msgid "administrators cannot be upgraded any further" -msgstr "administratorów nie można już wyżej awansować" - -#: fas/model.py:165 -msgid "users cannot be downgraded any further" -msgstr "użytkowników nie można już niżej zdegradować" - -#: fas/model.py:174 -msgid "user is not an unapproved member" -msgstr "użytkownik nie jest zaakceptowanym członkiem" - -#: fas/openid_fas.py:55 -msgid "The OpenID request could not be decoded." -msgstr "Nie można rozszyfrować żądania OpenID." - -#: fas/safasprovider.py:126 -#, python-format -msgid "Loading: %(visitmod)s" -msgstr "Wczytywanie: %(visitmod)s" - -#: fas/user.py:38 -#, python-format -msgid "'%s' does not exist." -msgstr "\"%s\" nie istnieje." - -#: fas/user.py:50 -#, python-format -msgid "Error: Could not create - '%s'" -msgstr "Błąd: nie można utworzyć - \"%s\"" - -#: fas/user.py:52 -#, python-format -msgid "'%s' already exists." -msgstr "\"%s\" już istnieje." - -#: fas/user.py:60 -msgid "" -"To prevent email loops, your email address cannot be @fedoraproject.org." -msgstr "" -"Aby zapobiec pętli adresów e-mail, twój adres nie może być \"@fedoraproject." -"org\"." - -#: fas/user.py:76 -#, python-format -msgid "Error - Not a valid ssh key: %s" -msgstr "Błąd - nieprawidłowy klucz SSH: %s" - -#: fas/user.py:85 -#, python-format -msgid "'%s' is an illegal username." -msgstr "\"%s\" jest niedozwoloną nazwą użytkownika." - -#: fas/user.py:245 -#, python-format -msgid "You cannot edit %s" -msgstr "Nie można modyfikować %s" - -#: fas/user.py:263 -#, python-format -msgid "You do not have permission to edit '%s'" -msgstr "Nie masz uprawnień do modyfikowania \"%s\"" - -#: fas/user.py:272 -#, python-format -msgid "Email Change Requested for %s" -msgstr "Zażądano zmiany adresu e-mail dla %s" - -#: fas/user.py:274 -#, python-format -msgid "" -"\n" -"You have recently requested to change your Fedora Account System email\n" -"to this address. To complete the email change, you must confirm your\n" -"ownership of this email by visiting the following URL (you will need to\n" -"login with your Fedora account first):\n" -"\n" -"https://admin.fedoraproject.org/accounts/user/verifyemail/%s\n" -msgstr "" -"\n" -"Zażądano zmiany adresu e-mail w systemie kont Fedory na ten adres. Aby\n" -"zakończyć zmianę adresu e-mail, musisz potwierdzić własność tego adresu\n" -"e-mail odwiedzając następujący adres (będziesz musiał najpierw zalogować\n" -"się na swoim koncie Fedory):\n" -"\n" -"https://admin.fedoraproject.org/accounts/user/verifyemail/%s\n" - -#: fas/user.py:282 -msgid "" -" Before your new email takes effect, you must confirm it. You should " -"receive an email with instructions shortly." -msgstr "" -" Zanim nowy adres e-mail zostanie uwzględniony, musisz go potwierdzić. " -"Powinieneś wkrótce otrzymać e-mail z instrukcjami." - -#: fas/user.py:294 -#, python-format -msgid "Your account details could not be saved: %s" -msgstr "Nie można zapisać szczegółów konta: %s" - -#: fas/user.py:296 -msgid "Your account details have been saved." -msgstr "Zapisano szczegóły konta." - -#: fas/user.py:322 -#, python-format -msgid "No users found matching '%s'" -msgstr "Nie znaleziono użytkowników pasujących do \"%s\"" - -#: fas/user.py:344 -msgid "" -"Your pending email change has been canceled. The email change token has " -"been invalidated." -msgstr "" -"Anulowano oczekującą zmianę adresu e-mail. Token zmiany adresu e-mail został " -"unieważniony." - -#: fas/user.py:348 fas/user.py:364 -msgid "You do not have any pending email changes." -msgstr "Brak oczekujących zmian adresów e-mail." - -#: fas/user.py:352 fas/user.py:368 -msgid "Invalid email change token." -msgstr "Nieprawidłowy token zmiany adresu e-mail." - -#: fas/user.py:377 -#, python-format -msgid "You have successfully changed your email to '%s'" -msgstr "Pomyślnie zmieniono adres e-mail na \"%s\"." - -#: fas/user.py:385 -msgid "No need to sign up, you have an account!" -msgstr "Nie musisz zakładać konta, masz już je!" - -#: fas/user.py:407 -msgid "Welcome to the Fedora Project!" -msgstr "Witaj w Projekcie Fedora!" - -#: fas/user.py:408 -#, python-format -msgid "" -"\n" -"You have created a new Fedora account!\n" -"Your new password is: %s\n" -"\n" -"Please go to https://admin.fedoraproject.org/accounts/ to change it.\n" -"\n" -"Welcome to the Fedora Project. Now that you've signed up for an\n" -"account you're probably desperate to start contributing, and with that\n" -"in mind we hope this e-mail might guide you in the right direction to\n" -"make this process as easy as possible.\n" -"\n" -"Fedora is an exciting project with lots going on, and you can\n" -"contribute in a huge number of ways, using all sorts of different\n" -"skill sets. To find out about the different ways you can contribute to\n" -"Fedora, you can visit our join page which provides more information\n" -"about all the different roles we have available.\n" -"\n" -"http://fedoraproject.org/en/join-fedora\n" -"\n" -"If you already know how you want to contribute to Fedora, and have\n" -"found the group already working in the area you're interested in, then\n" -"there are a few more steps for you to get going.\n" -"\n" -"Foremost amongst these is to sign up for the team or project's mailing\n" -"list that you're interested in - and if you're interested in more than\n" -"one group's work, feel free to sign up for as many mailing lists as\n" -"you like! This is because mailing lists are where the majority of work\n" -"gets organised and tasks assigned, so to stay in the loop be sure to\n" -"keep up with the messages.\n" -"\n" -"Once this is done, it's probably wise to send a short introduction to\n" -"the list letting them know what experience you have and how you'd like\n" -"to help. From here, existing members of the team will help you to find\n" -"your feet as a Fedora contributor.\n" -"\n" -"And finally, from all of us here at the Fedora Project, we're looking\n" -"forward to working with you!\n" -msgstr "" -"\n" -"Utworzono nowe konto Fedory!\n" -"Nowe hasło to: %s\n" -"\n" -"Przejdź do https://admin.fedoraproject.org/fas/, aby je zmienić.\n" -"\n" -"Witaj w Projekcie Fedora. Teraz, kiedy założyłeś konto, prawdopodobnie\n" -"desperacko chcesz zacząć tworzenie, a z myślą o tym mamy nadzieję, że ten\n" -"e-mail skieruje cię w dobrym kierunku, aby ułatwić ten proces, jak to tylko\n" -"możliwe.\n" -"\n" -"Fedora jest ekscytującym projektem, w którym wiele się dzieje i możesz go\n" -"współtworzyć na wiele sposobów, używając różnych umiejętności.\n" -"Aby dowiedzieć się o różnych sposobach współtworzenia Fedory, możesz \n" -"odwiedzić naszą stronę o dołączaniu, która podaje więcej informacji o \n" -"wszystkich, różnorodnych dostępnych rolach.\n" -"\n" -"http://fedoraproject.org/pl/join-fedora\n" -"\n" -"Jeśli wiesz już, jak chcesz współtworzyć Fedorę, i znalazłeś grupę już \n" -"pracującą nad obszarem, który cię interesuje, pozostało tylko kilka kroków.\n" -"\n" -"Po pierwsze musisz zapisać się na listę mailingową zespołu lub projektu,\n" -"który cię interesuje - a jeśli jesteś zainteresowany więcej niż jedną\n" -"grupą, możesz zapisać się do tylu list, ilu chcesz! Jest tak, ponieważ\n" -"listy mailingowe to miejsca, gdzie większość pracy jest organizowana, a\n" -"zadania są przydzielane, tak więc aby być na czasie, czytaj wiadomości na\n" -"tych listach.\n" -"\n" -"Kiedy już to zrobisz, najlepiej jest wysłać krótkie wprowadzenie na listę,\n" -"powiadamiając członków grupy o twoich doświadczeniach i o tym, jak\n" -"zamierzasz pomagać. Od teraz członkowie zespołu pomogą ci odnaleźć swoje\n" -"miejsce jako współtwórca Fedory.\n" -"\n" -"A na koniec: wszyscy członkowie Projektu Fedora nie mogą się doczekać, aby\n" -"z tobą pracować!\n" - -#: fas/user.py:449 -msgid "An account has already been registered with that email address." -msgstr "Konto zostało już zarejestrowane za pomocą tego adresu e-mail." - -#: fas/user.py:453 -msgid "" -"Your password has been emailed to you. Please log in with it and change " -"your password" -msgstr "Hasło zostało wysłane e-mailem. Zaloguj się za jego pomocą i zmień je" - -#: fas/user.py:487 -msgid "Your password could not be changed." -msgstr "Nie można zmienić hasła." - -#: fas/user.py:490 -msgid "Your password has been changed." -msgstr "Zmieniono hasło." - -#: fas/user.py:498 -msgid "You are already logged in!" -msgstr "Jesteś już zalogowany!" - -#: fas/user.py:509 -msgid "You are already logged in." -msgstr "Jesteś już zalogowany." - -#: fas/user.py:515 -msgid "Username email combo does not exist!" -msgstr "Kombinacja nazwy użytkownika i adresu e-mail nie istnieje!" - -#: fas/user.py:518 -msgid "username + email combo unknown." -msgstr "nieznana kombinacja nazwy użytkownika i adresu e-mail" - -#: fas/user.py:521 -msgid "Fedora Project Password Reset" -msgstr "Resetowanie hasła Projektu Fedora" - -#: fas/user.py:522 -#, python-format -msgid "" -"\n" -"Somebody (hopefully you) has requested a password reset for your account!\n" -"To change your password (or to cancel the request), please visit\n" -"https://admin.fedoraproject.org/accounts/user/verifypass/%(user)s/%(token)s\n" -msgstr "" -"\n" -"Ktoś (prawdopodobnie ty) zażądał resetowania hasła konta!\n" -"Aby zmienić hasło (lub anulować żądanie), odwiedź\n" -"https://admin.fedoraproject.org/accounts/user/verifypass/%(user)s/%(token)s\n" - -#: fas/user.py:534 -msgid "" -"This user does not have a GPG Key ID set, so an encrypted email cannot be " -"sent." -msgstr "" -"Ten użytkownik nie ustawił identyfikatora klucza GPG, więc nie można wysłać " -"zaszyfrowanego e-maila." - -#: fas/user.py:538 -msgid "Your key could not be retrieved from subkeys.pgp.net" -msgstr "Nie można pobrać klucza z subkeys.pgp.net" - -#: fas/user.py:560 -msgid "Your password reset email could not be encrypted." -msgstr "Nie można zaszyfrować e-maila resetowania hasła." - -#: fas/user.py:566 -msgid "A password reset URL has been emailed to you." -msgstr "Adres resetowania hasła został wysłany e-mailem." - -#: fas/user.py:576 fas/user.py:592 fas/user.py:612 -msgid "You do not have any pending password changes." -msgstr "Brak oczekujących zmian haseł." - -#: fas/user.py:581 fas/user.py:596 fas/user.py:617 -msgid "Invalid password change token." -msgstr "Nieprawidłowy token zmiany hasła." - -#: fas/user.py:601 -msgid "" -"Your password reset has been canceled. The password change token has been " -"invalidated." -msgstr "Anulowano resetowanie hasła. Token zmiany hasła został unieważniony." - -#: fas/user.py:626 -msgid "" -"You have successfully reset your password. You should now be able to login " -"below." -msgstr "Pomyślnie zresetowano hasło. Możesz się teraz zalogować." - -#: fas/user.py:663 -#, fuzzy -msgid "Before generating a certificate, you must first complete the CLA." -msgstr "Przed utworzeniem certyfikatu musisz podpisać CLA." - -#: fas/templates/about.html:7 -msgid "About FAS" -msgstr "Informacje o FAS" - -#: fas/templates/about.html:10 -msgid "FAS - The Open Account System" -msgstr "FAS - otwarty system kont" - -#: fas/templates/about.html:11 -msgid "" -"FAS is designed around an open architecture. Unlike the traditional account " -"systems where a single admin or group of admins decide who gets to be in " -"what group, FAS is completely designed to be self operating per team. Every " -"group is given at least one administrator who can then approve other people " -"in the group. Also, unlike traditional account systems. FAS allows people " -"to apply for the groups they want to be in. This paridigm is interesting as " -"it allows anyone to find out who is in what groups and contact them. This " -"openness is brought over from the same philosophies that make Open Source " -"popular." -msgstr "" -"FAS został zaprojektowany wokół otwartej architektury. Inaczej niż " -"tradycyjne systemy kont, gdzie jeden administrator lub ich grupa decyduje o " -"przydziale do grup, FAS został zaprojektowany tak, aby zespoły mogły działać " -"samodzielnie. Każda grupa posiada co najmniej jednego administratora, który " -"może akceptować innych członków grupy. FAS pozwala ludziom także na " -"wysyłanie aplikacji do grup, do których chcą należeć. To ciekawa sprawa, " -"ponieważ pozwala każdemu użytkownikowi na wyszukiwanie ludzi w grupach i " -"kontaktowanie się z nimi. Ta otwartość powstała na podstawie tej samej " -"filozofii, która uczyniła oprogramowanie open source tak popularnym." - -#: fas/templates/about.html:12 -msgid "Etiquette" -msgstr "Etykieta" - -#: fas/templates/about.html:13 -msgid "" -"People shouldn't assume that by applying for a group that they're then in " -"that group. Consider it like applying for another job. It often takes " -"time. For best odds of success, learn about the group you're applying for " -"and get to know someone in the group. Find someone with sponsor or admin " -"access and ask them if they'd have time to mentor you. Plan on spending at " -"least a few days learning about the group, doing a mundain task, " -"participating on the mailing list. Sometimes this process can take weeks " -"depending on the group. It's best to know you will get sponsored before you " -"apply." -msgstr "" -"Ludzie nie powinni przyjmować, że wysyłając aplikację o dołączenie do grupy " -"stają się członkami tej grupy. Należy rozumieć to jak wysyłanie aplikacji o " -"inną pracę. To zwykle zajmuje czasu. Aby dołączyć do grupy, dowiedz się " -"więcej o niej i poznaj któregoś z jej członków. Znajdź kogoś z dostępem " -"sponsora lub administratora i zapytaj go, czy nie miałby trochę czasu, aby " -"ci pomóc. Zaplanuj co najmniej kilka dni na uczenie się o grupie, " -"wykonywanie prostych zadań, uczestniczenie w liście mailingowej. Czasami ten " -"proces może zająć tygodnie, w zależności od grupy. Najlepiej jest wiedzieć, " -"że zostaniesz zasponsorowany, zanim wyślesz aplikację." - -#: fas/templates/about.html:14 -msgid "Users, Sponsors, Administrators" -msgstr "Użytkownicy, sponsorzy i administratorzy" - -#: fas/templates/about.html:15 -msgid "" -"Once you're in the group, you're in the group. Sponsorship and " -"Administrators typically have special access in the group in questions. " -"Some groups consider sponsorship level to be of a higher involvement, " -"partial ownership of the group for example. But as far as the account " -"system goes the disctinction is easy. Sponsors can approve new users and " -"make people into sponsors. They cannot, however, downgrade or remove other " -"sponsors. They also cannot change administrators in any way. " -"Administrators can do anything to anyone in the group." -msgstr "" -"Kiedy już będziesz w grupie, to będziesz w tej grupie. Sponsorzy i " -"administratorzy zwykle posiadają specjalny dostęp do grup w razie pytań. " -"Niektóre grupy uważają sponsorów za wyższy poziom udziału, jak na przykład " -"częściowe posiadanie grupy. Ale na poziomie systemu kont podział jest " -"prosty. Sponsorzy mogą akceptować nowych użytkowników i wybierać nowych " -"sponsorów. Nie mogą jednak degradować lub usuwać innych sponsorów. Nie mogą " -"też w żaden sposób zmieniać administratorów. Administratorzy mogą wszystko " -"ze wszystkimi w grupie." - -#: fas/templates/error.html:7 fas/templates/home.html:7 -#: fas/templates/cla/index.html:7 fas/templates/openid/about.html:7 -#: fas/templates/openid/id.html:7 fas/templates/openid/trusted.html:7 -msgid "Fedora Accounts System" -msgstr "System kont Fedory" - -#: fas/templates/error.html:17 -msgid "Error!" -msgstr "Błąd!" - -#: fas/templates/error.html:18 -msgid "The following error(s) have occured with your request:" -msgstr "Po żądaniu wystąpiły następujące błędy:" - -#: fas/templates/home.html:11 -msgid "Todo queue:" -msgstr "" - -#: fas/templates/home.html:17 -#, python-format -msgid "" -"%(user)s requests approval to join %(group)s." -msgstr "" -"%(user)s poprosił o zaakceptowanie dołączenia do %(group)s." - -#: fas/templates/home.html:24 -#, fuzzy, python-format -msgid "" -"CLA not completed. To become a full Fedora Contributor please complete the CLA." -msgstr "Nie podpisano CLA. Aby zostać pełnoprawnym współtwórcą Fedory, " - -#: fas/templates/home.html:25 -#, python-format -msgid "" -"You have not submitted an SSH key, some Fedora resources require an SSH " -"key. Please submit yours by editing My Account" -msgstr "" - -#: fas/templates/home.html:29 -msgid "Download a client-side certificate" -msgstr "" - -#: fas/templates/login.html:7 -msgid "Login to the Fedora Accounts System" -msgstr "Zaloguj się do systemu kont Fedory" - -#: fas/templates/login.html:17 fas/templates/login.html:23 -msgid "Login" -msgstr "Zaloguj się" - -#: fas/templates/login.html:20 -msgid "User Name:" -msgstr "Nazwa użytkownika:" - -#: fas/templates/login.html:21 fas/templates/user/view.html:37 -msgid "Password:" -msgstr "Hasło:" - -#: fas/templates/login.html:29 -msgid "Forgot Password?" -msgstr "Zapomniałeś hasła?" - -#: fas/templates/login.html:30 -msgid "Sign Up" -msgstr "Załóż konto" - -#: fas/templates/master.html:22 -msgid "Fedora" -msgstr "Fedora" - -#: fas/templates/master.html:35 -msgid "Learn about Fedora" -msgstr "Dowiedz się więcej o Fedorze" - -#: fas/templates/master.html:36 -msgid "Download Fedora" -msgstr "Pobierz Fedorę" - -#: fas/templates/master.html:37 -msgid "Projects" -msgstr "Projekty" - -#: fas/templates/master.html:38 -msgid "Join Fedora" -msgstr "Dołącz do Fedory" - -#: fas/templates/master.html:39 -msgid "Communicate" -msgstr "Komunikacjua" - -#: fas/templates/master.html:40 -msgid "Help/Documentation" -msgstr "Pomoc/dokumentacja" - -#: fas/templates/master.html:46 -msgid "Logged in:" -msgstr "Zalogowany:" - -#: fas/templates/master.html:52 -msgid "My Account" -msgstr "Moje konto" - -#: fas/templates/master.html:53 fas/templates/master.html:94 -msgid "Log Out" -msgstr "Wyloguj się" - -#: fas/templates/master.html:54 fas/templates/welcome.html:21 -msgid "Log In" -msgstr "Zaloguj się" - -#: fas/templates/master.html:61 -msgid "Home" -msgstr "Strona domowa" - -#: fas/templates/master.html:64 -msgid "New Group" -msgstr "Nowa grupa" - -#: fas/templates/master.html:65 -msgid "User List" -msgstr "Lista użytkowników" - -#: fas/templates/master.html:67 -msgid "Group List" -msgstr "Lista grup" - -#: fas/templates/master.html:68 -msgid "Apply For a new Group" -msgstr "Wyślij aplikację nowej grupy" - -#: fas/templates/master.html:69 -msgid "News" -msgstr "Wiadomości" - -#: fas/templates/master.html:74 -#, fuzzy -msgid "Locale:" -msgstr "Język" - -#: fas/templates/master.html:78 -msgid "OK" -msgstr "" - -#: fas/templates/master.html:90 -msgid "About" -msgstr "Informacje o" - -#: fas/templates/master.html:91 -msgid "Contact Us" -msgstr "Kontakt" - -#: fas/templates/master.html:92 -msgid "Legal & Privacy" -msgstr "Legalność i prywatność" - -#: fas/templates/master.html:97 -msgid "" -"Copyright © 2007 Red Hat, Inc. and others. All Rights Reserved. Please " -"send any comments or corrections to the websites team." -msgstr "" -"Copyright © 2007 Red Hat, Inc. i inni. Wszystkie prawa zastrzeżone. Prosimy " -"wysyłać wszystkie komentarze lub poprawki do zespołu stron WWW (w języku angielskim)." - -#: fas/templates/master.html:100 -msgid "" -"The Fedora Project is maintained and driven by the community and sponsored " -"by Red Hat. This is a community maintained site. Red Hat is not " -"responsible for content." -msgstr "" -"Projekt Fedora jest zarządzany i prowadzony przez społeczność i sponsorowany " -"przez Red Hata. Ta strona jest zarządzana przez społeczność. Red Hat nie " -"jest odpowiedzialny za jej zawartość." - -#: fas/templates/welcome.html:7 -msgid "Welcome to FAS2" -msgstr "Witaj w FAS2" - -#: fas/templates/welcome.html:18 -msgid "" -"Welcome to the Fedora Accounts System 2. Please submit bugs to https://fedorahosted.org/fas2/ or stop " -"by #fedora-admin on irc.freenode.net." -msgstr "" - -#: fas/templates/welcome.html:22 -msgid "New Account" -msgstr "Nowe konto" - -#: fas/templates/welcome.html:23 -msgid "Why Join?" -msgstr "Po co się przyłączać?" - -#: fas/templates/cla/index.html:10 -msgid "Fedora Contributor License Agreement" -msgstr "Warunki licencji współtwórcy Fedory" - -#: fas/templates/cla/index.html:11 fas/templates/cla/index.html:13 -#, python-format -msgid "Text Version" -msgstr "Wersja tekstowa" - -#: fas/templates/cla/index.html:15 -#, fuzzy -msgid "You have already sucessfully complete the CLA." -msgstr "Już podpisałeś CLA." - -#: fas/templates/cla/index.html:20 -msgid "I agree" -msgstr "Zgadzam się" - -#: fas/templates/cla/index.html:21 -msgid "I do not agree" -msgstr "Nie zgadzam się" - -#: fas/templates/group/edit.html:7 -msgid "Edit Group" -msgstr "Edytuj grupę" - -#: fas/templates/group/edit.html:10 -#, python-format -msgid "Edit Group: %s" -msgstr "Edytuj grupę: %s" - -#: fas/templates/group/edit.html:13 fas/templates/group/new.html:18 -msgid "Display Name:" -msgstr "Wyświetlana nazwa:" - -#: fas/templates/group/edit.html:18 fas/templates/group/new.html:28 -msgid "Group Type:" -msgstr "Typ grupy:" - -#: fas/templates/group/edit.html:23 fas/templates/group/new.html:23 -msgid "Group Owner:" -msgstr "Właściciel grupy:" - -#: fas/templates/group/edit.html:28 fas/templates/group/new.html:33 -#: fas/templates/group/view.html:41 -msgid "Needs Sponsor:" -msgstr "Wymaga sponsora:" - -#: fas/templates/group/edit.html:34 fas/templates/group/new.html:38 -#: fas/templates/group/view.html:45 -msgid "Self Removal:" -msgstr "Można ją samodzielnie usunąć:" - -#: fas/templates/group/edit.html:40 -msgid "Group Prerequisite:" -msgstr "Wymagania wstępne grupy:" - -#: fas/templates/group/edit.html:46 -msgid "Group Join Message:" -msgstr "Wiadomość o dołączeniu do grupy:" - -#: fas/templates/group/edit.html:51 fas/templates/user/edit.html:80 -msgid "Save!" -msgstr "Zapisz!" - -#: fas/templates/group/invite.html:7 fas/templates/group/invite.html:10 -msgid "Invite a new community member!" -msgstr "Zaproś nowego członka społeczności!" - -#: fas/templates/group/invite.html:14 -msgid "To email:" -msgstr "E-mail:" - -#: fas/templates/group/invite.html:15 -msgid "From:" -msgstr "Od:" - -#: fas/templates/group/invite.html:16 -msgid "Subject:" -msgstr "Temat:" - -#: fas/templates/group/invite.html:17 -msgid "Message:" -msgstr "Wiadomość:" - -#: fas/templates/group/invite.html:39 -msgid "Send!" -msgstr "Wyślij!" - -#: fas/templates/group/list.html:7 -msgid "Groups List" -msgstr "Lista grup" - -#: fas/templates/group/list.html:19 fas/templates/user/list.html:11 -#, python-format -msgid "List (%s)" -msgstr "Lista (%s)" - -#: fas/templates/group/list.html:20 -msgid "Search Groups" -msgstr "Znajdź grupę" - -#: fas/templates/group/list.html:22 -msgid "\"*\" is a wildcard (Ex: \"cvs*\")" -msgstr "\"*\" jest wieloznacznikiem (np. \"cvs*\")" - -#: fas/templates/group/list.html:25 -msgid "Search" -msgstr "Znajdź" - -#: fas/templates/group/list.html:28 fas/templates/user/list.html:19 -msgid "Results" -msgstr "Wyniki" - -#: fas/templates/group/list.html:31 fas/templates/user/list.html:22 -msgid "All" -msgstr "Wszystkie" - -#: fas/templates/group/list.html:36 -msgid "Group" -msgstr "Grupa" - -#: fas/templates/group/list.html:36 -msgid "Description" -msgstr "Opis" - -#: fas/templates/group/list.html:36 -msgid "Status" -msgstr "Stan" - -#: fas/templates/group/list.html:44 fas/templates/group/view.html:22 -#: fas/templates/user/view.html:70 -msgid "Approved" -msgstr "Zaakceptowana" - -#: fas/templates/group/list.html:45 fas/templates/group/view.html:23 -msgid "Unapproved" -msgstr "Niezaakceptowana" - -#: fas/templates/group/new.html:7 fas/templates/group/new.html:10 -msgid "Create a new FAS Group" -msgstr "Utwórz nową grupę FAS" - -#: fas/templates/group/new.html:13 -msgid "Group Name:" -msgstr "Nazwa grupy:" - -#: fas/templates/group/new.html:43 -msgid "Must Belong To:" -msgstr "Musi należeć do:" - -#: fas/templates/group/new.html:48 fas/templates/group/view.html:49 -msgid "Join Message:" -msgstr "Wiadomość o dołączeniu:" - -#: fas/templates/group/new.html:53 -msgid "Create!" -msgstr "Utwórz!" - -#: fas/templates/group/view.html:7 fas/templates/user/view.html:77 -msgid "View Group" -msgstr "Wyświetl grupę" - -#: fas/templates/group/view.html:21 -msgid "My Status:" -msgstr "Mój stan:" - -#: fas/templates/group/view.html:24 -msgid "Not a Member" -msgstr "Nie jest członkiem" - -#: fas/templates/group/view.html:32 -msgid "Remove me" -msgstr "Usuń mnie" - -#: fas/templates/group/view.html:34 fas/templates/user/view.html:17 -msgid "(edit)" -msgstr "(edytuj)" - -#: fas/templates/group/view.html:37 fas/templates/openid/id.html:16 -msgid "Name:" -msgstr "Nazwa:" - -#: fas/templates/group/view.html:38 -msgid "Description:" -msgstr "Opis:" - -#: fas/templates/group/view.html:39 -msgid "Owner:" -msgstr "Właściciel:" - -#: fas/templates/group/view.html:40 -msgid "Type:" -msgstr "Typ:" - -#: fas/templates/group/view.html:42 fas/templates/group/view.html:46 -msgid "Yes" -msgstr "Tak" - -#: fas/templates/group/view.html:43 fas/templates/group/view.html:47 -msgid "No" -msgstr "Nie" - -#: fas/templates/group/view.html:50 -msgid "Prerequisite:" -msgstr "Wymagania wstępne:" - -#: fas/templates/group/view.html:53 -msgid "Created:" -msgstr "Utworzono:" - -#: fas/templates/group/view.html:55 -msgid "Add User:" -msgstr "Dodaj użytkownika:" - -#: fas/templates/group/view.html:71 -msgid "Members" -msgstr "Członkowie" - -#: fas/templates/group/view.html:75 fas/templates/user/list.html:27 -msgid "Username" -msgstr "Nazwa użytkownika" - -#: fas/templates/group/view.html:77 -msgid "Date Added" -msgstr "Dodano datę" - -#: fas/templates/group/view.html:78 -msgid "Date Approved" -msgstr "Data zaakceptowania" - -#: fas/templates/group/view.html:79 -msgid "Approval" -msgstr "Akceptacja" - -#: fas/templates/group/view.html:80 -msgid "Role Type" -msgstr "Typ roli" - -#: fas/templates/group/view.html:81 -msgid "Action" -msgstr "Działanie" - -#: fas/templates/group/view.html:87 fas/templates/group/view.html:90 -#: fas/templates/user/view.html:71 -msgid "None" -msgstr "Brak" - -#: fas/templates/openid/about.html:10 fas/templates/openid/trusted.html:10 -msgid "Fedora Project OpenID Provider" -msgstr "Dostawca OpenID Projektu Fedora" - -#: fas/templates/openid/id.html:11 -#, python-format -msgid "User %s" -msgstr "Użytkownik %s" - -#: fas/templates/openid/id.html:14 fas/templates/user/new.html:13 -#: fas/templates/user/resetpass.html:13 -msgid "Username:" -msgstr "Nazwa użytkownika:" - -#: fas/templates/openid/trusted.html:15 -#, python-format -msgid "Allow %s to authenticate to your OpenID identity?" -msgstr "Pozwolić %s na uwierzytelnianie tożsamości OpenID?" - -#: fas/templates/openid/trusted.html:16 -msgid "Submit" -msgstr "Wyślij" - -#: fas/templates/user/changepass.html:7 fas/templates/user/changepass.html:10 -#: fas/templates/user/changepass.html:16 fas/templates/user/verifypass.html:16 -msgid "Change Password" -msgstr "Zmień hasło" - -#: fas/templates/user/changepass.html:13 -msgid "Current Password:" -msgstr "Obecne hasło:" - -#: fas/templates/user/changepass.html:14 fas/templates/user/verifypass.html:13 -msgid "New Password:" -msgstr "Nowe hasło:" - -#: fas/templates/user/changepass.html:15 fas/templates/user/verifypass.html:14 -msgid "Confirm Password:" -msgstr "Potwierdź hasło:" - -#: fas/templates/user/edit.html:7 -msgid "Edit Account" -msgstr "Edytuj konto" - -#: fas/templates/user/edit.html:10 -#, python-format -msgid "Edit Account (%s)" -msgstr "Edytuj konto (%s)" - -#: fas/templates/user/edit.html:13 -msgid "Human Name" -msgstr "Imię i nazwisko człowieka" - -#: fas/templates/user/edit.html:19 -msgid "Email" -msgstr "E-mail" - -#: fas/templates/user/edit.html:22 fas/templates/user/view.html:24 -#, python-format -msgid "(pending change to %(email)s - cancel)" -msgstr "(oczekiwana zmiana na %(email)s - anuluj)" - -#: fas/templates/user/edit.html:31 -msgid "Telephone Number" -msgstr "Numer telefonu" - -#: fas/templates/user/edit.html:41 -msgid "IRC Nick" -msgstr "Pseudonim na IRC-u" - -#: fas/templates/user/edit.html:46 -msgid "PGP Key" -msgstr "Klucz PGP" - -#: fas/templates/user/edit.html:57 -msgid "Time Zone" -msgstr "Strefa czasowa" - -#: fas/templates/user/edit.html:75 -msgid "Comments" -msgstr "Komentarze" - -#: fas/templates/user/edit.html:81 fas/templates/user/verifyemail.html:17 -#: fas/templates/user/verifypass.html:17 -msgid "Cancel" -msgstr "Anuluj" - -#: fas/templates/user/list.html:7 -msgid "Users List" -msgstr "Lista użytkowników" - -#: fas/templates/user/list.html:13 -msgid "\"*\" is a wildcard (Ex: \"ric*\")" -msgstr "\"*\" jest wieloznacznikiem (np. \"ric*\")" - -#: fas/templates/user/list.html:38 fas/templates/user/view.html:45 -msgid "CLA Done" -msgstr "Zakończono CLA" - -#: fas/templates/user/list.html:39 fas/templates/user/view.html:46 -msgid "Not Done" -msgstr "Nie zakończono" - -#: fas/templates/user/new.html:7 fas/templates/user/new.html:10 -msgid "Sign up for a Fedora account" -msgstr "Załóż konto Fedory" - -#: fas/templates/user/new.html:17 -msgid "Full Name:" -msgstr "Imię i nazwisko:" - -#: fas/templates/user/new.html:21 fas/templates/user/resetpass.html:14 -#: fas/templates/user/view.html:22 -msgid "Email:" -msgstr "E-mail:" - -#: fas/templates/user/new.html:38 -msgid "Sign up!" -msgstr "Załóż konto!" - -#: fas/templates/user/resetpass.html:7 fas/templates/user/resetpass.html:10 -#: fas/templates/user/resetpass.html:16 fas/templates/user/verifypass.html:7 -#: fas/templates/user/verifypass.html:10 -msgid "Reset Password" -msgstr "Zresetuj hasło" - -#: fas/templates/user/resetpass.html:15 -msgid "Encrypt/Sign password reset email" -msgstr "Zaszyfruj/podpisz e-mail resetowania hasła" - -#: fas/templates/user/verifyemail.html:7 -#: fas/templates/user/verifyemail.html:10 -msgid "Confirm Email Change Request" -msgstr "Potwierdź żądanie zmiany adresu e-mail" - -#: fas/templates/user/verifyemail.html:14 -#, fuzzy, python-format -msgid "Do you really want to change your email to: %s?" -msgstr "Pomyślnie zmieniono adres e-mail na \"%s\"." - -#: fas/templates/user/verifyemail.html:16 -msgid "Confirm" -msgstr "Potwierdź" - -#: fas/templates/user/view.html:7 -msgid "View Account" -msgstr "Wyświetl konto" - -#: fas/templates/user/view.html:15 -msgid "Your Fedora Account" -msgstr "Konto Fedory" - -#: fas/templates/user/view.html:16 -#, python-format -msgid "%s's Fedora Account" -msgstr "Konto Fedory użytkownika %s" - -#: fas/templates/user/view.html:17 -msgid "Account Details" -msgstr "Szczegóły konta" - -#: fas/templates/user/view.html:20 -msgid "Account Name:" -msgstr "Nazwa konta:" - -#: fas/templates/user/view.html:21 -msgid "Real Name:" -msgstr "Imię i nazwisko:" - -#: fas/templates/user/view.html:27 -msgid "Telephone Number:" -msgstr "Numer telefonu:" - -#: fas/templates/user/view.html:28 -msgid "Postal Address:" -msgstr "Adres pocztowy:" - -#: fas/templates/user/view.html:30 -msgid "IRC Nick:" -msgstr "Pseudonim na IRC-u:" - -#: fas/templates/user/view.html:31 -msgid "PGP Key:" -msgstr "Klucz PGP:" - -#: fas/templates/user/view.html:32 -msgid "Public SSH Key:" -msgstr "Publiczny klucz SSH:" - -#: fas/templates/user/view.html:36 -msgid "Comments:" -msgstr "Komentarze:" - -#: fas/templates/user/view.html:37 -msgid "Valid" -msgstr "Prawidłowe" - -#: fas/templates/user/view.html:38 -msgid "Account Status:" -msgstr "Stan konta:" - -#: fas/templates/user/view.html:39 -msgid "Active" -msgstr "Aktywne" - -#: fas/templates/user/view.html:40 -msgid "Vacation" -msgstr "Wakacje" - -#: fas/templates/user/view.html:41 -msgid "Inactive" -msgstr "Nieaktywne" - -#: fas/templates/user/view.html:42 -msgid "Pinged" -msgstr "" - -#: fas/templates/user/view.html:44 -msgid "CLA:" -msgstr "CLA:" - -#: fas/templates/user/view.html:46 -msgid "Complete it!" -msgstr "" - -#: fas/templates/user/view.html:50 -msgid "Your Roles" -msgstr "Role" - -#: fas/templates/user/view.html:51 -#, python-format -msgid "%s's Roles" -msgstr "Role użytkownika %s" - -#: fas/templates/user/view.html:68 -msgid "Status:" -msgstr "Stan:" - -#: fas/templates/user/view.html:74 -msgid "Tools:" -msgstr "Narzędzia:" - -#: fas/templates/user/view.html:78 -msgid "Invite a New Member..." -msgstr "Zaproś nowego członka..." - -#: fas/templates/user/view.html:79 -msgid "Manage Group Membership..." -msgstr "Zarządzaj członkostwem w grupach" - -#: fas/templates/user/view.html:80 -msgid "Manage Group Details..." -msgstr "Zarządzaj szczegółami grupy..." - -#: fas/templates/user/view.html:84 -msgid "Queue:" -msgstr "Kolejka:" - -#: fas/templates/user/view.html:88 -#, fuzzy, python-format -msgid "" -"%(user)s requests approval to join %" -"(unapproved_role.group)s." -msgstr "" -"%(user)s poprosił o zaakceptowanie dołączenia do %" -"(group)s." - -#~ msgid "Sign the CLA" -#~ msgstr "Podpisz CLA" - -#~ msgid "Welcome to the Fedora Accounts System 2. Please submit bugs to " -#~ msgstr "Witaj w systemie kont Fedory 2. Prosimy zgłaszać błędy do " - -#~ msgid "Sign it!" -#~ msgstr "Podpisz!" diff --git a/fas/pybabel.conf b/fas/pybabel.conf deleted file mode 100644 index 7c7997f..0000000 --- a/fas/pybabel.conf +++ /dev/null @@ -1,16 +0,0 @@ -# Extraction from Python source files - -[python: **.py] - -# Extraction from Genshi HTML and text templates - -[genshi: **/templates/**.html] -extract_text = False -#ignore_tags = script,style -#include_attrs = alt title summary - -#[genshi: **/templates/**.txt] -#template_class = genshi.template:TextTemplate -#extract_text = False -#encoding = UTF-8 - diff --git a/fas/sample-prod.cfg b/fas/sample-prod.cfg deleted file mode 100644 index f43d783..0000000 --- a/fas/sample-prod.cfg +++ /dev/null @@ -1,86 +0,0 @@ -[global] -# This is where all of your settings go for your production environment. -# You'll copy this file over to your production server and provide it -# as a command-line option to your start script. -# Settings that are the same for both development and production -# (such as template engine, encodings, etc.) all go in -# fas/config/app.cfg - -# DATABASE - -# pick the form for your database -# sqlobject.dburi="postgres://username@hostname/databasename" -# sqlobject.dburi="mysql://username:password@hostname:port/databasename" -# sqlobject.dburi="sqlite:///file_name_and_path" - -# If you have sqlite, here's a simple default to get you started -# in development -sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" - - -# if you are using a database or table type without transactions -# (MySQL default, for example), you should turn off transactions -# by prepending notrans_ on the uri -# sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename" - -# for Windows users, sqlite URIs look like: -# sqlobject.dburi="sqlite:///drive_letter:/path/to/file" - - -# SERVER - -server.environment="production" - -# Sets the number of threads the server uses -# server.thread_pool = 1 - -# if this is part of a larger site, you can set the path -# to the TurboGears instance here -# server.webpath="" - -# session_filter.on = True - -# Set to True if you are deploying your App behind a proxy -# e.g. Apache using mod_proxy -# base_url_filter.on = False - -# Set to True if your proxy adds the x_forwarded_host header -# base_url_filter.use_x_forwarded_host = True - -# If your proxy does not add the x_forwarded_host header, set -# the following to the *public* host url. -# (Note: This will be overridden by the use_x_forwarded_host option -# if it is set to True and the proxy adds the header correctly. -# base_url_filter.base_url = "http://www.example.com" - -# Set to True if you'd like to abort execution if a controller gets an -# unexpected parameter. False by default -# tg.strict_parameters = False - -# LOGGING -# Logging configuration generally follows the style of the standard -# Python logging module configuration. Note that when specifying -# log format messages, you need to use *() for formatting variables. -# Deployment independent log configuration is in fas/config/log.cfg -[logging] - -[[handlers]] - -[[[access_out]]] -# set the filename as the first argument below -args="('server.log',)" -class='FileHandler' -level='INFO' -formatter='message_only' - -[[loggers]] -[[[fas]]] -level='ERROR' -qualname='fas' -handlers=['error_out'] - -[[[access]]] -level='INFO' -qualname='turbogears.access' -handlers=['access_out'] -propagate=0 diff --git a/fas/setup.py b/fas/setup.py deleted file mode 100644 index 54171b7..0000000 --- a/fas/setup.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/python -tt - -import os -import re -import glob -import subprocess -import shutil - -from distutils.command.build import build as _build -from distutils.command.install_data import install_data as _install_data -from distutils.dep_util import newer - -from setuptools import setup, find_packages -from turbogears.finddata import find_package_data, standard_exclude, \ - standard_exclude_directories - -execfile(os.path.join('fas', 'release.py')) - -excludeFiles = ['*.cfg.in'] -excludeFiles.extend(standard_exclude) -excludeDataDirs = ['fas/static'] -excludeDataDirs.extend(standard_exclude_directories) - -poFiles = filter(os.path.isfile, glob.glob('po/*.po')) - -SUBSTFILES = ('fas/config/app.cfg',) - -class Build(_build, object): - ''' - Build the package, changing the directories that data files are installed. - ''' - user_options = _build.user_options - user_options.extend([('install-data=', None, - 'Installation directory for data files')]) - # These are set in finalize_options() - substitutions = {'@DATADIR@': None, '@LOCALEDIR@': None} - subRE = re.compile('(' + '|'.join(substitutions.keys()) + ')+') - - def initialize_options(self): - self.install_data = None - super(Build, self).initialize_options() - - def finalize_options(self): - if self.install_data: - self.substitutions['@DATADIR@'] = self.install_data + '/fas' - self.substitutions['@LOCALEDIR@'] = self.install_data + '/locale' - else: - self.substitutions['@DATADIR@'] = '%(top_level_dir)s' - self.substitutions['@LOCALEDIR@'] = '%(top_level_dir)s/../locale' - super(Build, self).finalize_options() - - def run(self): - '''Substitute special variables for our installation locations.''' - for filename in SUBSTFILES: - # Change files to reference the data directory in the proper - # location - infile = filename + '.in' - if not os.path.exists(infile): - continue - try: - f = file(infile, 'r') - except IOError: - if not self.dry_run: - raise - f = None - outf = file(filename, 'w') - for line in f.readlines(): - matches = self.subRE.search(line) - if matches: - for pattern in self.substitutions: - line = line.replace(pattern, self.substitutions[pattern]) - outf.writelines(line) - outf.close() - f.close() - - # Make empty en.po - dirname = 'locale/' - if not os.path.isdir(dirname): - os.makedirs(dirname) - shutil.copy('po/LINGUAS', 'locale/') - - for pofile in poFiles: - # Compile PO files - lang = os.path.basename(pofile).rsplit('.', 1)[0] - dirname = 'locale/%s/LC_MESSAGES/' % lang - if not os.path.isdir(dirname): - os.makedirs(dirname) - # Hardcoded gettext domain: 'fas' - mofile = dirname + 'fas' + '.mo' - subprocess.call(['/usr/bin/msgfmt', pofile, '-o', mofile]) - super(Build, self).run() - -### FIXME: This method breaks eggs. -# Unfortunately, instead of eggs being built by putting together package *.py -# files and data sanely at the last minute, they are built by putting them -# together in the build step. This makes it extremely hard to put the -# separate pieces together in different places depending on what type of -# install we're doing. -# -# We can work around this by using package_data for static as eggs expect and -# then overriding install to install static in the correct place. -# -# Eventually someone needs to rewrite egg generation to tag files into -# separate groups (module, script, data, documentation, test) and put them -# into the final package format in the correct place. -# -# For some reason, the install-data switch also doesn't propogate to the build -# script. So if we invoke install without --skip-build the app.cfg that is -# installed is also broken. Grr.... - -class InstallData(_install_data, object): - def finalize_options(self): - '''Override to emulate setuptools in the default case. - install_data => install_dir - ''' - self.temp_lib = None - self.temp_data = None - self.temp_prefix = None - haveInstallDir = self.install_dir - self.set_undefined_options('install', - ('install_data', 'temp_data'), - ('install_lib', 'temp_lib'), - ('prefix', 'temp_prefix'), - ('root', 'root'), - ('force', 'force'), - ) - if not self.install_dir: - if self.temp_data == self.root + self.temp_prefix: - self.install_dir = os.path.join(self.temp_lib, 'fas') - else: - self.install_dir = self.temp_data - -# fas/static => /usr/share/fas/static -data_files = [('fas/static', filter(os.path.isfile, glob.glob('fas/static/*'))), - ('fas/static/css', filter(os.path.isfile, glob.glob('fas/static/css/*'))), - ('fas/static/images', filter(os.path.isfile, glob.glob('fas/static/images/*'))), - ('fas/static/images/balloons', filter(os.path.isfile, glob.glob('fas/static/images/balloons/*'))), - ('fas/static/js', filter(os.path.isfile, glob.glob('fas/static/js/*'))), -] -for langfile in filter(os.path.isfile, glob.glob('locale/*/*/*')): - data_files.append((os.path.dirname(langfile), [langfile])) - -package_data = find_package_data(where='fas', package='fas', exclude=excludeFiles, exclude_directories=excludeDataDirs,) -# Even if it doesn't exist yet, has to be in the list to be included in the build. -package_data['fas.config'].append('app.cfg') - -setup( - name=NAME, - version=VERSION, - - description=DESCRIPTION, - author=AUTHOR, - author_email=EMAIL, - url=URL, - download_url=DOWNLOAD_URL, - license=LICENSE, - - cmdclass={ - 'build': Build, - 'install_data': InstallData, - }, - install_requires = [ - 'TurboGears >= 1.0.4', - 'SQLAlchemy >= 0.4', - 'TurboMail', - 'python_fedora >= 0.2.99.2' - ], - scripts = ['client/fasClient', 'client/restricted-shell'], - zip_safe=False, - packages=find_packages(), - data_files = data_files, - package_data = package_data, - keywords = [ - # Use keywords if you'll be adding your package to the - # Python Cheeseshop - - # if this has widgets, uncomment the next line - # 'turbogears.widgets', - - # if this has a tg-admin command, uncomment the next line - # 'turbogears.command', - - # if this has identity providers, uncomment the next line - 'turbogears.identity.provider', - - # If this is a template plugin, uncomment the next line - # 'python.templating.engines', - - # If this is a full application, uncomment the next line - 'turbogears.app', - ], - classifiers = [ - 'Development Status :: 4 - Beta', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Framework :: TurboGears', - # if this is an application that you'll distribute through - # the Cheeseshop, uncomment the next line - 'Framework :: TurboGears :: Applications', - - # if this is a package that includes widgets that you'll distribute - # through the Cheeseshop, uncomment the next line - # 'Framework :: TurboGears :: Widgets', - ], - test_suite = 'nose.collector', - entry_points = { - 'console_scripts': ( - 'start-fas = fas.commands:start', - ), - 'turbogears.identity.provider': ( - 'safas3 = fas.safasprovider:SaFasIdentityProvider', - ) - } -) diff --git a/fas/start-fas b/fas/start-fas deleted file mode 100755 index e84a35b..0000000 --- a/fas/start-fas +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/python -tt -# -*- coding: utf-8 -*- -'''Start script for the fas TurboGears project. - -This script is only needed during development for running from the project -directory. When the project is installed, easy_install will create a -proper start script. -''' - -import sys -from fas.commands import start, ConfigurationError - -if __name__ == '__main__': - try: - start() - except ConfigurationError, exc: - sys.stderr.write(str(exc)) - sys.exit(1) diff --git a/fas/test.cfg b/fas/test.cfg deleted file mode 100644 index bf06ec5..0000000 --- a/fas/test.cfg +++ /dev/null @@ -1,4 +0,0 @@ -# You can place test-specific configuration options here (like test db uri, etc) - -#sqlobject.dburi = "sqlite:///:memory:" -