Mercurial > p > roundup > code
comparison doc/customizing.txt @ 1292:f7d9fefcae88
Fixes for SourceForge tracker bugs.
- remember the change note on bad submissions [SF#625989]
- highlight required form fields [SF#625989]
add a couple of examples to the customisation doc too.
un-reversed the message list so first messages appear first.
fixed the messages header (post introduction of "remove")
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Mon, 21 Oct 2002 00:42:00 +0000 |
| parents | 0c0494deb09f |
| children | 1b0e91e426ea |
comparison
equal
deleted
inserted
replaced
| 1291:bf8b2380adb3 | 1292:f7d9fefcae88 |
|---|---|
| 1 =================== | 1 =================== |
| 2 Customising Roundup | 2 Customising Roundup |
| 3 =================== | 3 =================== |
| 4 | 4 |
| 5 :Version: $Revision: 1.58 $ | 5 :Version: $Revision: 1.59 $ |
| 6 | 6 |
| 7 .. This document borrows from the ZopeBook section on ZPT. The original is at: | 7 .. This document borrows from the ZopeBook section on ZPT. The original is at: |
| 8 http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx | 8 http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx |
| 9 | 9 |
| 10 .. contents:: | 10 .. contents:: |
| 2577 | 2577 |
| 2578 8. If you're using a persistent web server - roundup-server or mod_python for | 2578 8. If you're using a persistent web server - roundup-server or mod_python for |
| 2579 example - then you'll need to restart that to pick up the code changes. | 2579 example - then you'll need to restart that to pick up the code changes. |
| 2580 When that's done, you'll be able to use the new time logging interface. | 2580 When that's done, you'll be able to use the new time logging interface. |
| 2581 | 2581 |
| 2582 Using a UN*X passwd file as the user database | |
| 2583 --------------------------------------------- | |
| 2584 | |
| 2585 On some systems, the primary store of users is the UN*X passwd file. It holds | |
| 2586 information on users such as their username, real name, password and primary | |
| 2587 user group. | |
| 2588 | |
| 2589 Roundup can use this store as its primary source of user information, but it | |
| 2590 needs additional information too - email address(es), roundup Roles, vacation | |
| 2591 flags, roundup hyperdb item ids, etc. Also, "retired" users must still exist | |
| 2592 in the user database, unlike some passwd files in which the users are removed | |
| 2593 when they no longer have access to a system. | |
| 2594 | |
| 2595 To make use of the passwd file, we therefore synchronise between the two user | |
| 2596 stores. We also use the passwd file to validate the user logins, as described | |
| 2597 in the previous example, `using an external password validation source`_. We | |
| 2598 keep the users lists in sync using a fairly simple script that runs once a | |
| 2599 day, or several times an hour if more immediate access is needed. In short, it: | |
| 2600 | |
| 2601 1. parses the passwd file, finding usernames, passwords and real names, | |
| 2602 2. compares that list to the current roundup user list: | |
| 2603 a. entries no longer in the passwd file are *retired* | |
| 2604 b. entries with mismatching real names are *updated* | |
| 2605 a. entries only exist in the passwd file are *created* | |
| 2606 3. send an email to administrators to let them know what's been done. | |
| 2607 | |
| 2608 The retiring and updating are simple operations, requiring only a call to | |
| 2609 ``retire()`` or ``set()``. The creation operation requires more information | |
| 2610 though - the user's email address and their roundup Roles. We're going to | |
| 2611 assume that the user's email address is the same as their login name, so we | |
| 2612 just append the domain name to that. The Roles are determined using the | |
| 2613 passwd group identifier - mapping their UN*X group to an appropriate set of | |
| 2614 Roles. | |
| 2615 | |
| 2616 The script to perform all this, broken up into its main components, is as | |
| 2617 follows. Firstly, we import the necessary modules and open the tracker we're | |
| 2618 to work on:: | |
| 2619 | |
| 2620 import sys, os, smtplib | |
| 2621 from roundup import instance, date | |
| 2622 | |
| 2623 # open the tracker | |
| 2624 tracker_home = sys.argv[1] | |
| 2625 tracker = instance.open(tracker_home) | |
| 2626 | |
| 2627 Next we read in the *passwd* file from the tracker home:: | |
| 2628 | |
| 2629 # read in the users | |
| 2630 file = os.path.join(tracker_home, 'users.passwd') | |
| 2631 users = [x.strip().split(':') for x in open(file).readlines()] | |
| 2632 | |
| 2633 Handle special users (those to ignore in the file, and those who don't appear | |
| 2634 in the file):: | |
| 2635 | |
| 2636 # users to not keep ever, pre-load with the users I know aren't | |
| 2637 # "real" users | |
| 2638 ignore = ['ekmmon', 'bfast', 'csrmail'] | |
| 2639 | |
| 2640 # users to keep - pre-load with the roundup-specific users | |
| 2641 keep = ['comment_pool', 'network_pool', 'admin', 'dev-team', 'cs_pool', | |
| 2642 'anonymous', 'system_pool', 'automated'] | |
| 2643 | |
| 2644 Now we map the UN*X group numbers to the Roles that users should have:: | |
| 2645 | |
| 2646 roles = { | |
| 2647 '501': 'User,Tech', # tech | |
| 2648 '502': 'User', # finance | |
| 2649 '503': 'User,CSR', # customer service reps | |
| 2650 '504': 'User', # sales | |
| 2651 '505': 'User', # marketing | |
| 2652 } | |
| 2653 | |
| 2654 Now we do all the work. Note that the body of the script (where we have the | |
| 2655 tracker database open) is wrapped in a ``try`` / ``finally`` clause, so that | |
| 2656 we always close the database cleanly when we're finished. So, we now do all | |
| 2657 the work:: | |
| 2658 | |
| 2659 # open the database | |
| 2660 db = tracker.open('admin') | |
| 2661 try: | |
| 2662 # store away messages to send to the tracker admins | |
| 2663 msg = [] | |
| 2664 | |
| 2665 # loop over the users list read in from the passwd file | |
| 2666 for user,passw,uid,gid,real,home,shell in users: | |
| 2667 if user in ignore: | |
| 2668 # this user shouldn't appear in our tracker | |
| 2669 continue | |
| 2670 keep.append(user) | |
| 2671 try: | |
| 2672 # see if the user exists in the tracker | |
| 2673 uid = db.user.lookup(user) | |
| 2674 | |
| 2675 # yes, they do - now check the real name for correctness | |
| 2676 if real != db.user.get(uid, 'realname'): | |
| 2677 db.user.set(uid, realname=real) | |
| 2678 msg.append('FIX %s - %s'%(user, real)) | |
| 2679 except KeyError: | |
| 2680 # nope, the user doesn't exist | |
| 2681 db.user.create(username=user, realname=real, | |
| 2682 address='%s@ekit-inc.com'%user, roles=roles[gid]) | |
| 2683 msg.append('ADD %s - %s (%s)'%(user, real, roles[gid])) | |
| 2684 | |
| 2685 # now check that all the users in the tracker are also in our "keep" | |
| 2686 # list - retire those who aren't | |
| 2687 for uid in db.user.list(): | |
| 2688 user = db.user.get(uid, 'username') | |
| 2689 if user not in keep: | |
| 2690 db.user.retire(uid) | |
| 2691 msg.append('RET %s'%user) | |
| 2692 | |
| 2693 # if we did work, then send email to the tracker admins | |
| 2694 if msg: | |
| 2695 # create the email | |
| 2696 msg = '''Subject: %s user database maintenance | |
| 2697 | |
| 2698 %s | |
| 2699 '''%(db.config.TRACKER_NAME, '\n'.join(msg)) | |
| 2700 | |
| 2701 # send the email | |
| 2702 smtp = smtplib.SMTP(db.config.MAILHOST) | |
| 2703 addr = db.config.ADMIN_EMAIL | |
| 2704 smtp.sendmail(addr, addr, msg) | |
| 2705 | |
| 2706 # now we're done - commit the changes | |
| 2707 db.commit() | |
| 2708 finally: | |
| 2709 # always close the database cleanly | |
| 2710 db.close() | |
| 2711 | |
| 2712 And that's it! | |
| 2713 | |
| 2714 | |
| 2715 Enabling display of either message summaries or the entire messages | |
| 2716 ------------------------------------------------------------------- | |
| 2717 | |
| 2718 This is pretty simple - all we need to do is copy the code from the example | |
| 2719 `displaying entire message contents in the issue display`_ into our template | |
| 2720 alongside the summary display, and then introduce a switch that shows either | |
| 2721 one or the other. We'll use a new form variable, ``:whole_messages`` to | |
| 2722 achieve this:: | |
| 2723 | |
| 2724 <table class="messages" tal:condition="context/messages"> | |
| 2725 <tal:block tal:condition="not:request/form/:whole_messages/value | python:0"> | |
| 2726 <tr><th colspan=3 class="header">Messages</th> | |
| 2727 <th colspan=2 class="header"> | |
| 2728 <a href="?:whole_messages=yes">show entire messages</a> | |
| 2729 </th> | |
| 2730 </tr> | |
| 2731 <tr tal:repeat="msg context/messages"> | |
| 2732 <td><a tal:attributes="href string:msg${msg/id}" | |
| 2733 tal:content="string:msg${msg/id}"></a></td> | |
| 2734 <td tal:content="msg/author">author</td> | |
| 2735 <td nowrap tal:content="msg/date/pretty">date</td> | |
| 2736 <td tal:content="msg/summary">summary</td> | |
| 2737 <td> | |
| 2738 <a tal:attributes="href string:?:remove:messages=${msg/id}&:action=edit">remove</a> | |
| 2739 </td> | |
| 2740 </tr> | |
| 2741 </tal:block> | |
| 2742 | |
| 2743 <tal:block tal:condition="request/form/:whole_messages/value | python:0"> | |
| 2744 <tr><th colspan=2 class="header">Messages</th> | |
| 2745 <th class="header"><a href="?:whole_messages=">show only summaries</a></th> | |
| 2746 </tr> | |
| 2747 <tal:block tal:repeat="msg context/messages"> | |
| 2748 <tr> | |
| 2749 <th tal:content="msg/author">author</th> | |
| 2750 <th nowrap tal:content="msg/date/pretty">date</th> | |
| 2751 <th style="text-align: right"> | |
| 2752 (<a tal:attributes="href string:?:remove:messages=${msg/id}&:action=edit">remove</a>) | |
| 2753 </th> | |
| 2754 </tr> | |
| 2755 <tr><td colspan=3 tal:content="msg/content"></td></tr> | |
| 2756 </tal:block> | |
| 2757 </tal:block> | |
| 2758 </table> | |
| 2759 | |
| 2582 | 2760 |
| 2583 ------------------- | 2761 ------------------- |
| 2584 | 2762 |
| 2585 Back to `Table of Contents`_ | 2763 Back to `Table of Contents`_ |
| 2586 | 2764 |
