Intranet Module

part of the ArsDigita Community System
by a lot of people at ArsDigita and maintained by Michael Bryzek.

The Big Picture

The faster the pace of change in an industry or business, the more time and effort workers have to spend on coordination. Consider Adam Smith's pin factory. The boss has to stand up in front of the workers once per year and say "Thank you for your fine work last year. This year, I want you to make lots of pins. They should be really straight, blunt on one end, and pointy on the other. See you next year." In a university literature department, you need to have a meeting once per semester. Perhaps some of the curriculum should be revised? In a business facing innovative competitors, you need to have a meeting once every month to think about required responses. In a complex organization that is try to change and adapt, meetings and other forms of coordination may consume 20-30 hours per week.

Why is this a problem? People still have to work 40 hours per week to get anything done. The result is that people will have to spend 60-70 hours per week at the office in order to coordinate and work.

What's the solution to this social problem? A computer program of course! (You didn't expect anything better from three MIT graduates did you?)

A modern organization exhibits the classical problem at which Web-based applications excel: people separated in time and space. We can thus use the same toolkit that we developed for helping people work together and teach each other over the Internet to work together within an organization.

For What Kinds of Organizations Is This Best?

What kinds of organizations can get the most out of this toolkit? Ones just like ArsDigita, of course! We built this for ourselves. ArsDigita currently (May 2000) has approximately ten offices, 140 busy highly-paid people, and rapid growth (revenue doubling every six months). Coincidentally, it also works great for groups within larger companies. Consider Jane Manager at Hewlett-Packard who is forming a team to build a new product. Within a couple of weeks, she might be managing 100 people spread out among four physical locations in Japan, California, the Pacific Northwest, and Spain. That's much faster organizational growth and change than any startup company will ever experience. It would be awfully nice if Jane could go up to a Web browser and ask "Who works for me today? What are their names? What do they look like? How do I get in touch with them?"

The Medium-Size Picture

We assume that all the employees are users in a standard ArsDigita Community System. We keep track of employees, customers, project, salaries, and deadlines. This enables us to ask questions like For companies that operate an Internet service based on our toolkit, a side benefit of using our intranet module is that employees who develop skills in maintaining the intranet can then apply those skills to the public Web services. Novices have to start out somewhere and they might as well start in a place where an error isn't observed by customers.

One of the key components to any intranet is keeping members of the company up-to-date. The intranet makes it easy to spam the entire company, a specific office, or employees working on a given project.

Daily Status Report

The intranet supports a daily status report that is sent to every employee in the company, on a nightly basis. Any module in the acs can add something to this status report by:
  1. Creating a procedure that generates the content to include in the status report. This procedure takes 4 arguments: database handle (db), number of days the report covers (coverage, defaults to 1), the date of the report (report_date, defaults to sysdate), and the purpose of the report (purpose, generally defaults to email, but can be either web_display or email_display.


    proc im_news_status {db {coverage ""} {report_date ""} {purpose ""} } {
        if { [empty_string_p $coverage] } {
    	set coverage 1
        if { [empty_string_p $report_date] } {
    	set report_date sysdate
        } else {
    	set report_date "'$report_date'"
        set since_when [db_string unused "select to_date($report_date, 'YYYY-MM-DD') - $coverage from dual"]
        return [news_new_stuff $since_when "f" $purpose]

  2. Use im_add_to_status_report to register the name of the procedure and caching information with the intranet status report:
    proc_doc im_add_to_status_report { title proc_name { cache_p "f" } } {
        Adds proc_name to the list of procs needed to generete the
        status_report. If cache_p is t, then we cache the result to reuse
        later (like when we're sending the status report to all employees). 
        You want to cache all information that does not depend on the
        current user id.
    } {
        ns_share im_status_report_section_list
        if { ![info exists im_status_report_section_list] || \
    	    [lsearch -glob $im_status_report_section_list $proc_name] == -1 } {
    	lappend im_status_report_section_list [list $title $proc_name $cache_p]
    # Register the procedure im_news_status with the intranet status report. We can cache
    # this information because it does not change based on the user id
    im_add_to_status_report "News" im_news_status t

Under the Hood

The parameters/ad.ini file

; Log email sent to groups as correspondance and enable auto-emailing of
; these correspondance to members of the group
; Use the intranet filters to restrict access to all bboard topic with a group_id
; Set to 1 if you want to automatically redirect users to enter project reports
; If you want users to automatically be redirected to log their hours
; if they haven't, set this flag to 1
; How many results/rows do we show on a page before splitting up into 
; previous/next links?
; On pages with dimensional sliders, having too many categories leads to 
; messy navigation. How many text characters do we allow before switching 
; from text links to a select bar?
; If you want to include a default graphic in your
; intranet header, put the path to that graphic here 
; If you would like to track projects with a
; survey in the survey module, put the project
; type and its paired survey
ProjectReportTypeSurveyNamePair=Client - full service,project_report
;You may send email notifications of payment
; changes to members of a specified group
IntranetName=aD Intranet
; If you want to restrict access to the /shared/community-member
; page to users of the intranet only, set KeepSharedInfoPrivate to 1
; Should we enable bboard with the intranet pages? The BBoard
; module must be scoped or be altered to have a groud_id column first
; do we serve the generated /intranet/index page or something 
; else from the file system?
; the unit of measurement for entering salaries (month or year)
; used to display salary
; list of fee types
FeeTypes="setup" "monthly development" "monthly hosting" "hourly" "stock"
; Do we want to track hours?
; what's the url stub? i.e.
; Group Types
CustomerPortalName=Customer Portals
; What color do we put in the header row of tables?
; What color do we put in the odd numbered rows of tables?
; What color do we put in the even numbered rows of tables?
; If we want to include people who are delinquent with project 
; reports in our status-report, what user_class_id do we use?
; Leave blank to skip this part of the status report

Categories of users

There are several different categories of users in intranet-enabled acs installations:

User Groups

The intranet module defines several standard user groups which can be used to model your organization. The intranet is a group_type, and each category of users is its own user_group of type intranet. Within each group, you can have subgroups if there is a need. One example is offices. There is a user group named Offices, and each office is a subgroup of the Office group. This way, we can keep the large number of objects organized in a hierarchical way.

Intranet administrators belong to the "Intranet Administrators" subgroup of the standard ACS Administration group. All other intranet groups have a group_type of intranet. The following groups are automatically created:

Top-level pages /intranet

Top-level files include the employee's workspace and pages that are commonly reused by intranet submodules. This includes: The following parameters can be used to modify the general behavior of the intranet module:

Categories in the intranet

The intranet module uses the categories table, originally created for user profiling, to categorize attributes of the various intranet objects. For instance, the list of possible job titles that an employee may have will vary between different organizations. Each job title is an entry in the categories table with category_type='Intranet Job Title'. The /admin/intranet page lists each of the intranet categories, and allows maintenance of their possible values. This flexibility allows the intranet data model to adapt to your company's standard practices and workflow.

Employees /intranet/employees

The employees section of the intranet is used to manage the employees in the company. Employees can search for others in the company, can spam all company employees, can download AIM lists to load into instant messenger, and can view a dynamically generated company org chart. Employees can also be categorized/seen by office or team.

Every employee is a member of the employees user_group. Detailed contact information is stored for users in order to provide a company directory. The im_employee_info table holds information like the employee's salary and Social Security number. When a new person is hired, you can add them to the employees group by choosing "Add an employee" from the intranet admin screen. This gives them access to the intranet portal.

Making the recruiting process visible

The im_employee_pipeline table allows recruiters to keep track of applicants, interviews, and hiring status. From the employee administration page, summaries of current candidates as well as employee referrals are available.

Work absences /intranet/absences Users record their work absences (e.g. vacations, travel, personal time-off) providing a central resource to see where all employees are. Users can describe why they are taking time off and list emergency contact information.

Employee administration /intranet/employees/admin

Employee administration consists of:

Customers /intranet/customers

Customers in the intranet maintain basic information, such as the type of customer, and are associated with address books and projects. From the customers page, you can also see everyone who works with that customer. We also store all correspondance with the customer through the general comments module. Like projects, customers can be categorized into types or states, stored in the categories table.

The intranet module also provides easy integration with the bboard module to create and link bboard topics to each customer. Note that you should probably enable the EnableIntranetBBoardSecurityFiltersP=1 parameter to register a filter that prevents unauthorized access to the intranet bboards.

Employees who work for a given customer can be automatically redirected to a Customer Portal (identified by the CustomerPortalName parameter)

Projects /intranet/projects

The projects module is probably the most useful place for employees. Typical projects include Each project maintains basic information about the project (a description, the project lead, the supervisor, etc.), tracks project reports (in the form of general comments and/or surveys), and basic categorization to help you organize the lists of projects. We also track project payments and allocations over time. Projects can also be categorized by type (e.g. client/internal) or state (e.g. open/closed).

We currently support one level of subprojects allowing users to break up a large project if needed. Each intranet project can be associated with a ticket tracker project. The idea here is to use projects in the intranet as a way of managing information about the projects - employee allocations, payments, people involved, etc. - and use the ticket tracker to actually manage work on the project.

Project reports

The intranet module currently supports two types of reports:

If set, the ForceUsersToEnterProjectReportsP parameter will automatically redirects users who are late with a project report to the forms they need to fill in. If desired, this feature can be enabled to intrusively remind employees to fill out late reports.

Tracking Hours

All hours logged on any project are records in the im_hours table using the generic on_which_table and on_what_id structure. This allows us to extend hour tracking in the future to the ticket tracker or other tools that employees use throughout the work day, recording hours in the same table and thus simplifying hour reporting. Note that the TrackHours parameter should be enabled to prompt users to log their hours.

If set, the ForceUsersToLogHoursP parameter will automatically redirect users to the log hours screen for yesterday if they have not logged their hours in the past day.


Facilities represent physical locations including offices and company homes, such as extravigant Cape Cod beach houses. Each facility stores basic information, including its location, directions, and primary contact.

An office is fundamentally a business unit to which employees are assigned. The main difference between an office and any other user group is that an office is associated with a particular facility.

Company houses are another special type of facility, containing rooms and beds that can be reserved. Most company house serves a particular office location, providing temporary accomodations for visiting ArsDigitans. Some company houses, such as special retreat facilities, however, aren't associated with any particular office.

Partners /intranet/partners

Company partners, such as strategic consultants, graphics design firms, etc., are each subgroups of the Partners user group. Partners are similar to customers in that they have their own address books, correspondance history, and list of employees engaged with the partner. Partners can also be categorized through the categories table.

Allocations /intranet/allocations

You can use allocations to assign a percentage of an employee's time to a specific project. This helps you to keep track of what each employee is working on and to ensure that all employees are properly allocated.


You can set up qmail on your box to receive emails sent to your server and automatically log them with a corresponding project, customer, partner, or random user group. The idea is to send mail to the user_groups.short_name@somedomain, and associate the emails with the user_groups. This works because the short_name is unique. Here's how it works (very briefly):
  1. Point a domain (like to the box running your intranet
  2. Install qmail and pipe all mail to to a perl script that stuffs the mail into your database. The perl scripts to do this come with acs.
  3. Use these "aliases": (the actual processing is handled through email-handler-sweeper-procs)
  4. Finally, be sure to set the LogEmailToGroupsP parameter to tell the email-handler to process the group-based email!

In the future, we'd like to create mailing lists for the groups and aliases rather than use ns_sendmail for all members of the group.


The im_procedures table can be used to record company procedures and the people certified to do them. Anyone can add a procedure, and they're allowed to pick the first person certified for that procedure. Anyone certified in procedure X can certify anyone else to do that procedure. (That person can also add restrictions like only certified on HPUX) Certified users should record times they supervised a non-certified user doing a procedure, so we can use rules like A user can be certified after completing the procedure 5 times under the supervision of a certified user. Each procedure is a subgroup of the Procedures user group.


Process and process tracking is vital to make sure that a company runs well-oiled. For each process, we need to record the process details, which department is involved, and the primary owner. The primary owner is in charge of auditing the process periodically to make sure the description is correct and that the process is happening as planned.
create table im_processes  (
    im_process_id       integer primary key,
    -- name of the basic process
    process_name        varchar(100) not null,
    -- department that runs this process
    department_id       integer references categories(category_id),
    description         clob,
    primary_owner_id    integer references users(user_id)
    -- processes should be audited by the primary owner
    -- we record when this process was last audited
    last_audit          date,
    -- when will we check on the health of this process?
    next_audit		date

Simple processes might only involved the owners. For those that involved more people, we keep track of this users involved in this process and their role using the standard user group tables.

For example, take a process "hiring pipeline". Roles would be

Reports /intranet/reports

General reports about the intranet broken down by office, team, data quality, employee information, etc.