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

Roundup Issue Tracker: http://roundup-tracker.org/