Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

Managing Team-Based Web Development


Jan01: Managing Team-Based Web Development

Jacob is a corporate MIS manager responsible for data networking and Internet communications systems. He can be reached at [email protected].


As web applications become more complex, the old "webmaster does it all" approach no longer works, even for relatively small applications. Going from a one-person shop to team development brings new requirements, such as the need for a source-code control and versioning applications that may be familiar to programmers, but not web developers.

Unfortunately, the tools used for traditional software development don't work well for web site development. First of all, unlike a software-development team, which consists of programmers with similar qualifications, a web team is more diversified. Designers, writers, HTML coders, and programmers are typical members of a web team. Traditional software-development tools might be fine for programmers, but other members of web teams can quickly become lost.

More importantly, the process of developing a web application is not the same as designing a stand-alone C++, Java, or Visual Basic application. Figure 1 illustrates a typical web site infrastructure. All development work is performed on the development server. This is quite different from traditional software development, where developers work with their own copy of the source code. For web development, team members would need their own web servers with all applications and add-ons. Not only would this be expensive, but it would require a tremendous administrative effort. Therefore, a shared development server used by all members of the web team makes sense. Again, tools tailored for the traditional software developer don't support this process very well.

Companies such as Interwoven (http://www.interwoven.com/) do offer software packages that address this problem. Unfortunately, many of these tools are expensive, and some organizations can't justify the investment. Consequently, in this article I present a web-based source-code control and versioning application that jumpstarts team-based web application development. The complete source code for the application, which my group developed for our own use, is available electronically; see "Resource Center," page 5.

Application Overview

Using the tool I present here, a web team can access the same set of files and folders on the development server. In developing this tool, we made it our first requirement that all files and folders on the development server are read-only for everyone — except administrators, and the account used for replicating the content to the staging server. This lets everyone run and test the application they work on, and at the same time, guarantees that no one modifies files without first getting permission from the source-code control application.

To make modifications, you need to use our application (see Figure 2):

  • The icons on the left describe the state of a file or folder.
  • Green indicates that a file or folder is checked-in, which means no one can make modifications to it.

  • Red shows that an item is checked-out by another user; the name of the user is listed under "LockedBy."

  • Files and folders that are checked-out by the user are displayed in blue.

Permissions on checked-out items are handled as follows: The user who checked out an item has full access to it, which means he can modify, delete, or add new items. The Replication account has no access to checked-out files and folders to ensure that checked-out items don't get replicated to the staging server. Everyone else has read-access to enable team members to run and test their web application while items are checked out by other users.

You navigate the directory tree of the application by clicking on the appropriate folder link; see Figure 2. To return to a previous level, click on the link at the upper left of the application, which lets you go back to any number of folders with one click.

To check items in or out, the Checkin/ Checkout checkbox next to the item name must be selected. The actual checkin/ checkout is performed as soon as you click the Check Out&In button. The Scan checkbox is used to search for items that have been added or deleted; the actual scanning is executed by clicking the Scan for New&Deleted Items button. Checkin, checkout, and scanning are performed recursively if the Recursive checkbox is checked.

Under the Hood

The source-control program is an Active Server Page (ASP) application that takes advantage of several COM components. First of all, we use Visual SourceSafe's Automation Interface to keep track of versioning as well as the checkin/checkout mechanics. We take advantage of FileSystemObject, which is part of the Scripting Runtime Library, to navigate to files and folders. While the FileSystemObject is an integral part of IIS4 and IIS5, you need to install Visual SourceSafe on the development server to be able to use its methods and properties via its Automation Interface.

You also need a COM component to manipulate NTFS file and folder permissions. Unfortunately, Microsoft doesn't provide a component that deals with this rudimentary task. Our source-control program uses the third-party COM component from Persits Software that deals with NT User and Security Management. To be able to change file and folder permissions, we install the ASPUser component as a Transaction Server (IIS4) or Component Services (IIS5) application. Transaction Server lets you run the component under the Administrator account, which is necessary to change file and folder permissions. (Persits Software, http://www.persits.com/, provides an evaluation copy of ASPUser at http://www.aspuser.com/.)

Visual SourceSafe 6.0 Automation

As you can see in Figure 3, the SourceSafe Object Model is straightforward. The VSSDatabase object represents one SourceSafe database, logged in as one user. To access a SourceSafe database via its Automation Interface, you create an instance of a SourceSafe database object, login by calling its Open method and create a VSSItem object, which is the start point for all other SourceSafe commands:

Set objVSSdb = CreateObject("SourceSafe")

objVSSdb.Open SrcSafeIni, UserName, Password

Set objVssItem = objVSSdb.vssItem("$/")

The VSSItem object represents a file or folder. As in Figure 3, there is also a VSSItems object, which is a collection for all the children in one project. You can get to any file or folder in the SourceSafe database by iterating through the VSSItems collection.

You can find a detailed description of the Visual SourceSafe 6.0 Automation interface at http://msdn.microsoft.com/library/techart/vssauto.htm. I also found the information at http://members.home.net/preston/VSS_OLE_Automation.html helpful, especially for Visual C++ developers.

Constants.asp

Our application consists of two ASP files. While default.asp renders the user interface, submit.asp performs checkin/checkout, sets the proper file permissions, and scans for new and deleted files and folders depending on the selections users make in default.asp (again, see Figure 2).

Both default.asp and submit.asp include constants.asp; see Listing One. Constants.asp defines the user names for the Replication, Administrator, and Everyone accounts. It also initializes three variables:

  • SSIni, which defines the location of the srcsafe.ini file.
  • SSTempDir, which defines the location of the SourceSafe Working folder.

  • SSDocs, which contains the directory location of the application files and folders.

The first time the source-code control application is called, the values of SSIni, SSTempDir, and SSDocs are passed in the URL and stored in Session variables: http://default.asp?SSIni=c:\srcsafe.ini&SSTempDir=c:\VSSTemp&VSSDocs=c:\htdocs. After the initial call, SSIni, SSTempDir, and SSDocs are loaded with the values of the ASP session variables.

The User Interface

In Listing Two, default.asp creates an instance of a SourceSafe database object at the beginning. In contradiction to the SourceSafe Object model, the objVSSdb.Open method does not provide a password. To avoid maintaining SourceSafe passwords, we utilize an option in the Visual SourceSafe Administrator program that enables SourceSafe to use the network login for authentication.

Users navigate through our application by clicking on a folder name or the navigation link in the upper-left corner. Either action triggers default.asp to call itself with a new path (cp) passed along in the URL: default.asp?cp=/newpath. Default.asp stores the value of cp in the currentPath variable.

We then create the VSSItem object objVssRoot, which contains all files and folders of the current location (currentPath). To be able to render the elements of the objVssRoot object into HTML, we create two arrays — folders and docs. Next, we iterate through the objVSSRoot object, check the item type of each element we find, and write the item name to the docs array if the item is a file, or the folders array if the item is a folder.

In addition to the item name, we store the username in the folders and docs array. For files, Visual SourceSafe provides this information in the VSSCheckouts collection. Unfortunately, Visual SourceSafe doesn't give you this information for folders. However, we can get the account information by looking at the folder's NTFS permissions. If a user account other than the Administrator and Replication accounts has full access to the folder, we know it was checked out by that user. The getItemCreator function returns the user name of the person who checked out the folder, or an empty string if it wasn't checked out.

The rest of default.asp iterates through the docs and folders array, displaying the checkin/checkout status of files and folders, the name of the account that checked out an item, as well as checkboxes that allow users to select items for checkin, checkout, and scanning.

Checkin and Checkout

The submit.asp page is available electronically; see "Resource Center," page 5. Depending on the hidden variable Mode, which is set on the default.asp page, we perform either a scan for new/deleted items, or a checkin/checkout of selected items.

If the CheckIn&Out Mode applies, the CheckInOut subroutine is called, which scans the Request.Forms collection for items to be checked in and out. In default.asp, we use a special naming convention for the various checkboxes: The name of the checkin checkbox starts with i1_, checkout with o1_, scan with s1_, and the name of the recursive checkbox with r1_. If we find items in the Request.Forms collection that start with i or o, we call the CheckIn or CheckOut subroutines.

The CheckIn and CheckOut subroutines perform the actual checkin/checkout in Visual SourceSafe, but also call the SetFile Permission or SetFolderPermission subroutines to adjust NTFS permissions. If the item to be checked-in or checked-out is a folder, the operation is performed recursively if the variable iFlag is set. CheckSetFolderItemPermission is called by SetFolderPermission to adjust the NTFS permission if necessary. CheckSetFolderItemPermission guarantees that NTFS permissions of items that are already checked out by another user aren't overwritten.

The actual NTFS permission change is performed in the SetNTFSPermission subroutine using the ASPUser component. If SetNTFSPermission is called with permission set to Full, users get full access to the file and folder. At the same time, the Replication account gets no access to the item, if the item is a file, to ensure that checked-out files are not replicated to the staging server. If permission is set to Read, users get read-only access to the item, and the Replication account gets full access if the item is a file. Revoke reverses a previously set permission.

Scan for New and Deleted Items

Visual SourceSafe keeps track of versions as well as the checkin and checkout status of files and folders. The challenge is to keep the SourceSafe database synchronized with the files and folders on the file system. Our application provides a Scan function, which scans a folder for items that have been added or deleted, and updates the SourceSafe database accordingly.

The UpdateVSS subroutine calls the AddNewItems and DelOrphantItems subroutines to synchronize SourceSafe with the actual content on the file system. AddNewItems scans the file system using the FileSystemObject and checks if the item exists in the Visual SourceSafe database. If the file or folder doesn't exist within SourceSafe, it is added.

DelOrphantItems traverses the SourceSafe database and checks if the item exists on the file system. If an item was deleted from the file system, our application deletes the item in Visual SourceSafe as well. Both AddNewItems and DelOrphantItems can be performed recursively to synchronize a complete folder tree.

Deployment

To install the source-code control application, perform the following steps:

1. The development server needs to be either Windows NT 4.0 Server running IIS4 or Windows 2000 Server. I used Microsoft Site Server 3.0 to replicate the content from the development server to the staging server. Whatever replication software you use, make sure that the replication service runs under the replication account that you define in constants.asp. Also, make sure that the Administrators and Everyone accounts are set properly in constants.asp.

2. Download the ASPUser component from Persits Software's web site and install it on the development server.

3. Create a new COM Application ASPUser in Component Services (Windows 2000) or Microsoft Transaction Server (Windows NT 4.0). Make sure that the account the COM Application will run under is part of the Administrators group; this is required for ASPUser to change file and folder permissions. Finally, add the ASPUser component installed in Step 2 to the Components of our new COM Application.

4. Install Visual SourceSafe Administrator and Client on the development server.

5. Create a Visual SourceSafe database in the Visual SourceSafe Administrator for the web application you want to manage. Make sure to check "Use network name for automatic user login" under the SourceSafe Options. I recommend using the Visual SourceSafe client to initially add the files and folders you want to manage.

6. Change permissions on all the files and folders you want to manage to read-only for the Everyone account, and full control for the Administrators and Replication accounts.

7. Create a web site for the source-code control application in IIS4 or IIS5. Depending on the performance of your development server and the number of files and folders you will manage, you may have to adjust the ASP Script timeout setting from 90 seconds to a higher number. For instance, scanning an application with thousands of files and folders recursively may take several minutes. If you don't increase the Script timeout value, you get a timeout error before the application has finished searching for new/deleted items.

8. Determine the location of the srcsafe.ini file, the SourceSafe Working Folder (can be set to any location on the Development server), as well as the location of the application you want to manage. You will need these values to properly set the SSIni, SSTempDir, and SSDocs variables to start the application in a browser: http://default.asp?SSIni=c:\srcsafe.ini&SSTempDir=c:\VSSTemp&VSSDocs=c:\htdocs.

Conclusion

The application presented here gives you an easy-to-use tool for team-based web development. It uses Visual SourceSafe and lets experienced users use the Visual SourceSafe client for more advanced features, such as going back to a previous version, or retrieving all versions of a particular file.

DDJ

Listing One

<% 'Global variables initialization
dim SSIni, Username, SSTempDir, DocsDir, ReplicationUser, 
                                              AdminUser, EveryoneUser
UserName       = Request.ServerVariables("Remote_USER")
'-----------------------------------------------------------------------------
'Account may need to be adjusted to match account names used in environment
'-----------------------------------------------------------------------------
ReplicationUser = "replica"
EveryoneUser    = "Everyone"
AdminUser       = "Administrators"
'-----------------------------------------------------------------------------
'-----------------------------------------------------------------------------
' This section does not have to be changed!
' Following Session variables are initialized the first time app is called:
' -- SSini:     Location of the srcsafe.ini file.
' -- SSTempDir: Location of the SourceSafe Working Folder
' -- SSDocsDir: Location of the Document folder of app you want to manage.
'-----------------------------------------------------------------------------
If Session("SSIni") = "" OR Session("SSTempDir") = 
                                      "" OR Session("DocsDir")=""  Then
   'Session has expired or missing parameters!
   If Request.QueryString("SSIni")="" OR Request.QueryString("SSTempDir")="" 
                                     OR Request.QueryString("DocsDir")="" Then
      Response.Write("<br><b>The User Session has expired or arguments 
                           are missing! You need to login again!</b><br><br>")
      Response.Write("<b>This application expects 3 parameters in 
                                                           the URL:</b><br>")
      Response.Write("-- <b>SSIni:</b>    
                The path to the srcsafe.ini file of the current project<br>")
      Response.Write("-- <b>SSTempDir:</b> The location of 
                the Working Folder for Visual Source Safe<br>")
      Response.Write("-- <b>DocsDir:</b>     
                  The location of the Project<br><br>")
      Response.Write("Example: default.asp?SSIni=
           c:\Project\SRCSAFE.INI&SSTempDir=e:\VSStemp&DocsDir=c:\htdocs<br>")
      Response.End
   Else
      Session("SSIni")     = Request.QueryString("SSIni")
      Session("SSTempDir") = Request.QueryString("SSTempDir")
      Session("DocsDir")   = Request.QueryString("DocsDir")
   End If
End If
SSIni = Session("SSIni")
SSTempDir  = Session("SSTempDir")
DocsDir    = Session("DocsDir")
%>

Back to Article

Listing Two

<%@ Language=VBScript %>     <
%option explicit%><
!-- #include file="inc/constants.asp"--><
!-- #include file="inc/vssconst.asp"--><
%
  Dim currentPath, index, docCount,FolderCount,
                                   strSlash, segmentCnt, strDBName,FSPath
  dim docs(),folders(),segments(50)
  dim  objVSSdb, objVSSObject,objVssRoot
 '--- Create an instance of a Visual Source Database object ---
 On Error Resume Next
 Set objVSSdb = CreateObject("SourceSafe")
 objVSSdb.Open SSIni, UserName
 If Err.Number <> 0 Then
    Response.Write "User '<b>" & Username & "</b>' doesn't exist in 
            SourceSafe, or the path to the srcsafe.ini (<b>" & 
            SSIni & "</b>) file is wrong!"
    Session.Abandon()
    Response.End
 End If
  '--- Variable initialization
  strDBName = objVSSdb.databasename
  currentPath= Request.QueryString("cp")
  If currentPath=empty  Then
    currentpath="/"
  Else
      currentpath=BPath(currentPath)
  End If   
  If currentPath = "/" Then
     strSlash = ""
  Else
     strSlash = "/"
  End If
  '--- Create vssItem Object ---
  set objVssRoot=objVSSdb.vssItem("$" + currentPath,False)
  If currentPath = "/" Then
     objVssRoot.LocalSpec=SSTempDir
  End If
  FSPath = Replace( DocsDir & currentPath,"/","\") 
                      'File System equivalent of the VSS Folder
  '--- Create docs and folders array of the proper size ---
  docCount=0
  folderCount=0
  for each objVSSObject In objVssRoot.items
  if  objVSSObject.Type = VSSITEM_FILE Then
    docCount=docCount+1
  else  
    foldercount=foldercount+1
  end If
  Next
  redim docs(doccount,2)
  redim folders(FolderCount,2)
  '--- Fills the docs & folders array with items in the current folder ---
  '--- Docs array stores: filename & Username in case file is checked out ---
  '--- Folders array stores: foldername & Username in case folder is checked out  --- 
  docCount=0
  folderCount=0
  for each objVSSObject In objVssRoot.items
    if objVSSObject.Type = VSSITEM_FILE Then
      docs(docCount,0)=objVSSObject.Name
      if objVSSObject.isCheckedOut = VSSFILE_NOTCHECKEDOUT then 
        docs(docCount,1)=""
      else
        docs(docCount,1)=LCase(objVSSObject.checkouts(1).username)
      end if  
      docCount=docCount+1
    else  
      folders(foldercount,0)=objVSSObject.Name
      folders(folderCount,1)= LCase(getItemCreator(FSPath & "\" & objVSSObject.Name))
      foldercount=foldercount+1 
    end If
  Next
   '--------------------------------------------------------------------------
   ' getItemCreator: Returns the Username that created the file or folder
   ' Input:  ItemName --> File or Folder name
   '--------------------------------------------------------------------------
   Function getItemCreator(ByVal ItemName)
      Dim Au, Item, Ace, AceUser, ItemCreator
      On Error Resume Next
      ItemCreator = ""
      Set Au = Server.CreateObject("Persits.AspUser")
      Set Item = Au.File(ItemName)
      If Err.Number <> 0 Then
        'An error is generated if the Folder was deleted from the File System'
        Response.Write "<b>A File or Folder doesn't exist!</b><BR>"
        Response.Write "Go one page back, check the Scan checkbox next 
                                                    to the folder name, <BR>"
        Response.Write "and click on the 'Scan for 
                                              New&Deleted Items' button!<BR>"
         Response.End
      End If
      For i = 1 to Item.AllowanceCount
         Set Ace = Item.GetAllowanceAce(i)
         AceUser = LCase(Ace.AccountName)
         If AceUser <> LCase(AdminUser) AND AceUser <> LCase(EveryoneUser) 
                                  AND AceUser <> LCase(ReplicationUser) Then
            ItemCreator = AceUser
            Exit For
         End If
      Next
      getItemCreator = ItemCreator
   End Function
   '--------------------------------------------------------------------------
   ' BPath: Replaces // with /
   ' Input: Path string
   '--------------------------------------------------------------------------
  function BPath(byVal s1)
      if len(s1)>2 then
       if mid(s1,1,2)="//" then  
        s1=left(s1,1) & right(s1,len(s1)-2)
       end if 
      end if 
      Bpath=s1
  end function 
   '--------------------------------------------------------------------------
   ' getPathSegments: Breaks up a path string (separated by /) and puts each 
   '                  segment into the segments array.
   ' Input: Path string
   '--------------------------------------------------------------------------
  function getPathSegments(byval s)
     dim cnt,i,slen,sStr,iStr
     cnt =0
     iStr = s
     i = InStr(iStr,"/")
     do while i > 0
        if i>1 then 
           sStr = Left(iStr,i-1) 
           segments(cnt) = sStr
           cnt = cnt + 1
        else 
           sStr="" 
        end if  
        iStr = Right(iStr,len(iStr)-len(sStr)-1)
        i = InStr(iStr,"/")
     loop
     if iStr <> "" then
        segments(cnt) = iStr
        cnt = cnt + 1
     end if
     getPathSegments = cnt
  end function
%><
HTML><
HEAD><
TITLE>Source Code Control User Interface</TITLE><
/HEAD><
SCRIPT LANGUAGE="JavaScript"><
!--
   function doCheckInOut(){
      document.frmItems.Mode.value = "CheckIn&Out";
      document.frmItems.submit();
      return(true);
   }
   function doScan(){
      document.frmItems.Mode.value = "Scan";
      document.frmItems.submit();
      return(true);
   }
--><
/SCRIPT><
BODY><
form name="frmItems" action="submit.asp" method="POST" >
  <table cellspacing="2" cellpadding="2" border="0">
  <tr><td colspan="8"><center><b><%=strDBName%></b></center></td></tr>
  <tr><td colspan="8"><HR></td></tr>
  <!-- -------------------------- -->
  <!-- Render the Navigation Link -->
  <!-- -------------------------- -->
  <tr><td colspan="8"><font size="2">
  <%  segmentCnt = getPathSegments(currentpath)
      dim i,ii,strcp,strSlsh1,strSlsh
      strSlsh1 = ""
      for i=0 to segmentCnt
            strcp="/"
            strSlsh = ""
            for ii=0 to (i-1)
               strcp = strcp & strSlsh & segments(ii)
               strSlsh = "/"
            next
  %>        <a href="default.asp?cp=<%=strcp%>">
            <%=strSlsh1%><%=segments(i)%>
  <%        strSlsh1 = "/"
      next 
  %>
  </font>
  </td></tr>
  <tr>
  <th></th>
  <th> Checkout </th>
  <th> Checkin </th>
  <th> Recursive </th>
  <th> </th>
  <th> Name </th>
  <th> Scan </th>
  <th> LockedBy </th>
  </tr>
  <!-- -------------------------- -->
  <!--       Render Folders       -->
  <!-- -------------------------- --><
%for index=0 to foldercount-1 %>
  <tr><td><
% if LCase(folders(index,1))= LCase(username) then %>
     <IMG SRC="images/bl_diam.gif"> <
% else 
     if folders(index,1)<>""  then 
%>      <IMG SRC="images/or_diam.gif"><
%   else %>
        <IMG SRC="images/gr_diam.gif"><
%   end if
   end if  
%></td>
  <td align=center>
     <input type="Checkbox" name="o1_<%=index%>"> 
  </td>
  <td align=center>
     <input type="Checkbox" name="i1_<%=index%>"> 
  </td>
  <td align=center>
     <input type="Checkbox" name="r1_<%=index%>"> 
  </td>
  <td><IMG SRC="images/dir.gif"></td>
  <td><A href="default.asp?cp=<%=currentpath & strSlash & folders(index,0)%>">
                              <%=folders(index,0)%></A>   </td>
  <td align=center><input type="Checkbox" name="s1_<%=index%>"></td>
  <td align="left"><%=folders(index,1)%> </td>
  </tr>
  <input type="Hidden" name="t1_<%=index%>" value="<%=currentpath  & 
                                            strSlash & folders(index,0)%>"><
%next%><
!-- -------------------------- --><
!--         Render Files       --><
!-- -------------------------- --><
% for index=0 to doccount-1 %>
  <tr><td><
% if LCase(docs(index,1))= LCase(username) then %>
     <IMG SRC="images/bl_diam.gif"><
% else
     if docs(index,1)<>""  then 
%>      <IMG SRC="images/or_diam.gif"><
%   else %>
        <IMG SRC="images/gr_diam.gif"><
%   end if
   end if  
%> 
  </td>
  <td align=center><
% if docs(index,1)=""  then %>
      <input type="Checkbox" name="o2_<%=index%>"  align="MIDDL"><
% end if %> 
  </td>
  <td align=center><
% if LCase(docs(index,1))= LCase(username) then %>
      <input type="Checkbox" name="i2_<%=index%>" align="MIDDL"><
%end if %> 
  </td>
  <td align=center> </td>
  <td><IMG SRC="images/text.gif"></td>
  <td><%=docs(index,0)%>   </td>
  <td align=center> </td>
   <td align=left><%=docs(index,1)%> </td>
  </tr>
  <input type="Hidden" name="t2_<%=index%>" 
                     value="<%=currentpath & strSlash & docs(index,0)%>"><
%next%>
  <tr><td colspan="8"> </td></tr>
  <tr><td colspan="8" align="center">
       <input type="Hidden" name="Mode">
       <input type="Button" value="Check Out&In" 
                                        onClick="return doCheckInOut();">
               
       <input type="Button" value="Scan for New&Deleted Items" 
                                        onClick="return doScan();">
  </td></tr><
/table><
input type="hidden" name="parent" value=<%=currentPath%>><
/form><
/BODY><
/HTML>

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.