Interchange Documentation (Full) ================================ icfull.1.2 This is the documentation for Interchange, all in one large file. Interchange Advanced Topics Interchange Catalog-Building Tutorial Interchange Configuration Interchange Database Interchange Ecommerce Functions Interchange Foundation Store Interchange I18N Features Interchange Templates Interchange Tags Reference Interchange Frequently Asked Questions Interchange Upgrade Guide Interchange Programmer Variables Reference line: Interchange Ecommerce Functions =============================== 1. THE ORDER PROCESS ==================== Interchange has a completely flexible order basket and checkout scheme. The foundation demo presents a common use of this process, in the directory pages/ord -- the files are: basket.html The order basket displayed by default checkout.html The form where the customer enters their billing and shipping info and in the directory etc: receipt.html The receipt displayed to the customer report The order report mailed to you mail_receipt The customer's email copy (if requested) It is not strictly necessary to display an order basket when an item is ordered. If you specify a different page to be displayed that is fine, but most customers will be confused if you don't give them an indication that the order operation has succeeded. Any order basket is an HTML FORM. It will have a number of variables on it. At the minimum it must have an [item-list] to loop through the items, and the quantity of each item must be set in some place on that form. Any valid Interchange tags may be used on the page, and you may use multiple item lists if necessary. 1.1. How to order an item ------------------------- Interchange can either use a form-based order or a link-based order to place an item in the shopping cart. The link-based order uses the special [order item-code] tag: [order code] named attributes: [order code="sku" quantity="n"* href="page"* cart="cartname"* base="table"*] * = optional parameters Expands into a hypertext link which will include the specified code in the list of products to order and display the order page. code should be a product SKU listed in one of the "products" tables, and is the only required parameter. quantity may be specified if more than one (the default) of the item should be placed in the cart. href allows some page other than the default order page to be displayed once the item has been added to the cart. cart selects the shopping cart the item will be placed in. The optional argument base constrains the order to a particular products file -- if not specified, all tables defined as products files will be searched in sequence for the item. Example: Order a [order TK112]Toaster today. Note that this is the same as: Order a [page order TK112]Toaster today. You can change frames for the order with: Order a Toaster today. [/order] Expands into . May be used to give the order tag the appearance of being a container tag, but neither necessary nor recommended. To order with a form, you set the form variable mv_order_item to the item-code/SKU and use the refresh action:
Order toaster
You may batch select whole groups of items:
Standard Toaster Super Toaster
Items that have a quantity of zero (or blank) will be skipped, and only items with a positive quantity will be placed in the basket. You may also specify attributes like size or color at time of order (see How to set up an order button). 1.2. How to set up an order link -------------------------------- On a product display page, use: [order 00-0011]Order the Mona Lisa If coming from a search results or on-the-fly page, you may use the generated [item-code] thusly: [order [item-code]]Order [item-field name] Bear in mind that if you have not reached the page via a search or on-the-fly operation, [item-code] means nothing and will cause an error. 1.3. How to set up an order button ---------------------------------- Interchange can order via form submission as well. This allows you to order a product (or group of products) via a form button. In its simplest form, it is:
The default quantity is one. An initial quantity may be set by the user by adding an mv_order_quantity variable: Number to order: You can order multiple items by stacking the variables:
Initial size or color may be set as well, provided UseModifier is set up properly: If the order is coming from a generated flypage, loop list, or search results page, you can get a canned select box from the [item-accessories size] or [item-accessories size] tag. See Item Attributes. 1.4. How to set up an on-the-fly item ------------------------------------- If you enable the catalog directive OnFly, setting it to the name of a subroutine (or possibly a UserTag) that can handle its calls, then Interchange will add items to the basket that are not in the product database. Interchange supplies an internal onfly subroutine, which will work according to the examples given below. In catalog.cfg: OnFly onfly If your item code is not to be named mv_order_item then you must perform a rename in the Autoload routine. A basic link can be generated like: Order item 000101 The form parameter value mv_order_fly can contain any number of fields which will set corresponding parameters in the item attributes. The fields are separated by the pipe (|) character and contain value-parameter pairs separated by an = sign. (These are URL-encoded by the [area ...] or [page ...] tag, of course.) You can set a size, color, or any other parameter. The special attribute mv_price can be used in conjunction with the CommonAdjust atom $ to set the price for checkout and display. The [item-list] sub-tag [item-description], when used with an item-list, will use the item attribute description to display in the basket. Note that [item-field description] or [item-data products description] will NOT work, as both of these tags reference an actual field value for a record in the products table - not applicable for on-the-fly items. Similarly, an attempt to generate a flypage for an on-the-fly item ([page 000101], for example) will fail, resulting in the display of the SpecialPage missing. If you wish to set up a UserTag to process on-the-fly items, it should accept a call of usertag(mv_item_code, mv_item_quantity, mv_order_fly) The mv_item_code and mv_order_fly parameters are required to trigger Interchange's add_item routine (along with mv_todo=refresh to set the action). The item will always act as if SeparateItems or mv_separate_items is set. Multiple items can be ordered at once by stacking the variables. If there is only one mv_order_item instance, however, you can stack the mv_order_fly variable so that all are concatenated together as with the | symbol. So the above example could be done as: [area form=" mv_todo=refresh mv_order_item=000101 mv_order_fly=description=An on-the-fly item mv_order_fly=price=100.00 "] Multiple items would need multiple instances of mv_order_item with a corresponding mv_order_fly for each mv_order_item. You can order both 000101 and 000101 as follows: [area form=" mv_todo=refresh mv_order_item=000101 mv_order_fly=description=An on-the-fly item|price=100.00 mv_order_item=000102 mv_order_fly=description=Another on-the-fly item|price=200.00 "] The following two forms correspond to the above two examples, in order, with the slight refinement of adding a quantity:
Qty:
Qty:
Qty:
1.5. Order Groups ----------------- Interchange allows you to group items together, making a master item and sub-items. This can be used to delete accessories or options when the master item is deleted. In its simplest form, you order just one master item and all subsequent items are sub-items.
If you wish to stack more than one master item, then you must define mv_order_group for all items, with either a 1 value (master) or 0 value (sub-item). A master owns all subsequent sub-items until the next master is defined.
When the master item 00-0011 is deleted from the basket, 00-0011a will be deleted as well. And when 19-202 is deleted, then 99-102 will be deleted from the basket. NOTE: Use of checkboxes for this type of thing can be hazardous, as they do not pass a value when unchecked. It is preferable to use radio groups or select/drop-down widgets. If you must use checkboxes, be sure to explicitly clear mv_order_group and mv_order_item somewhere on the page which contains the form: [value name=mv_order_group set=''] [value name=mv_order_item set=''] The attributes mv_mi and mv_si are set to the group and sub-item status of each item. The group, contained in the attribute mv_mi, is a meaningless yet unique integer. All items in a group will have the same value of mv_mi. The attribute mv_si is set to 0 if the item is a master item, and 1 if it is a sub-item. 1.6. Basket display ------------------- The basket page(s) are where the items are tracked and adjusted by the customer. It is possible to have an unlimited number of basket pages. It is also possible to have multiple shopping carts, as in buy or sell. This allows a basket/checkout type of ordering scheme, with custom order pages for items which have many accessories. The name of the page to display can be configured in several ways: 1. Set the SpecialPage order to the page to display when an item is ordered. 2. Use the [order code=item page=page_name] Order it! form of order tag to specify an arbitrary order page for an item. 3. If already on an order page, set the mv_orderpage, mv_nextpage, mv_successpage, or mv_failpage variables. The following variables can be used to control cart selection and page display: mv_cartname The shopping cart (default is main) to be used for this order operation. mv_failpage Page to be displayed on a failed order check (see Advanced Multi-level Order Pages) mv_nextpage Page to display on a return operation. mv_orderpage Page to be displayed on a refresh. mv_successpage Page to be displayed on a successful order check (see Advanced Multi-level Order Pages). mv_order_profile Order profile to be used if the form action is submit (see Advanced Multi-level Order Pages). 1.7. Multiple Shopping Carts ---------------------------- Interchange allows you to define and maintain multiple shopping carts. One shopping cart -- main, by name -- is defined when the user session starts. If the user orders item M1212 with the following tag: [order code=M1212 cart=layaway] Order this item! the order will be placed in the cart named layaway. However, by default you won't see the just-ordered item on the basket page. That is because the default shopping basket displays the contents of the 'main' cart only. So copy the default basket page (pages/ord/basket.html in the demo) to a new file, insert a [cart layaway] tag, and specify it as the target page in your [order] tag: [order code=M1212 cart=layaway page=ord/lay_basket] Order this item! Now the contents of the layaway cart will be displayed. Most of the ITL tags that are fundamental to cart display accept a 'cartname' option, allowing you to specify which cart to be used: [cart cartname] A 'sticky' setting of the default cart name to use for all subsequent cart-related tags. Convenient, but you must remember to use [cart main] to get back to the primary cart! As an alternative, you can specify the desired cart as a parameter of the other tags. These are not sticky, referencing the specified cart only for the instance in which they are called: [item-list cartname]...[/item-list] Iterates over the items in the specified cart - tags like [item-quantity] and [item-price] will be evaluated accordingly; [nitems cartname] Returns the total number of items in the specified cart; [subtotal cartname] Returns the monetary subtotal for the contents of specified cart; [shipping cartname], [handling cartname], [salestax cartname], [total-cost cartname] You get the idea. It is worth noting that tags which summarize cart contents do not need to be in used concert, or in conjunction with an [item-list]. For instance, you can display just the grand total for a cart on the sidebar or bottom of each page, using [total-cost] by itself, if you wish. You can also order items from a form, using the mv_order_item, mv_cartname, and optional mv_order_quantity variables.
Item M3243 Quantity
If you need to utilize an alternative item price in conjunction with the use of a custom cart, see the section on PRODUCT PRICING for pricing methods and strategies. 2. PRODUCT PRICING ================== Interchange maintains a price in its database for every product. The price field is the one required field in the product database -- it is necessary to build the price routines. For speed, Interchange builds the code that is used to determine a product's price at catalog configuration time. If you choose to change a directive that affects product pricing you must reconfigure the catalog. 2.1. Simple pricing ------------------- The simplest method is flat pricing based on a fixed value in the products database. If you put that price in a field named price, you don't need to do more. If you want to change pricing based on quantity, size, color or other factors read on. 2.2. Price Maintenance with CommonAdjust ---------------------------------------- A flexible chained pricing scheme is available when the CommonAdjust directive is set. We talk below about a CommonAdjust string; it will be defined in due time. A few rules about CommonAdjust, all assuming the PriceField directive is set to price: 1 If CommonAdjust is set to any value, a valid CommonAdjust string or not, extended price adjustments are enabled. It may also hold the default pricing scheme. 2 The price field may also hold a CommonAdjust string. It takes precedence over the default. 3 If the value of the CommonAdjust directive is set to a CommonAdjust string, and the price field is empty or specifically 0, then it will be used to set the price of the items. 4 If no CommonAdjust strings are found, then the price will be 0, subject to any later application of discounts. 5 If another CommonAdjust string is found as the result of an operation, it will be re-parsed and the result applied. Chaining is retained; a fallback may be passed and will take effect. Prices may be adjusted in several ways, and the individual actions are referred to below as atoms. Price atoms may be final, chained, or fallback. A final price atom is always applied if it does not evaluate to zero. A chained price atom is subject to further adjustment. A fallback price atom is skipped if a previous chained price was not zero. Atoms are separated by whitespace, and may be quoted (although there should not normally be whitespace in a setting). A chained item ends with a comma. A fallback item has a leading semi-colon. Final atoms have no comma appended or semi-colon prepended. A settor is the means by which the price is set. There are eight different types of price settors. All settors can then yield another CommonAdjust string. It is quite possible to create endless loops, so the maximum number of initial CommonAdjust strings is set to 16, and there may be only 32 iterations by default before the price will return zero on an error. (The maximum iterations is specified with the Limit directive.) NOTE: Common needs are easily shown but not so easily explained; skip to the examples in the reference below if your vision starts to blur when reading the next section. 8-) USAGE: Optional items below have asterisks appended. The asterisk should not be used in the actual string. Optional base or table always defaults to the active products database table. The optional key defaults to the item code except in a special case for the attribute-based lookup. The field name is not optional except in the case of an attribute lookup. N.NN or -N.NN where N is a digit. A number which is applied directly; for instance 10 will yield a price of 10. May be a positive or negative number. N.NN% where N is a digit. A number which is applied as a percentage of the current price value. May be a positive or negative number. For example, if the price is 10 and -8% is applied, the next price value will be 9.20. table*:column:key* Causes a straight lookup in a database table. The optional table defaults to the main products database table for the item (subject of course to multiple product files). The column must always be present. The optional key defaults to the item code except in a special case for the attribute-based lookup. The return value is then re-parsed as another price settor. table*:col1..col5,col10:key* Causes a quantity lookup in database table table (which defaults to the products database), with a set of comma-separated fields, looked up by the optional key. (Key defaults to the item code, of course). If ranges are specified with .., each column in the sequence will be used; Therefore pricing:p1,p2,p3,p4,p5,p10: is the same as pricing:p1..p5,p10: Leading non-digits are stripped, and the item quantity is compared with the numerical portion of the column name. The price is set to the value of the database column (numeric portion) that is at least equal to it but doesn't yet reach the next break. WARNING: If the field at the appropriate quantity level is blank, a zero cost will be returned from the atom. It is important to have all columns populated. ==attribute:table*:column*:key* Does an attribute-based adjustment. The attribute is looked up in the database table, with the optional column defaulting to the same name as the value of the attribute. If the column is not left blank, the key is set to the value of the attribute if blank. & CODE The leading & sign is stripped and the code is passed to the equivalent of a [calc] tag. No Interchange tags can be used, but the &tag_data routine is available, the current value of the price and quantity are available as $s, and the current item (code, quantity, price, and any attributes) are available as $item, all forced to the package Vend::Interpolate. That means that in a UserTag: $Vend::Interpolate::item is the current item $Vend::Interpolate::item->{code} gives key for current item $Vend::Interpolate::item->{size} gives size for current item (if there) $Vend::Interpolate::item->{mv_ib} gives database ordered from [valid Interchange tags] If the settor begins with a square bracket ([) or underscore, it is parsed for Interchange tags with variable substitution (but no Locale substitution). You may define a price in a Variable in this fashion. The string is re-submitted as an atom, so it may yield yet another settor. $ Tells Interchange to look in the mv_price attribute of the shopping cart, and apply that price as the final price, if it exists. The attribute must be a numerical value. >>word Tells the routine to return word directly as the result. This is not useful in pricing, as it will evaluate to zero. But when CommonAdjust is used for shipping, it is a way of re-directing shipping modes. word The value of word, which must not match any of the other settors, is available as a key for the next lookup (only). If the next settor is a database lookup, and it contains a dollar sign ($) the word will be substituted; i.e. table:column:$ becomes table:column:word. ( settor ) The value returned by settor will be used as a key for the next lookup, as above. 2.3. CommonAdjust Examples -------------------------- Most examples below use an outboard database table named pricing, but any valid table including the products table can be used. We will refer to this pricing table: code common q1 q5 q10 XL S red 99-102 10 9 8 1 -0.50 0.75 00-343 2 red 0.75 The simplest case is a straight lookup on an attribute; size in this case. 10.00, ==size:pricing With this value in the price field, a base price of 10.00 will be adjusted with the value of the size attribute. If size for the item 99-102 is set to XL then 1.00 will be added for a total price of 11.00; if it is S then .50 will be subtracted for a total price of 9.50; for any other value of size no further adjustment would be made. 00-343 would be adjusted up 2.00 only for XL. 10.00, ==size:pricing, ==color:pricing This is the same as above, except both size and color are adjusted for. A color value of red for item code 99-102 would add 0.75 to the price. For 00-343 it would have no effect. 10.00, ==size:pricing, ==color:pricing:common Here price is set based on a common column, keyed by the value of the color attribute. Any item with a color value of red would have 0.75 added to the base price. pricing:q1,q5,q10:, ;10.00, ==size:pricing, ==color:pricing:common Here is a quantity price lookup, with a fallback price setting. If there is a valid price found at the quantity of 1, 5, or 10, depending on item quantity, then it will be used. The fallback of 10.00 only applies if no non-zero/non-blank price was found at the quantity lookup. In either case, size/color adjustment is applied. pricing:q1,q5,q10:, ;10.00 ==size:pricing, ==color:pricing:common Removing the comma from the end of the fallback string stops color/size lookup if it reaches that point. If a quantity price was found, then size and color are chained. pricing:q1,q5,q10:, ;products:list_price, ==size:pricing, ==color:pricing The value of the database column list_price is used as a fallback instead of the fixed 10.00 value. The above value might be a nice one to use as the default for a typical retail catalog that has items with colors and sizes. 2.4. PriceBreaks, discounts, and PriceAdjustment ------------------------------------------------ There are several ways that Interchange can modify the price of a product during normal catalog operation. Several of them require that the pricing.asc file be present, and that you define a pricing database. You do that by placing the following directive in catalog.cfg: Database pricing pricing.asc 1 NOTE: PriceAdjustment is slightly deprecated by CommonAdjust, but will remain in use at least through the end of Version 3 of Interchange. Configurable directives and tags with regard to pricing: o Quantity price breaks are configured by means of the PriceBreaks and MixMatch directives. They require a field named specifically price in the pricing database. The price field contains a space-separated list of prices that correspond to the quantity levels defined in the PriceBreaks directive. If quantity is to be applied to all items in the shopping cart (as opposed to quantity of just that item) then the MixMatch directive should be set to Yes. o Individual line-item prices can be adjusted according to the value of their attributes. See PriceAdjustment and CommonAdjust. The pricing database must be defined unless you define the CommonAdjust behavior. o Product discounts for individual products, specific product codes, all products, or the entire order can be configured with the [discount ...] tag. Discounts are applied on a per-user basis -- you can gate the discount based on membership in a club or other arbitrary means. See Product Discounts. For example, if you decided to adjust the price of T-shirt part number 99-102 up 1.00 when the size is extra large and down 1.00 when the size is small, you would have the following directives defined in : Database pricing pricing.asc 1 UseModifier size PriceAdjustment size To enable automatic modifier handling, you define a size field in products.txt: code description price size 99-102 T-Shirt 10.00 S=Small, M=Medium, L=Large*, XL=Extra Large You would place the proper tag within your [item-list] on the shopping-basket or order page: [item-accessories size] In the pricing.asc database source, you would need: code S XL 99-102 -1.00 1.00 If you want to assign a price based on the option, precede the number with an equals sign: code S M L XL 99-102 =9.00 =10 =10 =11 IMPORTANT NOTE: Price adjustments occur AFTER quantity price breaks, so the above would negate anything set with the PriceBreaks directive/option. Numbers that begin with an equals sign (=) are used as absolute prices and are interpolated for Interchange tags first, so you can use subroutines to set the price. To facilitate coordination with the subroutine, the session variables item_code and item_quantity are set to the code and quantity of the item being evaluated. They would be accessed in a global subroutine with $Vend::Session->{item_code} and $Vend::Session->{item_quantity}. The pricing information must always come from a database because of security. 2.5. Item Attributes -------------------- Interchange allows item attributes to be set for each ordered item. This allows a size, color, or other modifier to be attached to a common part number. If multiple attributes are set, then they should be separated by commas. Previous attribute values can be saved by means of a hidden field on a form, and multiple attributes for each item can be stacked on top of each other. The configuration file directive UseModifier is used to set the name of the modifier or modifiers. For example UseModifier size,color will attach both a size and color attribute to each item code that is ordered. IMPORTANT NOTE: You may not use the following names for attributes: item group quantity code mv_ib mv_mi mv_si You can also set it in scratch with the mv_UseModifier scratch variable -- [set mv_UseModifier]size color[/set] has the same effect as above. This allows multiple options to be set for products. Whichever one is in effect at order time will be used. Be careful, you cannot set it more than once on the same page. Setting the mv_separate_items or global directive SeparateItems places each ordered item on a separate line, simplifying attribute handling. The scratch setting for mv_separate_items has the same effect. The modifier value is accessed in the [item-list] loop with the [item-modifier attribute] tag, and form input fields are placed with the [modifier-name attribute] tag. This is similar to the way that quantity is handled, except that attributes can be "stacked" by setting multiple values in an input form. You cannot define a modifier name of code or quantity, as they are already used. You must be sure that no fields in your forms have digits appended to their names if the variable is the same name as the attribute name you select, as the [modifier-name size] variables will be placed in the user session as the form variables size0, size1, size2, etc. You can use the [loop arg="item item item"] list to reference multiple display or selection fields for modifiers, or you can use the built-in [PREFIX-accessories ...] tags available in most Interchange list operations. The modifier value can then be used to select data from an arbitrary database for attribute selection and display. Below is a fragment from a shopping basket display form which shows a selectable size with "sticky" setting. Note that this would always be contained within the [item_list] [/item-list] pair. It could just as easily be done with a radio button group combined with the [checked ...] tag. Interchange will automatically generate the above select box when the [accessories or [item-accessories size] tags are called. They have the syntax: [item_accessories attribute*, type*, field*, database*, name*, outboard*] [accessories code attribute*, type*, field*, database*, name*, outboard*] code Not needed for item-accessories, this is the product code of the item to reference. attribute The item attribute as specified in the UseModifier configuration directive. Typical are size or color. type The action to be taken. One of: select Builds a dropdown menu for the attribute. The size is equal to the number of option choices. display Shows the label text for *only the selected option*. show Shows the option choices (no labels) for the option. radio Builds a radio box group for the item, with spaces separating the elements. radio nbsp Builds a radio box group for the item, with   separating the elements. radio left n Builds a radio box group for the item, inside a table, with the checkbox on the left side. If "n" is present and is a digit from 2 to 9, it will align the options in that many columns. radio right n Builds a radio box group for the item, inside a table, with the checkbox on the right side. If "n" is present and is a digit from 2 to 9, it will align the options in that many columns. check Builds a checkbox group for the item, with spaces separating the elements. check nbsp Builds a checkbox group for the item, with   separating the elements. check left n Builds a checkbox group for the item, inside a table, with the checkbox on the left side. If "n" is present and is a digit from 2 to 9, it will align the options in that many columns. check right n Builds a checkbox group for the item, inside a table, with the checkbox on the right side. If "n" is present and is a digit from 2 to 9, it will align the options in that many columns. The default is 'select', which builds an HTML select form entry for the attribute. Also recognized is 'multiple', which generates a multiple-selection drop down list, 'show', which shows the list of possible attributes, and 'display', which shows the label text for the selected option only. field The database field name to be used to build the entry (usually a field in the products database). Defaults to a field named the same as the attribute. database The database to find field in, defaults to the first products file where the item code is found. name Name of the form variable to use if a form is being built. Defaults to mv_order_attribute -- i.e. if the attribute is size, the form variable will be named mv_order_size. outboard If calling the item-accessories tag, and you wish to select from an outboard database, you can pass the key to use to find the accessory data. When called with an attribute, the database is consulted and looks for a comma-separated list of attribute options. They take the form: name=Label Text, name=Label Text* The label text is optional -- if none is given, the name will be used. If an asterisk is the last character of the label text, the item is the default selection. If no default is specified, the first will be the default. An example: [item_accessories color] This will search the product database for a field named "color". If an entry "beige=Almond, gold=Harvest Gold, White*, green=Avocado" is found, a select box like this will be built: In combination with the mv_order_item and mv_order_quantity variables this can be used to allow entry of an attribute at time of order. If used in an item list, and the user has changed the value, the generated select box will automatically retain the current value the user has selected. The value can then be displayed with [item-modifier size] on the order report, order receipt, or any other page containing an [item-list]. 2.6. Product Discounts ---------------------- Product discounts can be set upon display of any page. The discounts apply only to the customer receiving them, and are of one of three types: 1. A discount for one particular item code (key is the item-code) 2. A discount applying to all item codes (key is ALL_ITEMS) 3. A discount for an individual line item (set the mv_discount attribute with embedded Perl) 4. A discount applied after all items are totaled (key is ENTIRE_ORDER) The discounts are specified via a formula. The formula is scanned for the variables $q and $s, which are substituted for with the item quantity and subtotal respectively. The variable $s is saved between iterations, so the discounts are cumulative. In the case of the item and all items discount, the formula must evaluate to a new subtotal for all items of that code that are ordered. The discount for the entire order is applied to the entire order, and would normally be a monetary amount to subtract or a flat percentage discount. Discounts are applied to the effective price of the product, including any quantity discounts or price adjustments. To apply a straight 20% discount to all items: [discount ALL_ITEMS] $s * .8 [/discount] or with named attributes: [discount code=ALL_ITEMS] $s * .8 [/discount] To take 25% off of only item 00-342: [discount 00-342] $s * .75 [/discount] To subtract $5.00 from the customer's order: [discount ENTIRE_ORDER] $s - 5 [/discount] To reset a discount, set it to the empty string: [discount ALL_ITEMS][/discount] Perl code can be used to apply the discounts, and variables are saved between items and are shared with the [calc] tag. This example gives 10% off if two items are ordered, with 5% more for each additional up to a maximum of 30% discount: [calc] [item-list] $totalq{"[item-code]"} += [item-quantity]; [/item-list] return ''; [/calc] [item-list] [discount code="[item-code]"] return ($s) if $totalq{"[item-code]"} == 1; return ($s * .70) if $totalq{"[item-code]"} > 6; return ($s * ( 1 - 0.05 * $totalq{"[item-code]"} )); [/discount] [/item-list] Here is an example of a special discount for item code 00-343 which prices the second one ordered at 1 cent: [discount 00-343] return $s if $q == 1; my $p = $s/$q; my $t = ($q - 1) * $p; $t .= 0.01; return $t; [/discount] If you want to display the discount amount, use the [item-discount] tag. [item-list] Discount for [item-code]: [item-discount] [/item-list] Finally, if you want to display the discounted subtotal, you need to use the [calc] capability: [item-list] Discounted subtotal for [item-code]: [currency][calc] [item-price] * [item-quantity] [/calc][/currency] [/item-list] 3. Taxing ========= Interchange allows taxing in a number of ways. Simple salestax.asc table The SalesTax directive is set to a form field or fields for user input, and those form fields are used look up the tax rate in salestax.asc. Fly tax Another simple tax method. A series of Interchange Variable settings are read to develop a salestax rate for one or a few localities, usually a state in the US. Salestax multi -- VAT taxing The country and state tables are used to develop complex VAT or salestax rate calculations based on country and state, possibly with different rates based on product type. Levies -- multiple levels of tax Using the Levies setting and the Levy structure, any or all of the above methods is used to implement one or more taxes. 3.1. Sales Tax -- simple salestax.asc table ------------------------------------------- Interchange allows calculation of sales tax on a straight percentage basis, with certain items allowed to be tax-exempt. To enable this feature, the directive SalesTax is initialized with the name of a field (or fields) on the order form. Commonly, this is zipcode and/or state: SalesTax zip,state This being done, Interchange assumes the presence of a file salestax.asc, which contains a database with the percentages. Each line of salestax.asc should be a code (again, usually a five-digit zip or a two letter state) followed by a tab, then a percentage. Example: DEFAULT 0.0 45056 .0525 61821 .0725 61801 .075 IL .0625 OH .0525 VAT .15 WA .08 Based on the user's entry of information in the order form, Interchange will look up (for our example SalesTax directive) first the zip, then the state, and apply the percentage to the SUBTOTAL of the order. The subtotal will include any taxable items, and will also include the shipping cost if the state/zip is included in the TaxShipping directive. It will add the percentage, then make that available with the [salestax] tag for display on the order form. If no match is found, the entry DEFAULT is applied -- it is normally zero. If business is being done on a national basis, it is now common to have to collect sales tax for multiple states. If you are doing so, it is possible to subscribe to a service which issues regular updates of the sales tax percentages -- usually by quarterly or monthly subscription. Such a database should be easily converted to Interchange format -- but some systems are rather convoluted, and it will be well to check and see if the program can export to a flat ASCII file format based on zip code. If some items are not taxable, then you must set up a field in your database which indicates that. You then place the name of that field in the NonTaxableField directive. If the field for that item evaluates true on a yes-no basis (i.e. is set to yes, y, 1, or the like), sales tax will not be applied to the item. If it evaluates false, it will be taxed. If your state taxes shipping, use the TaxShipping directive. Utah and Nevada are known to tax shipping -- there may be others. If you want to set a fixed tax for all orders, as might occur for VAT in some countries, just set the SalesTax directive to a value like tax_code, and define a variable in the user session to reflect the proper entry in the salestax.asc file. To set it to 15% with the above salestax.asc file, you would put in a form: or to do it without submitting a form: [perl] $Values->{tax_code} = 'VAT'; return; [/perl] 3.2. Fly tax ------------ The [fly-tax] tag is placed in the DEFAULT setting of salestax.asc, and the variables TAXAREA, TAXRATE, and TAXSHIPPING are used to build the rates. TAXAREA A space-separated or comma-seperated list of states to apply tax to. Not needed for anything in the calculation, it is used to build the UI list of states to tax. TAXRATE An Interchange accessory-list style of value, with the format XX=N.NN, XX=N.NN where XX is the two-letter state code and N.NN is the tax rate in percent. To apply a tax of 7.25% for Illinois and 5.5% for Nevada, you would use: IL=7.25, NV=5.5 TAXSHIPPING A space- or comma-separated list of states where shipping is taxed. For the above example, if Nevada taxed shipping and Illinois did not, you would make TAXSHIPPING equal to "NV". The Salestax Directive To set the field that is used for the state code, you use the standard Interchange SalesTax directive. It would almost always be set to state. 3.3. Salestax "multi" -- VAT taxing ----------------------------------- If the SalesTax directive is set to "multi", then the type of tax is read from the country table. To see the tax type in force for the UK, you can place in a page: [data table=country col=tax key="UK"]. NOTE: We mention the "country" table above. In actual practice, most everything is configurable for variable name and field name via different Variable settings. They are: MV_COUNTRY_TABLE Table for country info (default "country") MV_COUNTRY_FIELD Form field determining country (default "country") MV_COUNTRY_TAX_FIELD Table column for country-wide tax (default "tax") MV_STATE_TABLE Table for state/province info (default "state") MV_STATE_FIELD Form field determining state/province (default "state") MV_STATE_TAX_FIELD Table column for state-wide tax (default "tax") MV_TAX_TYPE_FIELD Table column enumerating tax types (default "tax_type") MV_TAX_CATEGORY_FIELD Table column for product type (default tax_category) Below, we refer to the tables, columns, and fields by their default names. The first lookup is done in table country based on the user input of country (i.e. [value country]). The tax field is read and one of the following is done: 1. If no string is found, tax returns 0. 2. If string "simple:XX" is found, uses [fly-tax] with the area specifed in XX. 3. If string "state" is found, does a re-lookup with select tax from state where country = country and state = state and value is applied as below. 4. If just digits are found, rate applied directly -- i.e. "0.05" 5. If N.NN% is found, applied as percentage. 6. If category = N.NN%, default = N.NN% is found, the tax_category field in the products table is used to determine tax basis. If no tax_category is found for the product, default rate is used. This product data sku price tax_category os28003 10.00 tools os28004 20.00 food with this country and state data: code name tax US U.S.A. state JP Japan tools=10%, default=15% code country state name tax 0001 US IL Illinois 6.5% 0002 US OH Ohio default = 5.5%, food = 1% 0003 US AZ Arizona Will yield tax for one each of os28003 and os28004 of: Japan $4.00 US/IL $1.95 US/OH $0.75 US/AZ $0.00 4. THE CHECKOUT PROCESS ======================= 4.1. Advanced Multi-level Order Pages ------------------------------------- An unlimited number of order checking profiles can be defined with the OrderProfile directive, or by defining order profiles in scratch variables. This allows a multi-level ordering process, with checking for format and validity at every stage. To custom-configure the error message, place it after the format check requirement. Specifications take the form of an order page variable (like name or address), followed by an equals sign and one of five check types: required A non-blank value is required mandatory Must be non-blank, and must have been specified on this form, not a saved value from a previous form phone The field must look like a phone number, by a very loose specification allowing numbers from all countries phone_us Must have US phone number formatting, with area code state Must be a US state, including DC and Puerto Rico. province Must be a Canadian province or pre-1997 territory. state_province Must be a US state or Canadian province. zip Must have US postal code formatting, with optional ZIP+4. Also called by the alias us_postcode. ca_postcode Must have Canadian postal code formatting. Checks for a valid first letter. postcode Must have Canadian or US postal code formatting. true Field begins with y, 1, or t (Yes, 1, or True) - not case sensitive false Field begins with n, 0, or f (No, 0, or False) - not case sensitive email Rudimentary email address check, must have an '@' sign, a name, and a minimal domain regex One or more regular expressions (space-separated) to check against. To check that all submissions of the "foo" variable have "bar" at the beginning, do: foo=regex ^bar You can add an error message by putting it in quotes at the end: foo=regex ^bar "You must have bar at the beginning of this" You can require that the value not match the regex by preceding the regex with a ! character (and no space afterwards): foo=regex !^bar "You may not have bar at the beginning!" length A range of lengths you want the input to be: foo=length 4-10 That will require foo be from 4 to 10 characters long. unique Tests to see that the value would be a unique key in a table: foo=unique userdb Sorry, that username is already taken filter Runs the value through an Interchange filter and checks that the returned value is equal to the original value. foo=filter entities Sorry, no HTML allowed To check for all lower-case characters: foo=filter lower Sorry, no uppercase characters Also, there are pragmas that can be used to change behavior: &charge Perform a real-time charge operation. If set to any value but "custom", it will use Interchange's CyberCash routines. To set to something else, use the value "custom ROUTINE". The ROUTINE should be a GlobalSub which will cause the charge operation to occur -- if it returns non-blank, non-zero the profile will have succeeded. If it returns 0 or undef or blank, the profile will return failure. &credit_card Checks the mv_credit_card_* variables for validity. If set to "standard", it will use Interchange's encrypt_standard_cc routines. This destroys the CGI value of mv_credit_card_number -- if you don't want that to happen (perhaps to save it for sending to CyberCash) then add the word keep on the end. Example: # Checks credit card number and destroys number after encryption # The charge operation can never work &credit_card=standard &charge=custom authorizenet # Checks credit card number and keeps number after encryption # The charge operation can now work &credit_card=standard keep &charge=custom authorizenet You can supply your own check routine with a GlobalSub: &credit_card=check_cc The GlobalSub check_cc will be used to check and encrypt the credit card number, and its return value will be used to determine profile success. &fail Sets the mv_failpage value. &fail=page4 If the submit process succeeds, the user will be sent to the page page4. &fatal Set to '&fatal=yes' if an error should generate the error page. &final Set to '&final=yes' if a successful check should cause the order to be placed. &update Set to '&update=yes' if a successful check should cause the variable to be copied from the CGI space to the Values space. This is like [update values] except only for that variable. This is typically used when using a mv_form_profile check so that a failing check will not cause all values to be reset to their former state upon returning to the form. &return Causes profile processing to terminate with either a success or failure depending on what follows. If it is non-blank and non-zero, the profile succeeds. # Success :) &return 1 # Failure :\ &return 0 Will ignore the &fatal pragma, but &final is still in effect if set. &set Set a user session variable to a value, i.e. &set=mv_email [value email]. This will not cause failure if blank or zero. &setcheck Set a user session variable to a value, i.e. &set=mv_email [value email]. This will cause failure if set to a blank or zero. It is usually placed at the end after a &fatal pragma would have caused the process to stop if there was an error -- can also be used to determine pass/fail based on a derived value, as it will cause failure if it evaluates to zero or a blank value. &success Sets the mv_successpage value. Example: &success=page5 If the submit process succeeds, the user will be sent to the page page5. As an added measure of control, the specification is evaluated for the special Interchange tags to provide conditional setting of order parameters. With the [perl] [/perl] capability, quite complex checks can be done. Also, the name of the page to be displayed on an error can be set in the mv_failpage variable. The following file specifies a simple check of formatted parameters: name=required You must give us your name. address=required Oops! No address. city=required state=required zip=required email=required phone_day=phone_us XXX-XXX-XXXX phone-number for US or Canada &fatal=yes email=email Email address missing the domain? &set=mv_email [value email] &set=mv_successpage ord/shipping The profile above only performs the &set directives if all of the previous checks have passed -- the &fatal=yes will stop processing after the check of the email address if any of the previous checks failed. If you want to place multiple order profiles in the same file, separate them with __END__, which must be on a line by itself. User-defined check routines can be defined in a GlobalSub: GlobalSub <{'values'} hash # $var is the passed name of the variable # $val is current value of checked variable my($ref, $var, $val) = @_; if ($ref->{country} =~ /^(PT|portugal)$/i) { return $val =~ /^\d\d\d\d$/ ? (1, $var, '') : (undef, $var, "not a Portugese postal code"); } else { return (1, $var, ''); } } } } EOF Now you can specify in an order profile: postcode=pt_postcode Very elaborate checks are possible. There must be an underscore preceding the routine name. The return value of the subroutine should be a three-element array, consisting of: 1. the pass/fail ('1' or 'undef') status of the check; 2. the name of the variable which was checked; 3. a standard error message for the failure, in case a custom one has not been specified in the order profile. The latter two elements are used by the [error] tag for on-screen display of form errors. The checkout page of the Foundation demo includes examples of this. 4.2. Simple Order Report File ----------------------------- The simple order report file, "report", defines the layout of the order report which gets mailed on the completion of the order. For example, Order Date: $date Name: $name Email address: $email Shipping address: $addr Town, State, Zip: $town, $state $zip Country: $country Any input field from the order page can be included using the dollar sign notation. Interchange defines some values for use in the search form -- they begin with mv_ and are automatically ignored. 4.3. Fully-configurable Order Reports ------------------------------------- You can specify a fully-configurable order report by setting the hidden field "mv_order_report" to a legal Interchange page. This page will be interpolated with all Interchange tags before sending by email. The order number as set by backend ordering is in the variable mv_order_number, and available for your use. You could if you wish include HTML in the file, which will be interpreted by many mailers, but you can choose to use standard ASCII format. An example report is provided in the demo file . 4.4. Order Receipts ------------------- The file can of course be configured with all Interchange tags, and will be interpolated for the ordered items before they are deleted from the user session. You can set the default receipt with the receipt key in SpecialPage. If using order Routes, as in the foundation demo, you set it with the receipt key to Route. 4.5. The Order Counter ---------------------- An order counter can be enabled if the OrderCounter directive is set to a file name. An incrementing count of all orders will be kept and assigned as orders are placed. By default, the number starts at 0, but you can edit the file and change the default starting number at any time. This capability is made possible by the File::CounterFile module, available (as a part of the fine libwww modules) at the same place you got Interchange. It is included with the distribution. 4.6. Customer Input Fields -------------------------- On the order (or shopping basket) page, by default order.html, you will have a number of input fields allowing the customer to enter information such as their name and address. You can add more fields simply by putting more input elements on the order.html page, and the information will automatically be included in the order report. Input elements should be written in this way: Choose a name for this input field such as "email" for an email address. Set the name attribute to the name you have chosen. The value attribute specifies the default value to give the field when the page is displayed. Because the customer may enter information on the order page, return to browsing, and come back to the order page, you want the default value to be what was entered the first time. This is done with the [value var] element, which returns the last value of an input field. Thus, value="[value name]" will evaluate to the name entered on the previous order screen, such as: value="Jane Smith" which will be displayed by the browser. The size attributes specifies how many characters wide the input field should be on the browser. You do not need to set this to fit the longest possible value since the browser will scroll the field, but you should set it large enough to be comfortable for the customer. ______________________________________________________________________ Copyright 2002-2004 Interchange Development Group. Copyright 2001-2002 Red Hat, Inc. Freely redistributable under terms of the GNU General Public License. line: Interchange + CVS HOWTO ======================= 5. Introduction =============== 5.1. Preamble ------------- Copyright 2001-2003 Dan Browning . This document is freely redistributable under terms of the GNU General Public License. 5.2. Purpose ------------ The purpose of this document is to help others take advantage of CVS and Interchange together to increase the quality of their programming, whether they are sole developers or part of a large team of programmers, graphic artists, and HTML design gurus. Portions of it apply to general CVS setup and use, but it is geared toward the average developer using Interchange to implement an e-commerce website. 5.3. Audience ------------- I intend for this document to be useful to those who are not yet familiar with CVS as well as those who are. If you already know how to setup a pserver then you might just skim chapter 2 ("Setup CVS"), or skip it all together. In addition, I have tried to write at a technical level that would be on par with what I perceive to be the average Interchange user that participates on the interchange-users mailing list. It is assumed that the reader can and already has setup Interchange and the template catalog (e.g. Foundation) is working correctly. 5.4. Contact the author ----------------------- If you find any spelling errors, technical slip-ups, mistakes, subliminal messages, or if you wish to send feedback, critique, remarks, comments, or if you wish to contribute examples, instructions for alternative platforms, chapters, or other material, please do so. The preferred method of submitting changes is in the form of a context diff against the SDF source file (ic_cvs.sdf). Please address your correspondence to: Dan Browning dan.browning@kavod.com 5.5. The advantages of using CVS -------------------------------- CVS is a very useful tool and can help you in your development, no matter if you are an independant developer or are part of a team of developers. o What is CVS all about? o What are its advantages? The official CVS website (http://www.cvshome.org/new_users.html) has more detailed answers to these questions, but here are some brief points of interest. o Checkout "historic" points in time or milestones in a project, for example when an e-commerce site went "live" or before a major branch in the code. o Revert to older versions of a file, directory, or an entire website. o Branching releases. Concurrently develop an unstable development version as well as fix bugs in the stable production version. o Multiple developers can work on the same catalog and even the same file at the same time. (For more information about how multiple simultaneous writes are merged and conflicts resolved, see the CVS docs in the Resources Appendix). o CVS is better than ftp for file transfer, because it automatically downloads only changed files, and even then, only the portion of the file that has changed (using patches). o CVS can automatically merge two simultaneous writes to the same file by different developers. o Allows one to keep track of the changes that have been made over time (many release managers repackage CVS commit logs into WHATSNEW, HISTORY, and/or NEWS files). 5.6. How to use this document ----------------------------- There are many potential uses of CVS as it applies to Interchange. In fact, there are as many unique ways to use CVS as there are unique developers. This document only covers some of the ways, including basic and useful techniques to get started using CVS. For the intents of the average web developer using IC for a B2C e-commerce site, I've identified a few of the possible uses: Simple o One server o One catalog o One CVS module o One branch Medium o One server o Two catalogs (e.g., one is live, one is development) o Two CVS modules o Separate development and live branches Complex/Custom o Multiple servers (e.g., developers' servers, staging servers, and live servers) o Multiple catalogs o Multiple CVS modules o Multiple branches o Custom setup This document attempts to cover the simple well, explain many aspects of the medium, and hopefully give you the background you need if you decide to setup your own complex development environment. 6. Setup CVS ============ 6.1. Assumptions ---------------- Here are some of the assumptions that I make that apply to various parts of the rest of this document: o Red Hat Linux 7.x o Interchange installed (RPM or tarball) o Default Interchange tarball installation directory paths (adjust for your environment) o Template catalog setup and working Note: I will assume "foundation" for the catalog name and directory paths, but it should be just as easy to use this document with your own catalog by mentally transposing the names and paths. There shouldn't be any reason why you could not do everything I mention here on other Linux distributions, Unices or Windows (using cygwin). However, my statements will reflect Red Hat Linux 7.x. Additionally, Red Hat Linux 6.x is for the most part the same as 7.x, except for the difference of using inetd instead of xinetd to setup pserver. 6.2. Install CVS ---------------- This is the easy part. For Red Hat Linux systems, download the CVS rpms and install them. You can search for rpms for your system using http://www.rpmfind.net. Create the user and group that will administrate the Interchange repository. For this document, it will be the interch user, (which was setup during the installation of Interchange). But if you understand the mechanics of Unix users/groups, then you can use whatever username and group scheme you prefer. For example, some create a cvs user and add it to the same group that interchange uses (e.g. interch), or add the Interchange user and catalog owner to its group or vice-versa. The integration of Interchange and CVS in the latter portion of this document will require that the CVS user can write to the catalog directory. 6.3. Create the CVS repository directory ---------------------------------------- You will need to create a repository directory such as /home/interch/rep, which is used here and in the rest of the document, but it can be any directory you desire, and must be owned by the cvs user. mkdir /home/interch/rep 6.4. Setup environment variables -------------------------------- The CVSROOT environment variable can be setup for your user (in ~/.bashrc or ~/.profile, or for all users in /etc/profile. ~/.profile: export CVSROOT=${HOME}/rep 6.4.1. .cvsrc ------------- We recommend these default options for CVS. ~/.cvsrc: cvs -q diff -u update -Pd checkout -P This directs CVS to (1) automatically compress all data communicated between you and our server (saving bandwidth), and be quieter; (2) show context-sensitive diffs; (3) prune empty directories and create any new directories added to the repository since your checkout; and (4) prune empty directories during your checkouts. Note: You will need to logout/login for the profile changes to take effect. 6.5. Initialize the repository ------------------------------ Initialize the repository as the CVS user, which is interch for this document. cvs -d /home/interch/rep init 6.6. CVS Authentication ----------------------- 6.6.1. Background ----------------- Authentication is done in CVS through the $CVSROOT/CVSROOT/passwd file. It can be easily manipulated through some of the CVS administration tools that are available. An alternate authentication method is ssh, which requires no extra setup on the server side. 6.6.2. CVS administration tools ------------------------------- o http://freshmeat.net/projects/cvsadmin/ o http://freshmeat.net/projects/cvspadm/ I recommend cvsadmin, but there are also a variety of manual methods that can be used in the absence of such tools, one of which involves copying the system shadow file and modifying it for use by CVS. For more information on this manual method, see the Red Hat CVS pserver setup guide by Michael Amorose (http://www.michael-amorose.com/cvs/). 6.6.3. Setup authentication using the cvsadmin tool --------------------------------------------------- You can find a tarball to install on your system using the above address, but here is the address of a recent RPM package of the version. This package is intended for Mandrake systems, but is compatible with Red Hat Linux 7.1: o ftp://rpmfind.net/linux/Mandrake/9.0/contrib/RPMS/cvsadmin-1.0.2-1mdk.i586.rpm After installing, create a password file (touch $CVSROOT/CVSROOT/passwd, touch $CVSROOT/CVSROOT/users), and execute cvsadmin add . 6.7. Setup CVS modules ---------------------- Note: From this point on, assume that all commands are executed as the CVS user (e.g. interch), unless otherwise specified. A module is CVS is like the concept of a "project", where each module has its own branches, trees, and other features. 6.7.1. Add your project to the modules configuration file --------------------------------------------------------- The format of the modules file is explained in detail in the CVS documentation, here is the simplest way to use it. First you will need to checkout your CVSROOT directory, then modify and commit the 'modules' file. cvs co CVSROOT cd CVSROOT modules: The module name can be whatever you want, and the module directory is what we will create later under /rep. We'll want a module for the template catalog (foundation). For example: foundation foundation 6.7.2. Create the module directory ---------------------------------- This is the directory that is referred to in the CVSROOT/modules file we just modified. mkdir /rep/foundation 6.8. Setup binary file types ---------------------------- This isn't necessary if you aren't going to manage any binary files (e.g. if you plan on excluding your images/ directory). But I recommend including it. The following is an example including many binary file types (by extension) used in web development. /rep/CVSROOT/cvswrappers: *.avi -k 'b' -m 'COPY' *.doc -k 'b' -m 'COPY' *.exe -k 'b' -m 'COPY' *.gif -k 'b' -m 'COPY' *.gz -k 'b' -m 'COPY' *.hqx -k 'b' -m 'COPY' *.jar -k 'b' -m 'COPY' *.jpeg -k 'b' -m 'COPY' *.jpg -k 'b' -m 'COPY' *.mov -k 'b' -m 'COPY' *.mpg -k 'b' -m 'COPY' *.pdf -k 'b' -m 'COPY' *.png -k 'b' -m 'COPY' *.ppt -k 'b' -m 'COPY' *.sit -k 'b' -m 'COPY' *.swf -k 'b' -m 'COPY' *.tar -k 'b' -m 'COPY' *.tgz -k 'b' -m 'COPY' *.tif -k 'b' -m 'COPY' *.tiff -k 'b' -m 'COPY' *.xbm -k 'b' -m 'COPY' *.xls -k 'b' -m 'COPY' *.zip -k 'b' -m 'COPY' 6.8.1. Commit changes --------------------- Remember to commit the changes you made to 'modules' and 'cvswrappers'. cvs commit -m "Update modules and binary types" modules cvswrappers 6.9. Setup the CVS pserver -------------------------- You will likely need to be root to do this, and there are lots of guides on the Internet for setting up a CVS pserver, hopefully you wont have any trouble doing it on your particular operating system. See the Resources Appendix for more information. 6.9.1. Setup pserver in Red Hat Linux 7.x using xinetd. ------------------------------------------------------- For Red Hat Linux 7.x, edit /etc/xinetd.d/cvspserver (create a new one if none exists). The following works for me, but customization may be required for your environment (see the next section below for an inetd-based system example). This also must be done as root. Remember to substitue /home/interch/rep with your repository directory below. su - root /etc/xinetd.d/cvspserver: service cvspserver { disable = no socket_type = stream protocol = tcp wait = no user = root server = /usr/bin/cvs server_args = -f --allow-root=/home/interch/rep pserver } Now, restart xinetd for the changes to take effect. service xinetd restart 6.9.2. Setup pserver in inetd-based systems. -------------------------------------------- For inetd-based systems such as Red Hat Linux 6.2, make sure that the following files are setup accordingly. /etc/services: cvspserver 2401/tcp N:/etc/inetd.conf: cvspserver stream tcp nowait \ root /usr/sbin/tcpd /usr/bin/cvs \ --allow-root=/home/interch/rep pserver 6.9.3. Testing your pserver --------------------------- At this point, you should be able to use a CVS client to use your pserver and execute all the same commands that you can locally (which we tested before). You may wish to take advantage of a graphical CVS client, which can be particularly helpful in leveling the learning curve. Your pserver connection string will something along the lines of: :pserver:@:/home/interch/rep See the Resources Appendix for links to some graphical CVS tools. 7. Import your Interchange catalog into CVS =========================================== 7.1. Configuring your catalog ----------------------------- Eventually, we will import your catalog into the CVS repository, but first we need to do some work with a temporary copy of the catalog so we can get it into shape for importing. Note: From here on, assume the use of the Interchange user, such as interch, unless otherwise noted. su - interch If you installed via RPM: service interchange stop If you installed via tarball (default path): /usr/local/interchange/bin/interchange --stop 7.2. Remove old CVS folders --------------------------- If, for any reason, you already have CVS/ directories in your catalog, they must be removed because they might interfere with the new CVS setup. You might use the following find command, which will find any folders named CVS in the current directory and remove them. sNote:You should make a backup of the catalog directory before you do this. #backup catalog folder first tar czf ~/foundation_backup.tgz /var/lib/interchange/foundation #get rid of any old CVS folders -- (BE CAREFUL!) cd /var/lib/interchange/foundation find . -name CVS -exec rm -Rf {} \; 7.3. Create a working copy of your catalog ------------------------------------------ A working copy of your catalog is necessary to get it into shape for use with CVS. The following command creates a copy in the /tmp directory. cp -a /var/lib/interchange/foundation /tmp/import_foundation cd /tmp/import_foundation 7.4. Streamline your catalog for CVS ------------------------------------ 7.4.1. Considerations about what to import into CVS --------------------------------------------------- From your working directory (/tmp/import_foundation), decide which files will be in the CVS repository, and which will not. While it is entirely possible to import the entire catalog into the repository unchanged, I usually prefer to doctor my directories up before letting them into my repository because of several reasons: o Will the file be modified by another source? For example, /etc/order.number is modified by Interchange when run. It is recommended that the CVSIGNORE features be used to handle these types of files. See CVSIGNORE. o The likelihood that you will modify the file. For example, if I am certain that I wont every want to modify the session/ files directly, then I probably wouldn't need to manage that through CVS, but I do import the empty session/ directory to make it easier when setting up new catalogs. o Speed. Managing less files in the repository takes away from the amount of time required for CVS checkout, update, branching, and other CVS actions. For most, this amount of time is small already, but it is a consideration for some. If you have a very large image directory, it may be benificial to leave it out at first. Note that you can add or remove anything later on. 7.4.2. Remove files that aren't needed in CVS --------------------------------------------- Here is an example of some things to remove from your catalog. If you do move more directories, be sure to move them to a directory that you can later use to re-unite with a checked-out copy for a working catalog. But here I chose just to move files that are not needed for a template "skeleton" catalog. If you want to add images to your repository, the images directory is typically symlinked to /var/www/html/foundation/images, so I remove this symlink from the working copy, and replace it with an exact copy which will go into the CVS repository. #Setup images directory rm images cp -a /var/www/html/foundation/images . #Remove rm -Rf \ error.log \ *.structure \ orders/* \ logs/* \ session/* \ tmp/* \ upload/* \ backup/* \ logs/* \ #done. # The ".empty" files make it so that CVS will still checkout the # directory, even though it is empty. touch \ orders/.empty \ logs/.empty \ session/.empty \ tmp/.empty \ upload/.empty \ backup/.empty \ #done. 7.5. Import the streamlined catalog ----------------------------------- Import the remaining portion of the catalog using the cvs import command, with "foundation" as the module name and repository directory name. See the CVS documentation resources mentioned in Appendix Resources for more information. When you run the import command, it will launch $EDITOR (set to 'vi' earlier), and ask for a message to go along with the import action. Whatever you see fit to write (e.g. "starting new CVS module with my foundation catalog...") is fine. This example import command includes renaming the foundation "working" directory back to "foundation" for the import. cvs import foundation foundation start 7.6. Testing the new CVS module ------------------------------- Now you should be able to do another test checkout or update using any CVS client, which should now download all the files that you have just imported into CVS. Additionally, you might test your newly imported code by making a change to one of your checked-out source files, saving it, then committing it. index.html: Now commit the change cvs commit index.html Your changed version will now be resident in the repository. There are a lot of good CVS documentation and resources for discovering more about the checkout/update/commit cycle and other CVS aspects in the Resources Appendix You'll also notice that even if you start your interchange server, the change you made did not take effect. The next section will detail the process of tying CVS and Interchange together in a way that this will happen automatically. 8. Integrate CVS and Interchange ================================ The next step is to allow CVS to update the directory that Interchange uses to serve pages. 8.1. CVS checkout into the catalog directory -------------------------------------------- Now it is the time to replace the directories in your catalog that have counterparts in CVS with fresh checkouts from CVS (this is a preliminary action to allow CVS to update your catalog directory when a change is made to CVS). Note: Make sure interchange daemon is stopped and you have a good backup before continuing. tar czf ~/foundation.backup2.tgz /var/lib/interchange/foundation Checkout a copy from CVS into a different directory (such as foundation_CVS). cd /var/lib/interchange/ cvs co -d foundation_CVS foundation This should create the foundation_CVS/ directory for you, so that it wont conflict with your existing foundation/ directory. 8.1.1. Add any needed files to checked-out catalog -------------------------------------------------- Note that empty directories are pruned, so they will need something in them for them to show up with a -P checkout. Often a zero-byte file called '.empty' is used. If you removed any directories during the streamlining step, we must first add those back so that the catalog is usable to Interchange. In this document, we only removed unneeded files and left empty directories. This can also be the time to copy any "data" files such as orders/ logs/, etc. that might be needed if it is a live catalog. cd /var/lib/interchange/foundation cp -a \ /var/lib/interchange/foundation_CVS 8.1.2. Install and test the new catalog --------------------------------------- Now lets move the old foundation out of the way and put the new foundation_CVS in its place. cd /var/lib/interchange/ mv foundation foundation_old mv foundation_CVS foundation Now, link up the CVS images for use by Apache. cd /var/www/html/foundation/ mv images images_old ln -s /var/lib/interchange/foundation/images images Now, you should have a working catalog again. To make sure, start up Interchange and test the site with your browser. 8.2. Testing manual CVS updates on Interchange catalogs ------------------------------------------------------- Next, lets again update the checkout we made a while back before importing our catalog. (Alternatively, one could use a visual CVS client detailed above). cd ~/src cvs -q up -d foundation # -q for quiet, -d for directory prune/update Additionally, you might test making a change to one of your checked-out source files, saving it, then committing it. index.html: Now commit the change cvs commit index.html Your changed version will now be resident in the repository. Again, CVS documentation is in the Resources Appendix. This time, we can allow the changes to take effect on the code being used by Interchange to serve pages. To do so, one must run a cvs update on the catalog directory: cd /var/lib/interchange/foundation cvs -q up -d #up is the shortened version of "update" That should notify you of the new version it downloaded with something like: U pages/index.html You may also get something like the following: M catalog.cfg M etc/status.foundation M ... ? orders/000001 ? ... The ? lines in the above example mean that the CVS server has never heard of the listed directories or files (they are in your local source dir but not in the CVS source dir). It is harmless, but sometimes annoying, and can be taken care of with CVSIGNORE. The M means that the file has been modified on your local copy, and is out of sync with the remote CVS version (e.g. when Interchange runs it updates etc/status.foundation). Normally this is corrected by uploading your "modified" version to the server, but in this case, the modification was done by Interchange instead of the programmer, and wasn't meant to be committed back to the CVS repository. See CVSIGNORE. Now, check to make sure that your change has taken effect by refreshing the homepage on the site. To see the comment, use View->Page Source or whatever the relevant command for your browser is. At this point, its obvious that it would be time consuming to manually run 'cvs up' every time you make a change to the source code, so the next step is to setup CVS to automatically update the catalog whenever you commit something to CVS. 8.3. Automatic updates on commit -------------------------------- Start by modifying $CVSROOT/CVSROOT/loginfo ^foundation (date; cat; ( \ sleep 1; cd /var/lib/interchange/foundation; cvs -q update -d \ ) &) >> $CVSROOT/CVSROOT/updatelog 2>&1 The first line tells CVS that for every commit on modules that start with "foundation" (notice the regular expression "^foundation"), it will run cvs update on the given catalog directory in the background. It is important that it is executed in a forked shell (notice the "&") after sleep'ing for 1 second, because otherwise you may run into contention issues that can cause file locking problems. The 1 second timing used above works fine for me, but a longer pause may be necessary for slower computers (you'll know if you get errors about "file locked by user"). See the CVS documentation in the Resources Appendix for more details. 8.4. Automatic e-mail on commit ------------------------------- Often it is very helpful to have a commit mailing list that keeps developers up-to-date on every commit happening to the CVS. Perform these steps: o Download syncmail mkdir ~/src; cd ~/src cvs co CVSROOT cd CVSROOT cvs up wget \ http://www.icdevgroup.org/~danb/log_accum.pl \ http://www.icdevgroup.org/~danb/mailout \ #done. chmod u+x log_accum.pl mailout cvs add log_accum.pl mailout touch updatelog cvs add updatelog cat >>checkoutlist <> loginfo cvs commit -m "Automatic E-mail" checkoutlist loginfo ${FN} As root, you must setup the "cvs-log" alias to go to the correct e-mail address(es). echo 'cvs-log: email_one@yahoo.com,email_two@yahoo.com' >> /etc/aliases newaliases See Mailserver for CVS updates. Here is what a sample e-mail looks like: User: danb Date: 2003-01-16 23:40:47 GMT Modified: pages index.html Log: Testing... Revision Changes Path 1.10 +1 -8 hoopstore/pages/index.html rev 1.10, prev_rev 1.9 Index: index.html =================================================================== RCS file: /home/interch/rep/hoopstore/pages/index.html,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- index.html 16 Jan 2003 22:47:55 -0000 1.9 +++ index.html 16 Jan 2003 23:40:47 -0000 1.10 @@ -31,7 +31,7 @@ [control-set] [component]none[/component] [/control-set] - + [control reset=1] @@ -51,10 +51,3 @@ @_LEFTRIGHT_BOTTOM_@ - - Now you have a working CVS development system. At this point it may be valuable to learn more about CVS the client tools that you are using. 9. The two track model: development and live catalogs ===================================================== It is often very valuable to have a two-track development model that separates the classes of work into separate timing and decision categories. Some use "staging" and "production" terminology, others prefer "unstable" and "stable", "beta" and "release", or "development" and "live". The easiest starting point for two-track development is to just use two completely separate CVS modules and catalogs. This can make a lot of sense for many situations, for example when the next revision of the site will be so different that it is for all practical purposes starting from ground zero. A slightly more complicated solution is to use the CVS branches feature. It is more difficult to set up, but can be rewarding when used correctly. 9.1. When to branch ------------------- The first decision is when to branch the source code. For websites, this can sometimes be an easy decision like "first went live", or "site-wide overhaul", etc. 9.2. Which way to branch ------------------------ There are many different ways to branch source code. What seems to be the most common method is to use the "trunk", which is the HEAD tag to CVS as the development version, and then make a branch when a stable release is to be made. That model doesn't fit my development style at the current time, so I use the HEAD default branch as my stable live version, and use other tags (like DEV1 and DEV_REALLY_UNSTABLE) for my development branch. You may find that you are merging (or "folding") most or all of your development ranch back into your stable branch frequently. This is because unlike traditional programming where products are launched every two or three years with new features, web sites often have little fixes and new features added every day or every few weeks, with new "releases" happening more often than traditional software development (though not all web sites follow that trend). The flexibility is there to branch the source for quite some time to work on a very complex feature or complete redesign before bringing it to the live site, as well as the flexibility for day-to-day updates. 9.3. Performing the branch -------------------------- To perform the branch use the cvs tag -b command. For example: cvs tag -b DEV1 Remember that this does not change your locally checked out working directory to the new tag automatically, it only creates the branch within the CVS repository. 9.4. Setup the development catalog ---------------------------------- Now we have a branch in CVS, but we need to tie it to something in the real world, namely, an Interchange catalog. 9.4.1. Importing the catalog ---------------------------- Like we did in Integrating CVS with Interchange, you must make another copy of your catalog for use as the development version. Some would like to keep the orders/, logs/, and other directories the same, but I prefer to start with a clean slate, especially since I don't plan on having any customers visit the development site. (In fact, you can restrict who can access the development URL using the Apache allow from... directive). 9.4.1.1. Checkout source code ----------------------------- cd /var/lib/interchange cvs co -d foundation_dev foundation 9.4.1.2. Copy any other needed directories to complete the catalog ------------------------------------------------------------------ Depending on how complete your catalog is in CVS, you may need to create or copy directories/files. cd /var/lib/interchange/foundation cp -a catalog.cfg orders/* \ /var/lib/interchange/foundation_dev Note: A lot of the following steps are performed by the /usr/local/interchange/bin/makecat script, but here is how to do it manually: 9.4.2. Setting up a separate database ------------------------------------- Most often, I find it profitable to make use of a second database for the development catalog, rather than having both catalogs reference the same database (especially if the first catalog is live). 9.4.2.1. Create a second database --------------------------------- Use the means of your database platform to create a separate database. For example, PostgreSQL users might do something like: createdb foundation_dev 9.4.2.2. Populate the database ------------------------------ You can rely on the catalogs internal products/*.txt data to generate the database tables and populate them, or you can export another catalog's database and import it for the development catalog, like the example below for PostgreSQL users. pg_dump foundation > ~/foundation.dump psql foundation_dev < ~/foundation.dump 9.4.3. Copy the catalog support files ------------------------------------- #Must be root su - root #Copy HTML cd /var/www/html/ cp -a foundation foundation_dev #Copy CGI cd /var/www/cgi-bin cp -a foundation foundation_dev 9.4.4. Configure the Interchange daemon --------------------------------------- Perform the necessary modifications to interchange.cfg. For example: /usr/local/interchange/interchange.cfg: Catalog found /var/lib/interchange/foundation /cgi-bin/foundation Catalog found_dev /var/lib/interchange/foundation_dev /cgi-bin/foundation_dev 9.4.5. Configure the catalog specifics -------------------------------------- The development catalog will differ at least a little bit from the standard catalog, such as in the CGI_URL and database parameters. I recommend using a separate "local" configuration file to hold the separate values, such as config/local.cfg, and then include it from catalog.cfg. /var/lib/interchange/config/local.cfg: Variable CGI_URL /cgi-bin/foundation_dev Variable IMAGE_DIR /foundation_dev/images Now you can restart Interchange to make your changes take effect. 9.5. Splitting updates on commit by tag --------------------------------------- Setup CVS so that when you commit to the DEV1 branch, only the development (foundation_dev) catalog will be updated. And when you commit with no tags (HEAD branch), the live (foundation) catalog will be updated. Here is an example loginfo. The -r may be used just in case your environment is such that the tags may be changed by other sources. $CVSROOT/CVSROOT/loginfo: foundation \ (date; cat; ( \ sleep 1; cd /var/lib/interchange/foundation_dev; cvs -q up -d; \ cd /var/lib/interchange/foundation; \ cvs -q up -d) &) >> $CVSROOT/CVSROOT/updatelog 2>&1 ALL /usr/bin/cvs-log $CVSROOT/CVSROOT/commitlog $USER "%{sVv}" 9.6. Using new branches ----------------------- To use your new branch, checkout a working copy of the source with the correct tag specified. For example: cvs co -P -r DEV1 Then make change to one of the files, and commit it. The change should show on your development catalog, but not your live catalog. 9.7. Merging ------------ When you want to merge a change that you have made on your development branch into your stable branch, there are many ways that you can do it. One would be to : o Make a change in the development branch (DEV1) and commit it. o Copy the development-tagged file to a temporary name o Update to the live version (HEAD) o Overwrite the live (HEAD) version of the file with your temporary one o Commit the result o Update back to the development version (DEV1) I do the above so often that I have written a Tcl script for WinCVS that will automatically perform the above steps. And similar shell scripts can probably be easily written to match your development environment. The above seems to be the easiest way, to me. However, there are other alternatives detailed in the CVS manual in chapter 5, "Branching and merging", that I highly recommend for reading. One method involves specifying the last version that has already been merged into the live branch using a specific version number, date, relative time, or special purpose tag. 10. Tools of the trade ====================== This is the productivity tips section, which will hopefully help you to be able to get more done in less time. 10.1. Workstation Interchange installation ------------------------------------------ Not all developers work on Linux workstations, many use Apples (graphics designers and HTML gurus tend to, I've found), and many use Windows. This means that many developers have the extra step of uploading their changes to a Unix server where Interchange is running in order to see their changes. The remedy to that is to setup an Interchange server on your workstation, or any location that has direct access to the CVS source files. I'll explain: The Interchange server that runs where the CVS server is (that we setup earlier) can be seen as the gathering point for all the developers. However, each developer may run as many Interchange daemons as he/she requires in a local context for the purpose of seeing the changes made before uploading them via CVS. For example, Bob could setup another Interchange catalog on the same server as the CVS, (e.g. foundation-bob). To get direct access to those files (rather than FTP), Bob could use NFS mounts (if Bob's workstation is Linux) or SMB mounts using Samba if his workstation is a Windows variant. Any way that Bob can get direct access to the files will save him some time (by cutting out the "upload" from the "edit->upload->test" development cycle). One could even use VMware to run a Linux server on your Windows workstation. Note: You can now use the cygwin compatibility confirmed in Interchange versions 4.7.6 and above to run Interchange right on your Windows workstation. The result will be that you can modify the files with your favorite text editor and see the results immediately through your local catalog. Setting up the catalog initially is quite easy. Just follow the same steps used to setup the CVS catalog. Which is: o Checkout from CVS into a new CVS catalog directory and link the images/ directory. o Make localized configuration modifications. I recommend creating a config/local.cfg file and then include it at the top of catalog.cfg, with the contents of: Variable CGI_URL /cgi-bin/foundation Variable SERVER testserver Variable SECURE_ENABLE 0 Variable IMAGE_DIR /foundation/images o Restart Interchange. You may need to remove all *.sql files from the products directory, to create all of the database files again. Additionally, you may need to create the database, username/password for your database again as well. You will need to recreate any symbolic links that previously existed, such as templates/default -> templates/foundation Another thing that you might have noticed at this point is all the files that are modified locally by the Interchange daemon will report ? or M when you run an update. To fix this, see CVSIGNORE. 10.2. CVSIGNORE --------------- On the heals of a workstation installation is the requirement to setup CVSIGNORE. For all files that change, but you want to ignore (such as etc/foundation.status), create an entry in the .cvsignore file in that directory. Note that the file must be removed from the cvs repository before it will work. Here is a script that will create some sample files: cat >.cvsignore <etc/.cvsignore <products/.cvsignore < config/.cvsignore echo "*" > backup/.cvsignore echo "*" > logs/.cvsignore echo "*" > orders/.cvsignore echo "*" > session/.cvsignore echo "*" > upload/.cvsignore echo "*" > tmp/.cvsignore cvs add \ .cvsignore \ etc/.cvsignore \ products/.cvsignore \ config/.cvsignore \ backup/.cvsignore \ logs/.cvsignore \ orders/.cvsignore \ session/.cvsignore \ upload/.cvsignore \ tmp/.cvsignore \ #done. 10.3. Mailserver for CVS updates -------------------------------- An easy alternative to setting up a mailserver is to merely alias the addresses that you would like updated. If you don't have many users following your commit list, it is recommended. In /etc/aliases, merely put: cvs-log: address_one@yahoo.com,address_two@yahoo.com,address_three@yahoo.com Then run newaliases and your "mini" mailing list will be all setup. To setup a mailserver for CVS updates, first download and install Mailman. For RPM-based systems, check on rpmfind.net for a precompiled binary package. After installing, read the following information about Mailman and what needs to be done after installation (taken from the RPM meta data): "Mailman is software to help manage email discussion lists, much like Majordomo and Smartmail. Unlike most similar products, Mailman gives each mailing list a web page, and allows users to subscribe, unsubscribe, etc. over the web. Even the list manager can administer his or her list entirely from the web. Mailman also integrates most things people want to do with mailing lists, including archiving, mail <-> news gateways, and so on. When the package has finished installing, you will need to: o Run /var/mailman/bin/mmsitepass to set the Mailman administrator password. o Edit /var/mailman/Mailman/mm_cfg.py to customize Mailman's configuration for your site. o Modify the sendmail configuration to ensure that it is running and accepting connections from the outside world (to ensure that it runs, set "DAEMON=yes" in /etc/sysconfig/sendmail, ensuring that it accepts connections from the outside world may require modifying /etc/mail/sendmail.mc and regenerating sendmail.cf), and o Add these lines: ScriptAlias /mailman/ /var/mailman/cgi-bin/ Alias /pipermail/ /var/mailman/archives/public/ Options +FollowSymlinks to /etc/httpd/conf/httpd.conf to configure your web server. Users upgrading from previous releases of this package may need to move their data or adjust the configuration files to point to the locations where their data is." Then run /var/mailman/bin/newlist and follow the directions from there. 10.4. Locally mapped source code for a network IC server -------------------------------------------------------- This is useful mostly to Windows users, since Linux users can just as easily run IC daemons on their own workstation as they can a separate server. The idea is to have the IC server use its own files and directories for things that won't be edited and modified locally, but reference a Samba directory or NFS directory for things that will (such as pages/, templates/, etc.). 10.4.1. Mount the Samba or NFS directory ---------------------------------------- smbmount <...> or mount -t nfsfs <...> The following script uses two directories (source and destination) to create symlinks for the commonly modified source directories of Interchange. export S=/mnt/nfs/foundation export D=/var/lib/interchange/foundation F=db; ln -s $S/$F $D/$F F=dbconf; ln -s $S/$F $D/$F F=etc; ln -s $S/$F $D/$F F=images; ln -s $S/$F $D/$F F=pages; ln -s $S/$F $D/$F F=special_pages; ln -s $S/$F $D/$F F=templates; ln -s $S/$F $D/$F This will leave you with a working catalog that can be quickly modified (since your editor can access the local copy), while Interchange has to do the work of going over the SMB or NFS connection. 10.5. jEdit - a good editor with Interchange/HTML/Perl colorization and CVS --------------------------------------------------------------------------- I have been quite impressed with jEdit (http://www.jedit.org, and open source editor that is written in Java and runs on most platforms. I use the interchange.xml language definition written by Chris Jesseman chris@sitemajic.net, which is available from http://www.sitemajic.net/jedit/. With this, jEdit automatically colors HTML, Perl, AND many Interchange tags very intelligently. Further, jEdit has a CVS plugin, written by Ben Sarsgard bsarsgard@vmtllc.com, and available at: http://www.vmtllc.com/~bsarsgard/jedit.html. This plugin allows you to diff, update, and commit right from the editor. 10.6. Separate servers for development and live catalogs -------------------------------------------------------- If you have the luxury of separate server hardware for the development and live catalogs, you might find the following utility helpful: o CVSviaFTP (http://www.cvshome.org/dev/addoncvsftp.html) - from the CVS Add-ons page (http://www.cvshome.org/dev/addons.html). It allows one to have a given CVS module automatically publish each update to an FTP server, which could serve as the live server. Or one could could use it if your CVS installation is only local and you could use it to upload your changes to your production server. A. Credits ========== o Jon Jensen: Thanks for helping me get going on the SDF format already used by the Interchange documentation, and fixing some SDF syntax errors. o Mike Heins & all who have contributed to the success of Interchange: Thanks for following the Way Of The Source, for quality programming, and for helping to making IC something to write about. o Thanks to the countless others who have written the CVS documentation that is available online, which was my only source for learning CVS. B. Document history =================== o May 2001. Conceived and written by Dan Browning. o July 19, 2001. First draft complete, first public release. o April 12, 2002. Minor typographical edit. o June 8, 2002. Minor updates. C. Resources ============ C.1. CVS Documentation ---------------------- Here are some resources for learning more about CVS. I have ranked them by the order of usefulness, which is of course, objective. o Karl Fogel's CVS book http://cvsbook.red-bean.com/ o The official CVS manual http://www.cvshome.org/docs/manual/ o The official CVS FAQ http://faq.cvshome.org/ o The official CVS homepage http://www.cvshome.org o Info-CVS mailing list http://mail.gnu.org/mailman/listinfo/info-cvs o CVS FAQ 2 http://www.cs.utah.edu/dept/old/texinfo/cvs/FAQ.txt o Sean Dreilinger's CVS Version Control for Web Site Projects http://durak.org/cvswebsites/ o Pascal Molli's CVS reference site http://www.loria.fr/~molli/cvs-index.html o CVS Tutorial http://cellworks.washington.edu/pub/docs/cvs/tutorial/cvs_tutorial_1.html o CVS Tutorial 2 http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/ o Red Hat CVS pserver setup guide http://www.michael-amorose.com/cvs/ o CVS Add-ons http://www.cvshome.org/dev/addons.html C.2. CVS Server Software ------------------------ o CVS RPM download (Red Hat Linux 7.1) ftp://speakeasy.rpmfind.net/linux/redhat/7.1/en/os/i386/RedHat/RPMS/cvs-1.11-3.i386.rpm o Links to source tarball links can can be found at cvshome.org. C.3. CVS Client Software ------------------------ There is a variety of client access methods for using CVS on your development box. o CVSGUI is a great project that brings graphical clients to Linux, Windows, and Mac at http://www.cvsgui.org. These also give you the same access to all the command line cvs commands. o jCVS is a great cross-platform graphical CVS client available at http://www.jcvs.org. o jEdit is a great cross-platform text editor written in java, which not only has a CVS module that allows you to commit (upload) files directly from the editor, but also has a Interchange Tag Language (and Perl language) colorizer/parser. It is available from http://www.jedit.org. ______________________________________________________________________ Copyright 2002-2004 Interchange Development Group. Copyright 2001-2002 Dan Browning . Freely redistributable under terms of the GNU General Public License. line: Interchange + Forum/Blog HOWTO ============================== 11. Introduction ================ 11.1. Preamble -------------- Copyright 2002-2004 Mike Heins and Michael Wilk . Freely redistributable under terms of the GNU General Public License. 11.2. Purpose ------------- The purpose of this document is to expose how to use Interchange 5.0's forum and blog capability, enabled with the [forum ...] tag and a few ancilliary files. 11.3. Audience -------------- Anyone who is using an Interchange catalog. This is not rocket science. 11.4. Contact the authors ------------------------- If you find any spelling errors, technical slip-ups, mistakes, subliminal messages, or if you wish to send feedback, critique, remarks, comments, or if you wish to contribute examples, instructions for alternative platforms, chapters, or other material, please do so. The preferred method of submitting changes is in the form of a context diff against the SDF source file (ic_howto_forum.sdf). Please address your correspondence to: Michael Wilk mwilk@steppenwolf.com Mike Heins mike.heins@perusion.net 11.5. What it does ------------------ Interchange forums allow your customers to comment on your products, or allow you to sponsor discussion threads on an interchange catalog. They maintain their content in a single database table named forum. 12. Component files of the forums ================================= There are two directories to add to your foundation catalog -- include/forum and pages/forum. You must add a database table definition, as well as a database source file. You should add Variable support to the variable.txt database, and supporting metadata with help. Finally, you need to add the forum.tag file which contains the forum code. If you build a foundation catalog from the latest Interchange source, all of these will be done already. The files that are needed in the catalog directory: dbconf/default_db/forum.dbm dbconf/mysql/forum.mysql dbconf/pgsql/forum.pgsql include/forum/reply_form include/forum/submit_form pages/forum/reply.html pages/forum/display.html pages/forum/submit.html products/forum.txt The files that are needed in the Interchange software directory: code/UserTag/forum.tag Add the following lines to products/variable.txt (change | to TAB): FORUM_ANON_NAME|Anonymous Coward|Forums FORUM_PRODUCTS|1|Forums FORUM_EMAIL_NOTIFY|sales@yourcompany.com|Forums Add the following records to products/mv_metadata.asc (shown in key: value format, edit file to match with "te" or some other tool): # # This is a temporary file, automatically generated from the # database file: # # /tmp/what.txt # # If you change anything in it, it will be converted back into the # original format and will replace the original file. # code:variable::Variable::FORUM_EMAIL_NOTIFY type:text width:50 height: field: db: name: outboard: options: attribute: label:Forum notify email help:An email address to send copies of user comments on products. lookup: filter: help_url: pre_filter: lookup_exclude: prepend: append: display_filter: default: extended: # code:variable::Variable::FORUM_ANON_NAME type:text width:20 height: field: db: name: outboard: options: attribute: label:Forum Anonymous Name help:Name to use when a user posts anonymously to a forum lookup: filter: help_url: pre_filter: lookup_exclude: prepend: append: display_filter: default: extended: # code:variable::Variable::FORUM_PRODUCTS type:yesno width: height: field: db: name: outboard: options: attribute: label:Enable Product Forums help:This enables user comments on products in the flypage. lookup: filter: help_url: pre_filter: lookup_exclude: prepend: append: display_filter: default: extended: Add the following block to pages/flypage.html: [if variable FORUM_PRODUCTS] [forum top="[item-code]" display_page="forum/display" /]

[page href="forum/reply" form=" product=1 mv_arg=[item-code] " ]Comment on this product.

[/if] This would normally go at the end of the table displaying the product, but you can place and edit to suit. Again, all of this is provided if you have installed from the latest Interchange 4.9.4 or higher. 13. The [forum] ITL Tag ======================= The [forum] tag is what provides the capability. It uses the forum table and follows tree-like threads in that table. It has one required parameter, top. That gives the id of the message that is the top of the thread to display. This is a complete forum display for a product: [forum top="[item-code]" /] You will see something like that in the snip from pages/flypage.html above. You can pass many more parameters to the [forum] tag. Some of them are: display-page By default, the [forum ...] tag uses the current page to link to to re-display the forum at another level. Normally this works fine, but on a product flypage it will not work. You must pass in a different page. [forum top="[item-code]" display-page="forum/display" /] show-level By default, [forum] only displays the text of top-level replies to the current thread. If you want to display more levels, set the show-level parameter to 1 or higher: [forum top="[data session arg]" show-level=3 /] The above will show the first four levels of replies, with links to any further down the tree. scrub-score If you want to moderate certain comments so that their text won't be shown, you can set the scrub-score parameter to -1 and then set the score field in the message's database record to -1. By default, it is linked to with a message: One message beneath your threshold If you want to totally disable the appearance of the link and message, set the scrub-template to something: [forum top=THREAD scrub-score="-1" scrub-template="" /] show-score By default, if a message has a score of two or higher, it's text will be shown no matter what level of display it is on. You can set that threshold with this paramter. template You can set the template that displays replies with this parameter. You can also pass this as the container text for the [forum] tag, i.e.: [forum top="[data session arg]"]
{SUBJECT} by {USERINFO} on {DATE} [ Reply ]
{COMMENT}
[/forum] See pages/forum/display.html for an example. header-template You can set the header template that displays the top-level message with this parameter. See pages/forum/display.html for an example. link-template You can set the template that displays the links to messages that are not shown with this parameter. See pages/forum/display.html for an example. scrub-template The template for a message that has a score lower than scrub-score. See pages/forum/display.html for an example. threshold-message The message displayed in the default scrub-template when a message is scrubbed. If you set the scrub-template yourself, it is ignored. display-page The page linked to with {DISPLAY_URL}. Default is the current page. See Templating below. reply-page The page linked to with {REPLY_URL}. Default is forum/reply.html. See Templating below. submit-page The page linked to with {SUBMIT_URL}. Default is forum/submit.html. See Templating below. date-format The format for the date provided with {DATE}. Default is %B %e, %Y @%H:%M, which provides a date like October 5, 2002 @21:19. 14. Templating ============== All of the [forum] display mechanisms are templated. You can pass four templates -- template, header-template, link-template, and scrub-template. They use the substitution style found in Interchange's attr-list ITL tag. The following values are available for templating: ADDITIONAL Additional text normally only used at the top level COMMENT Text of the message CREATED Created date in ISO format DATE Date the comment was made DISPLAY_URL URL to display the forum with a new starting point FORUM_APPEND End indent tags for item (automatic, don't use) FORUM_PREPEND Begin indent