You are on page 1of 15

The Brio Command Center:

Concepts and Basics

By Adam Franz
What Is a Command Center?

A command center is a single Brio Intelligence document that is used as a fully functional front end for
an entire collection of Brio Intelligence documents or other files generated by Brio Intelligence. It acts
as a parent document to many child documents in varying forms and states. It can control and/or
generate child documents in different ways depending on your company’s needs and reporting
specifications. It can seamlessly process another Brio Intelligence document, passing parameters and
generating a CSV file, for example, which aside from the Brio plugin splash screen is all that is seen by
the user. It can access PDF’s and other files pregenerated by a Broadcast Server or even dynamically
generate jobs on the Broadcast Server to process and e-mail larger, more time consuming reports. The
possibilities are virtually endless depending on how much effort you put into it and how powerful you
want it to be, but the end result is the true definition of business intelligence: an easy-to-use yet
functional and fully robust user interface to complex databases and various other data sources.

Why Should My Company Use a Command Center?

A command center has many advantages. It provides a central point within your Brio deployment where
all users can be directed to as a starting point making seamless integration with your company’s internet
or intranet site a breeze and providing a platform within Brio for things such as alerts to all of your users
or links to updates needed to access various features. It can also takes the entire burden off of the user to
learn and understand the various features of Brio Intelligence since the command center can manage all
of their requests via predesigned EIS controls. Additionally, it allows your Brio developers to do what
they do best: create in Brio. While similar concepts can be implemented by customizing the HTML and
JavaScript of the OnDemand Server, the strengths of your Brio developers to provide GUI’s and front-
end functionality will be put to far better use by having them develop these things in the application they
work with every day. One can easily see that all of these reasons, the simplification of integration with
existing applications, reductions and eliminations in training time, playing to your developers’ strengths
instead of having them learn other skills or hiring other developers…they all equate to what we seek
most of all in business: big savings.

What Type of User Base Is the Command Center Aimed At?

Primarily the Command Center is aimed at Quckview users because several of its uses involve
processing against the database. However, a command center can also provide the ability to expand your
user base at no extra charge by providing a wide range of use to Freeview users. While by nature
Freeview can’t process directly against the database it can make use of EIS sections and can access
documents that already contain data. In fact, with a little helps from technologies such as JSP or various
other tools you could even provide a back door to the Brio repository tables for the Broadcast Server to
dynamically generate ASAP jobs to provide a user with data that is practically real-time. Again it’s a
simple matter of how far you want to take your command center and what you’re capable of doing
within your company’s business rules. Insight users can also be directed to a command center simply as
a launching point and an area where they can receive news and updates.
What Is In the Command Center Parent Document?

Again this can vary widely depending on how you are setting up your command center. Obviously there
will be at least one EIS section that is the main screen a user comes to and, in some less robust
implementations, perhaps the only page a user will ever see. It will usually contain a list of reports in a
listbox, dropdown or other control object and often times will have a list of basic actions a user can
perform on the reports such as print, save, view, refresh, e-mail and so on depending on what your
specifications are and what sort of assumptions you can make of the user’s machine. It is usually the
first screen a user sees, though in some deployments a user might first be directed to a news or update
screen (usually an EIS section) where they are presented with information regarding report changes or
company news and updates. Your command center may also contain various other EIS screens for things
such as choosing general parameters (a pair of calendars for example for choosing date ranges) or even
other parameter-setting EIS screens that can dynamically incorporate parameter choice lists fed by query
sections. Often there will be an empty section (almost empty would be more accurate as we will discuss
later in this paper) that can be used for things like overwriting temp files when windows scripting
support is disabled and the files cannot be deleted. Also for the similar purposes a command center may
contain a results set, table section, or imported data file where a row or rows exist with all (usually only
one) columns are hidden. This can be used as an area to create computed items that are exported in
formats such as CSV for later import in child documents for communication purposes. However it’s
designed, the command center parent document is usually set up to be quick to load and easy to operate
from the user’s perspective.

How Do I Open and Close Child Documents?

While there are several different methods of opening and closing child documents, to avoid tripping
security features such as prompting for the closing of a window we will talk about the most common
method used. It makes use of the basic OpenURL() method, but with a few twists. You might think it
would be as simple as using the OpenURL() method with the URL to the child document, passing the
“_new” parameter, and in the end of the child document’s OnStartup script include a call:

OpenURL(“javascript:self.close”, “_self”)

However, since the child document would not be opened with a JavaScript window.open() call, this will
with most browsers cause the user to be prompted about the window trying to close itself. To avoid this,
you first need to add a little extra to your OpenURL() method call when opening the child document.
Let’s assume that you have created a variable called “url” to represent the URL you will be passing to
open your child document:

OpenURL(“javascript:window.open(\"" + url + "\", \"\");window.back();”, “_self”)

What this does is cause the child document to be opened with the JavaScript window.open() method so
that when it reaches the end of its OnStartup script it can, without tripping any security settings, use the
method call:

OpenURL(“javascript:self.close”, “_self”)
Optionally, you may want to include some code to cause the window for your child document to be
opened off the visual screen area. This can be done by passing some additional parameters to your
JavaScript window.open() call:

OpenURL("javascript:window.open(\"" + base + "\", \"\", \"left=1300\");window.back();", “_self”)


This will cause the window to appear off the right edge of the available screen area for screen
resolutions up to 1280x1024. Of course, you can increase the parameter for “left” to compensate for
higher resolution settings.

How Does the Command Center Parent Document Communicate With and Control Child BQY
Documents?

Since the command center is designed to be customizable, communication between the parent command
center document and child BQY documents can be as simple or complex as is required. Though we will
now discuss the two most common methods, either of which would be sufficient in most cases, you
should feel free and encouraged to explore any possibilities that you feel may be better suited to your
users’ configurations and your company’s needs.

Method 1: Session Variables

Perhaps the easiest way for a command center parent document to communicate with child BQY
documents is through the use of session variables, or what are often called URL parameters. All
documents available to a user on the OnDemand Server can be accessed via their URL. A simple way of
finding these URL’s for use in your code is by navigating to the appropriate document using the folder
tree provided accessed by way of logging into the OnDemand Server from it’s native homepage and
copying a shortcut to the document. Once you have done this, take a look at the URL. You’ll see
something like:

http://brio01/ods-cgi/odscgi.exe?Method=getDocument&Docname=%40%24%40%24290&JScript=Enable

Notice that this URL is already using session parameters such as Method and Docname in the format:

http://base_url/executable?Parameter1=value&Parameter2=value…

What is so often overlooked and underused in Brio is that you have the ability to append your own
custom parameters onto the URL that can then be read by the document once it’s opened in the
OnStartup script or in any other event handler. Therefore you could take the URL we looked at above
and add your own parameters to it. For example:

…?Method=getDocument&Docname=%40%24%40%24290&JScript=Enable&Name=Joe&Wife=Jane&Kids=true

Now the document being opened by the URL might incorporate the following in its OnStartup script:

var msg = “Hello “ + Session.URL.Item(“Name”) + “!\r\n”


msg += “I hope “ + Session.URL.Item(“Wife”)
if(eval(Session.URL.Item(“Kids”))){
msg += “ and the kids are”
}
else{
msg += “ is”
}
msg += “ well.”
Alert(msg, “Welcome”)

This would then result in the following alert, which would dynamically change based on the URL
parameters of Name, Wife, and Kids:

The above example may be quite obviously impractical, but let’s look at some of the common pieces of
information that we can pass the same way from the command center parent document to the child BQY
documents:

· Actions – Tell the child document if you’d like to re-process, print, save, export, send (via a
JOOLE accessible mail program) or perform any other type of action automatically.
· User Information – Tell the child document useful information about the user, such as whether
they have scripting support, where their temp file directory is, names for temp files they might
create and so on to save it the trouble of having to make the determinations on its own.
· Limit Values – Set your limits from within the command center and pass them to the child
documents.

These examples alone can provide you with all you need to allow the child document to produce all of
the desired results without any further need for user interaction. This gives the child document the
ability to act as an auto-run file driven entirely by the session parameters passed when the document is
opened.

Method 2: File Communication

While session parameters are a quick, easy and efficient way to communicate from a parent document to
a child document, some desired configurations might require an approach that allows for additional
functionality such as the transfer of excessively long values or communication back to the parent
document. For this, a good technique is to pass information into text files that can be written or read by
either document through a wide variety of techniques. While we will not discuss all of these techniques,
we will explain the 2 most common. Bear in mind, however, that you can use any other techniques such
as the utilization of JOOLE accessible programs such as MS Word or MS Excel that have the ability to
read and write text files (or any basic file type).
Text Stream1

Text stream objects can be created from the file system object. It is accessible via JOOLE and can be
used to read, write, or append text files. Let’s use a basic example to demonstrate how this can be used
for communication:
var fso = new JOOLEObject(“Scripting.FileSystemObject”)
var txt = fso.CreateTextFile(“C:\\WINDOWS\\TEMP\\BQCOMM.TXT”)
txt.WriteLine(“Joe”)
txt.WriteLine(“Jane”)
txt.Close()

This will create a text file in the temp directory named BQCOMM.TXT which will read:
Joe
Jane

Now, this file could have been written from either the parent or child document depending on which
direction you are intending to have the communication flow. Now, let’s look at what we can do when we
want to add information to the text file:
var fso = new JOOLEObject(“Scripting.FileSystemObject”)
var file = fso.GetFile(“C:\\WINDOWS\\TEMP\\BQCOMM.TXT”)
var txt = file.OpenAsTextStream(8) // 8 is used to specify the iomode “ForAppending”
txt.WriteLine(“true”)
txt.Close()

This will now make the text file read:


Joe
Jane
true

Now let’s use this information as read from another document:


var fso = new JOOLEObject("Scripting.FileSystemObject")
var file = fso.GetFile("C:\\WINDOWS\\TEMP\\BQCOMM.TXT")
var txt = file.OpenAsTextStream(1)
var msg = "Hello " + txt.ReadLine() + "!\r\n"
msg += "I hope " + txt.ReadLine()
if(eval(txt.ReadLine())){
msg += " and the kids are "
}
else{
" is "
}
msg += "well."
txt.Close()
Alert(msg, “Welcome”)

1 - Because text streams are accessed via the file system object they require that scripting support be enabled through the registration of the scrrun.dll file.
While this file is registered by default in most Windows operating systems it is not uncommon in corporate desktop images to find that this file has been
unregistered for security purpose. Should you run into this problem it is suggested that you use an alternative method for writing to and reading from
communication temp files.
Again we will get the same alert message we saw in our first example.

Import / Export

In some cases, such as in the event of a lack of assured scripting support, you may find it to be a better
option to use basic imports and exports to read and write your communication text files. It’s a little less
efficient but it does not require support from any operating system components and can therefore often
be more flexible. To use this method we will first want to create a section for our exporting in the parent
document or any other document that could be used as the first to initiate communication. This section is
usually a results section, table section or imported data file with a row count of 1 and column count of 1.
This can be created several ways, such as querying for a single column against the database (such as
selecting SYSDATE or ‘X’ or 0 from DUAL in an Oracle database) or by importing a data file with
nothing more than a single column (with header) and single row. Once we have our section and our
section has 1 column and 1 row, we will usually hide the single column to make it simpler to parse
values from the communication file, an example of which we will see later in this section. See the
figures below for an example of how to create this section:

First create a data file…

Then import the data file…


Then hide the single column…

Now that we have our section created as described, we can pass information into it that can then be
exported for future import into other documents. Let’s look at an example of how this would work:

var sec = ActiveDocument.Sections["CommExport"]


var cols = sec.Columns
cols.AddComputed("Name", "\"Joe\"")
cols.AddComputed("Wife", "\"Jane\"")
sec.Export("C:\\WINDOWS\\TEMP\\BQCOMM.CSV", 6, true, false)

This will produce a file in the temp directory named BQCOMM.CSV that if opened in a text editor
would look like:

Name,Wife
Joe,Jane

Now we should pause to note a few things here. First is that we are specifying the parameter to include
headers when we call out Export() method. This will make it easier for documents that will later be
importing the file so that the imported values will be in the columns and not the column names that
might cause problems when we retrieve them for use in our code. Also we have to make sure to include
quotes around our values when we pass them to the computed columns so that they are shown as strings
and are not interpreted as code. Now in the next document we access we will append our communication
file to include our “Kids” value:

ActiveDocument.Sections.ImportDataFile("C:\\WINDOWS\\TEMP\\BQCOMM.CSV", 2)
var sec = ActiveDocument.Sections[“BQCOMM.CSV”]
sec.Columns.AddComputed(“Kids”, “\”Yes\”)
sec.Export("C:\\WINDOWS\\TEMP\\BQCOMM.CSV", 6, true, false)

Our exported communications file (BQCOMM.CSV) will now read:

Name,Wife,Kids
Joe,Jane,Yes
Now notice that we have changed from our previous strategy of a boolean value for Kids. This is
because of the way the word “true” would be interpreted when we later import this file. The word “true”
would be evaluated and returned as the numeric representation of 1. This could get confusing so we will
simply use the string value “Yes” in it’s place for this example and we will be sure to check it’s string
value for our import code2. Now, let’s look at how our final document will make use of our exported
communications file:

ActiveDocument.Sections.ImportDataFile("C:\\WINDOWS\\TEMP\\BQCOMM.CSV", 2)
var cols = ActiveDocument.Sections["BQCOMM.CSV"].Columns
var msg = "Hello " + cols["Name"].GetCell(1) + "!\r\n"
msg += "I hope " + cols["Wife"].GetCell(1)
if(cols["Kids"].GetCell(1) == "Yes"){
msg += " and the kids are "
}
else{
msg += " is "
}
msg += "well."
Alert(msg, "Welcome")

Once again this will produce the sample message we have seen in the previous examples and again, this
information would not be very useful in a business situation but acts as a good demonstration of another
way of communicating information from one document to another and with these methods, if necessary,
back to the original document. While these methods do provide 2-way communication functionality
which might be helpful if you choose to take alternative approaches such as having child documents
determine their own temp file names and communicate them back to the parent document for later
cleanup, it does have the disadvantage of incorporating temp files. This can at times create problems, but
we will discuss how to best avoid these problems later in this paper.

Bring It All Together

Now that we have discussed communicating by session variables or by files, generating and reading files
by text streams or by imports and exports, we should remember that a command center is designed to
your specifications, to meet your needs. Therefore you may want to combine these techniques. Maybe
you want to use communication files where the names and locations of the files may vary so you might
pass the name and location to a child document via session variables. Maybe you’re unsure whether
your users will have scripting support or not so you might want to use a try/catch statement to attempt
the text stream method but use the import/export method if you cannot access the file system object.
Maybe while doing that you want to save work in the child documents by passing a boolean value as
you would a session variable to inform the child document whether the user has scripting support or not.
A command center will be what you make it, nothing more and nothing less. So don’t feel that you have
to make a single choice. Use one, use more, use all of your choices together. There is no “wrong way”
and there is no “right way” other than whatever best provides the functionality you desire as quickly and
efficiently as possible.

2 - While one would think that using the ExportWithoutQuotes method would solve this problem, it has been known to be a little problematic at times and
therefore it is advised that you simply use text (or numeric) values in place of boolean values in these situations.
What Are the Benefits of Using Temp Files and What Should I Know When Using Them?

While the idea of using temp files can seem complicated and even intimidating, when properly
configured they can add a great deal of flexibility and functionality to your command center. We’ve
already seen how communication files can help our documents to talk to each other, but that is just the
tip of the iceberg. What if you want to provide users with an option to automatically create an e-mail
from Outlook with a BQY or PDF file that was generated from your command center child documents
attached. What if you want to avoid the need to view reports in the native BQY report section in favor of
generating and calling a PDF file. The list goes on and on, but it is not difficult to see why using temp
files can be a huge asset in your command center or just in your general Brio use. So let’s go over what
you need to know when working with temp files to make it simple, efficient, and as beneficial as
possible.

Find a Temp Directory

The first step when working with temp files is finding a place to put them. Ideally, you’ll want to use the
operating system’s designated temp directory. For users with scripting support, the file system object
can easily give you access to this information by getting the location of the special folder with
folderspec 2:

function findTempDirectory(){
var fso = new JOOLEObject("Scripting.FileSystemObject")
var temp = fso.GetSpecialFolder(2)
return temp
}

Another method is to use the Windows Scripting Host (if available):

function findTempDirectory(){
var ws = new JOOLEObject("Wscript.Shell")
var temp = ws.Environment.Item(“TMP”)
return temp
}

Of course there might be times when this is also not supported. In that event you might want to
determine the hard drive through any number of methods (checking the path to any known JOOLE
accessible program, checking the first CurrentDirectory property when the document is opened, etc.) and
loop through common folder possibilities and attempting to export an empty test section:

function findTempDirectory(driveLetter){
var directory = null
var possible = new Array(6)
possible[0] = ":\\TEMP\\"
possible[1] = ":\\WINNT\\TEMP\\"
possible[2] = ":\\WINDOWS\\TEMP\\"
possible[3] = ":\\WINDOWS\\TEMPORARY INTERNET FILES\\"
possible[4] = ":\\WINNT\\TEMPORARY INTERNET FILES\\"
possible[5] = ":\\"
var x = 0
do{
try{
var fullPth = driveLetter + possible[x] + "TEST.TMP"
ActiveDocument.Sections["Empty"].Export(fullPth, 8, true, false)
directory = driveLetter + possible[x]
x=6
}
catch(er){
x++
}
}
while(x < 6)
return directory
}

Sometimes even that may not be sufficient. Maybe you will need to prompt the user for a temp directory
by leveraging a JOOLE accessible program’s InputBox() method then test it with a file export3 wrapped
in a try/catch statement:

function getTempDirectory(){
var xl = new JOOLEObject("Excel.Application")
var valid = false
do{
var inputVal = xl.InputBox("", "Specify Temp Directory", "Please specify a temp directory (Format as C:\TEMP):")
try{
ActiveDocument.Sections["Empty"].Export(inputVal + "\\TEST.TMP", 4, true, false)
valid = true
}
catch(er){
}
}
while(valid == false)
return inputVal
}

While this method is usually used as a last resort, quite commonly all of the above methods will be used
with try/catch statements to attempt all methods if necessary. In the unlikely event that none of these
methods produce a valid temp directory, a command center might be designed to run an alternative,
scaled down version of its code to avoid the use of temp files.

Generating Temp File Names

This is yet another aspect of the command center that can be as simple or as complicated as you want to
make it. Often times, a simple naming convention incorporating a number which can be incremented
each time a file is generated such as:

“BQCC” + [number]

3 – Make sure to avoid formats such as PDF and JPG when exporting files from within a try/catch statement because these (and possible other formats, test
thoroughly to be certain) will not throw an error even if the export method call does not execute successfully.
Added to this would be an extension of TMP4 or an extension appropriate to the file type. Other
situations may call for more random or system based temp file names. For this we once again first look
to the file system object:

function getTempFileName(){
var fso = new JOOLEObject(“Scripting.FileSystemObject”)
var filename = fso.GetTempName()
return filename
}

While this will generate file names that already contain a TMP extension, you can easily shave these off
with a simple String.substr() method call. Of course, as we have seen throughout this paper we will need
to have a backup plan in case scripting support is not assured:

function getTempFileName(){
var fileName = “”
for(var a = 1; a <= 8; a++){
var num = Math.ceil(Math.random() * 26) + 64
fileName += String.fromCharCode(num)
}
return fileName
}

While the risk is slight (1 in 208,827,064,576 to be exact) that you will generate the same name twice, if
you’re at all concerned you can of course check it against the names you have already created which you
will likely be storing for cleanup purposes anyway.

Cleaning Up

Cleaning up temp files can sometimes be a bit tricky. But bear in mind that in some cases you may not
have to bother. If you’re using a standard file naming convention, the files will simply be overwritten on
the users’ machines and will never take up enough space to pose a serious problem because the same
space is being reused. You might think it a bad idea to leave a report that may contain sensitive
information on a user’s machine, but think about it. Aren’t they likely to save copies of the report on
their machine? Or even e-mail reports from a program such as Outlook that creates temp files for
attachments it uses? In most cases the files on a user’s machine can be assumed to be safe so don’t stress
out too much about removing temp files if it won’t pose a problem to leave them there. That said, there
are a few things you can do to delete temp files or to destroy the information they contain and free up
the space they have used. The first step is to make sure you know what files you will need to delete. This
can be done easily by creating a global array OnStartup and storing your filenames in it whenever one is
generated. Then, OnShutdown, you can loop through this array and delete or empty the files you have
created. To do so, we once again start by looking at the file system object. This provides the only
reasonable way to actually delete files from a user’s machine.

4 - Be aware that you may have trouble in some situations such as reimporting a file if you use a TMP extension or any extension that is not consistent with
the type of file that has been generated.
Let’s look at an example that uses an array of temp file names and also takes the established temp
directory as a second parameter:
function removeTempFiles(tempFileArray, tempDirectory){
var fso = new JOOLEObject(“Scripting.FileSystemObject”)
for(var a = tempFileArray; a.length > 0;){
var fileSpec = tempDirectory + tempFileArray[0]
var file = fso.GetFile(fileSpec)
try{
file.Delete()
tempFileArray.splice(0, 1)
}
catch(er){
var msg = “The command center cannot delete “
msg += tempFileArray[0]
msg += “ because the file is in use. Please close this file.”
Alert(msg, “Error”)
removeTempFiles(tempFileArray, tempDirectory)
}
}
}

Now notice the use of the try/catch. This is because files in use will not be able to be deleted. By using
the try/catch along with splicing the temp file names that have been successfully deleted from the array
we can successfully require the user to close temp any temp files to allow them to be deleted. Now as we
have seen throughout this paper, we have to address the problem of users who have no scripting support.
While we cannot delete their files, we can overlay them with empty or nearly empty section exports5:

function removeTempFiles(tempFileArray, tempDirectory){


for(var a = tempFileArray; a.length > 0;){
var fileSpec = tempDirectory + tempFileArray[0]
try{
ActiveDocuments.Sections[“Empty”].Export(fileSpec, 4, false, false)
tempFileArray.splice(0, 1)
}
catch(er){
var msg = “The command center cannot delete “
msg += tempFileArray[0]
msg += “ because the file is in use. Please close this file.”
Alert(msg, “Error”)
removeTempFiles(tempFileArray, tempDirectory)
}
}
}

While this will not delete the files, it will empty all information within them and cause their file size to
be diminished to around 1KB or less.

5 – Some sections such as reports cannot be exported when completely empty. To rectify this add a tiny amount of substance, such as an empty field. Also,
remember what we learned about export formats in try/catch statements from footnote #3.
This All Sounds Good for My Basic BQY Documents, But What About My Interactive Documents
That Have Things Like Pivots and Charts for Drill-down Functionality?

By now you might be thinking that while these ideas have their appeal, what about the functionality
you’ve already built into your documents? Things such as drill-downs or EIS sections. You want to still
be able to use these, right? So use them! Think about it. In the same way you opened the documents that
ran automatically, you can open interactive documents and allow users to perform actions as they would
normally. To get back to the command center, just use the OpenURL() method with the URL of the
command center in the document’s OnShutdown script. Or you can even use the “javascript:
window.back();” style of coding in the OpenURL() method as we saw in our section on opening and
closing child documents. It’s all about business intelligence and business intelligence is about things like
drill-downs and elaborate EIS sections. You’ve got them, or you’ll be making them, so use them!

What Else Can a Command Center Do?

It’s all up to you and what you want to do. It’s your command center so it’s only limited by what you
can do. Here are a few ideas that are in the testing stages that might be of interest…

Pre-Generated PDF Files

Got a Broadcast Server report that is taking forever to load when published to the ODS due to a large
quantity of data? Skip it! Instead of publishing to the ODS just export to a PDF file in a directory on
your web server. If security isn’t an issue, put it in one of the HTML folders on the ODS! In fact, with
the PDF publishing functionality extended in the 8.x performance suite versions, this concept is likely to
get easier and easier!

Dynamic Broadcast Server Jobs

Need real-time data but dealing with queries that are too complex and take far too long? Sick of having
your machine tied up while they process? Skip it! Use the SendSQL() statement to write ASAP jobs
directly to your repository tables to have the file processed and e-mailed to you. In fact, if you configure
a JSP page, ASP page or the like to receive session variables in the same way as your command center
child documents do and pass them to the repository tables, you can do the same in Freeview! You can
even use variable limits if you need to set parameters before processing!

Self-deleting Temp Files

Still having trouble getting your temp files deleted? Got a PDF that people want to keep open when they
close the command center? Some more recent browser versions fully support opening PDF’s as frames.
Why not use a text stream or text export to create an HTML page to open your PDF as a single frame
and give it an OnUnload script to hit the URL of a BQY file that can take it’s name as a session
parameter and delete both the HTML and PDF file?

The point that we keep coming back to is that the sky is the limit. Whatever you can conceive and create
in Brio you might find a use for in your command center. Experiment, try new and innovative things and
above all else, make your command center efficient, functional and something you can be proud of!
Where Can I Get Some More Information?

This paper is intended to be the first in a series of papers on command center strategies and concepts.
New ideas are coming to life every day and with them new techniques and solutions. Feel free to contact
the author of this paper to find out what’s in the works and what’s comming next:

Adam Franz
Independent Consultant
Phone: 603-661-6194
Fax: 775-796-4665
E-mail: afranz@junglemate.com
Fan Site: http://www.adamfranz.com
“CODITO ERGO SUM”

You might also like