With a little side of applesauce...

Wednesday, June 29, 2011

Debian / Apache2.2 - better versions of a2ensite / a2dissite and a2enmod / a2dismod

I had posted these to both Debian and Ubuntu back in 2006-2007, and since they were never picked up, I thought I would post them here.

For those who don't know, Debian allows you to enable virtual hosts and modules via these cool a2en*/a2dis* scripts. The original scripts simply spit out all of the files in /etc/apache2/*-available/ folders in a space-delimited list, plus you could only enable one vhost or mod at a time. Since I was dealing with hundreds of vhosts at the time, I wrote these scripts which displayed my list of available vhosts/mods in columnar format, and allowed me to enable multiple hosts, (explicitly or globbing). These were posted as open bugs for some time, and then a Debian maintainer wrote some Perl versions of these scripts, which are the ones that are used currently in Debian-derivatives.

I wanted to post these, as I still think these perform the task very powerfully, and are much, (MUCH), shorter than the Perl-equivs. I hope that you find them useful. They work on Debian, Ubuntu and Linux Mint. I also have man pages for them, but won't worry about posting them unless someone would like them.

a2ensite:
#!/bin/sh -e

# Initialize variables
SYSCONFDIR='/etc/apache2'

prompt=1
enabledsites=
usage="
Usage: ${0} [OPTION] [FILE] [FILE]...

Options:
-f Never prompt
-h This message
"

while getopts ":ft" OPT
do
case $OPT in
f )
prompt=0
;;
h )
echo "$usage"
exit 1
;;
\?)
echo "$usage"
exit 1;;
esac
done
# remove the flags from $@
shift $((${OPTIND} - 1))


# If a2ensite is called without an argument, then
# print a list of sites to choose from. Multiple sites
# can be passed to the SITENAME variable, (including globbing).
if [ -z $1 ]; then
echo "Which site would you like to enable?"
echo "Your choices are: "
ls $SYSCONFDIR/sites-available/
echo -n "Site name? "
read SITENAME
else
SITENAME="$@"
fi

if [ "$SITENAME" = "default" ]; then
PRIORITY="000"
fi

ensite="y"
# Loop through the site files in SITENAME
for siteentry in $SITENAME
do
# Allow for file globbing
for site in $(ls $SYSCONFDIR/sites-available/$siteentry | xargs -n1 -r basename)
do
# Set default site with top priority
if [ $site = "default" ]; then
PRIORITY="000"
fi

# Check to see if the site has already been enabled
if [ -e $SYSCONFDIR/sites-enabled/$site -o \
-e $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$site" ]; then
echo "$site is already enabled!"
enabled=1
fi

# If -f flag is not passed to the script, prompt user for confirmation on each site.
if [ ${enabled:-0} -eq 0 ]; then
if [ $prompt -eq 1 ]; then
ensitelast=$ensite
echo -n "a2ensite: enable $site? [${ensitelast}] "
read ensite

if [ "$ensite" = "" ]; then
ensite=$ensitelast
fi

if [ ${ensite:-n} = "y" ]; then
if [ $site = "default" ]; then
ln -sf $SYSCONFDIR/sites-available/$site \
$SYSCONFDIR/sites-enabled/"$PRIORITY"-"$site"
else
ln -sf $SYSCONFDIR/sites-available/$site \
$SYSCONFDIR/sites-enabled/$site
fi
enabledsites="$enabledsites $site "
fi
else
if [ $site = "default" ]; then
ln -sf $SYSCONFDIR/sites-available/$site \
$SYSCONFDIR/sites-enabled/"$PRIORITY"-"$site"
else
ln -sf $SYSCONFDIR/sites-available/$site \
$SYSCONFDIR/sites-enabled/$site
fi
enabledsites="$enabledsites $site "
fi
fi
unset enabled
done
done



# Print list of enabled sites, if any.
# Test to see if any sites were enabled
if [ ${#enabledsites} -gt 0 ]; then
echo "Following site(s) enabled: "
echo
for vhost in $enabledsites
do
echo "$vhost"
done
echo
echo "Run /etc/init.d/apache2 reload to fully enable."
else
echo
echo "No sites were enabled."
fi
unset enabledsites


a2dissite:
#!/bin/sh -e

# Initialize variables
SYSCONFDIR='/etc/apache2'

prompt=1
disabledsites=
usage="
Usage: ${0} [OPTION]...

Options:
-f Never prompt
-h This message
"

while getopts ":ft" OPT
do
case $OPT in
f )
prompt=0
;;
h )
echo "$usage"
exit 1
;;
\?)
echo "$usage"
exit 1;;
esac
done
# remove the flags from $@
shift $((${OPTIND} - 1))

# If a2dissite is called without an argument, then
# print a list of sites to choose from. Multiple sites
# can be passed to the SITENAME variable.
if [ -z $1 ]; then
echo "Which site would you like to disable?"
echo "Your choices are: "
ls $SYSCONFDIR/sites-enabled/
echo -n "Site name? "
read SITENAME
else
SITENAME="$@"
fi

delsite="n"
# Loop through the site files in SITENAME
for siteentry in $SITENAME
do
# Set default site with top priority
##
if [ $siteentry = "default" ]; then
siteentry="000-default"
fi

# Allow for file globbing
for site in $(ls $SYSCONFDIR/sites-enabled/$siteentry | xargs -n1 -r basename)
do

# Check to see if the site has already been disabled
if ! [ -e $SYSCONFDIR/sites-enabled/$site ]; then
echo "$site is already disabled, or does not exist!"
disabled=1
fi

# If -f flag is not passed to the script, prompt user for confirmation on each site.
if [ ${disabled:-0} -eq 0 ]; then
if [ $prompt -eq 1 ]; then
delsitelast=$delsite
echo -n "a2dissite: disable $site? [${delsitelast}] "
read delsite

if [ "$delsite" = "" ]; then
delsite=$delsitelast
fi

if [ ${delsite:-n} = "y" ]; then
rm $SYSCONFDIR/sites-enabled/$site 2>/dev/null
disabledsites="$disabledsites $site "
fi
else
rm $SYSCONFDIR/sites-enabled/$site 2>/dev/null
disabledsites="$disabledsites $site "
fi
fi
unset disabled
done
done


# Print list of disabled sites, if any
# Test to see if any sites were disabled
if [ ${#disabledsites} -gt 0 ]; then
echo "Following site(s) disabled: "
echo
for vhost in $disabledsites
do
echo "$vhost"
done
echo
echo "Run /etc/init.d/apache2 reload to fully disable."
else
echo
echo "No sites were disabled."
fi
unset disabledsites



a2enmod:
#!/bin/sh -e

# Initialize variables
SYSCONFDIR='/etc/apache2'

prompt=1
enabledmods=
usage="
Usage: ${0} [OPTION] [FILE] [FILE]...

Options:
-f Never prompt
-h This message
"

while getopts ":ft" OPT
do
case $OPT in
f )
prompt=0
;;
h )
echo "$usage"
exit 1
;;
\?)
echo "$usage"
exit 1;;
esac
done
# remove the flags from $@
shift $((${OPTIND} - 1))


# If a2enmod is called without an argument, then
# print a list of mods to choose from. Multiple mods
# can be passed to the MODNAME variable, (including globbing).
if [ -z $1 ]; then
echo "Which mod would you like to enable?"
echo "Your choices are: "
ls $SYSCONFDIR/mods-available/ | cut -d. -f1 | uniq | column
echo -n "Mod name? "
read MODNAME
else
MODNAME="$@"
fi

enmod="y"
# Loop through the mod files in MODNAME
for modentry in $MODNAME
do
# Allow for file globbing
for mod in $(ls $SYSCONFDIR/mods-available/$modentry* | xargs -n1 -r basename | cut -d. -f1 | uniq)
do
# Check to see if the mod has already been enabled
if [ -e $SYSCONFDIR/mods-enabled/${mod}.conf -o \
-e $SYSCONFDIR/mods-enabled/${mod}.load ]; then
echo "$mod is already enabled!"
enabled=1
fi

# If -f flag is not passed to the script, prompt user for confirmation on each mod.
if [ ${enabled:-0} -eq 0 ]; then
if [ $prompt -eq 1 ]; then
enmodlast=$enmod
echo -n "a2enmod: enable $mod? [${enmodlast}] "
read enmod

if [ "$enmod" = "" ]; then
enmod=$enmodlast
fi

if [ ${enmod:-n} = "y" ]; then
ln -sf $SYSCONFDIR/mods-available/${mod}* $SYSCONFDIR/mods-enabled/
enabledmods="$enabledmods $mod "
fi
else
ln -sf $SYSCONFDIR/mods-available/${mod}* $SYSCONFDIR/mods-enabled/
enabledmods="$enabledmods $mod "
fi
fi
unset enabled
done
done



# Print list of enabled mods, if any.
# Test to see if any mods were enabled
if [ ${#enabledmods} -gt 0 ]; then
echo "Following mod(s) enabled: "
echo
for m in $enabledmods
do
echo "$m"
done
echo
echo "Run /etc/init.d/apache2 reload to fully enable."
else
echo
echo "No mods were enabled."
fi
unset enabledmods



a2dismod:
#!/bin/sh -e

# Initialize variables
SYSCONFDIR='/etc/apache2'

prompt=1
disabledmods=
usage="
Usage: ${0} [OPTION]...

Options:
-f Never prompt
-h This message
"

while getopts ":ft" OPT
do
case $OPT in
f )
prompt=0
;;
h )
echo "$usage"
exit 1
;;
\?)
echo "$usage"
exit 1;;
esac
done
# remove the flags from $@
shift $((${OPTIND} - 1))

# If a2dismod is called without an argument, then
# print a list of mods to choose from. Multiple mods
# can be passed to the MODNAME variable.
if [ -z $1 ]; then
echo "Which mod would you like to disable?"
echo "Your choices are: "
ls $SYSCONFDIR/mods-enabled/ | cut -d. -f1 | uniq | column
echo -n "Mod name? "
read MODNAME
else
MODNAME="$@"
fi

delmod="n"
# Loop through the mod files in MODNAME
for modentry in $MODNAME
do
# Allow for file globbing
for mod in $(ls $SYSCONFDIR/mods-enabled/$modentry* | xargs -n1 -r basename | cut -d. -f1 | uniq)
do
# If -f flag is not passed to the script, prompt user for confirmation on each mod.
if [ $prompt -eq 1 ]; then
delmodlast=$delmod
echo -n "a2dismod: disable $mod? [${delmodlast}] "
read delmod

if [ "$delmod" = "" ]; then
delmod=$delmodlast
fi

if [ ${delmod:-n} = "y" ]; then
rm $SYSCONFDIR/mods-enabled/${mod}* 2>/dev/null
disabledmods="$disabledmods $mod "
fi
else
rm $SYSCONFDIR/mods-enabled/${mod}* 2>/dev/null
disabledmods="$disabledmods $mod "
fi
done
done


# Print list of disabled mods, if any
# Test to see if any mods were disabled
if [ ${#disabledmods} -gt 0 ]; then
echo "Following mod(s) disabled: "
echo
for m in $disabledmods
do
echo "$m"
done
echo
echo "Run /etc/init.d/apache2 reload to fully disable."
else
echo
echo "No mods were disabled."
fi
unset disabledmods

Friday, June 24, 2011

Using Coldfusion to retrieve Wufoo form entries

Instructions
------------

0. cd /path/to/your/wufoo/Wufoo.cfc
1. copy Wufoo.cfc to your desired webfolder
2. vi Wufoo.cfc and change variables.subdomain and variables.apiKey to your API Key and subdomain.
- variables.subdomain = erikin
- variables.apiKey = 16-character alpha-numeric string (api key is tied to the user, and can be reset on the API information page).
(a. login to wufoo admin
b. click on the form tab
c. choose any form, and click code
d. click on API information
e. copy the 16-character alpha-numeric string
)
3. build the calling page in the same directory as Wufoo.cfc, (refer to example.com for either javascript or coldfusion usage).
- you can use either the form name, or hashed form name, (hashed form name is recommended, as it will remain
the same, even if the form is renamed:
(a. login to wufoo admin
b. click on the form tab
c. choose any form, and click code
d. click on API information
e. scroll to bottom of field list and copy the 'HASH' value. (ie Hash m7p5s7)
)


Here is my example.cfm, which shows how to handle the entries using either jQuery or Coldfusion:
<!--- 
You can choose to one or both of the following lines:
1. cfset cfcWufooGetter... is used when you want to make the calls using Coldfusion
2. cfajaxproxy is used if you want to use Javascript to get your Wufoo data
--->
<!--- instantiate our wufoo getter library for CF object if we are using CF to display the data --->
<cfset cfcWufooGetter = CreateObject('component', 'Wufoo').init() />
<!--- setup our CF to JS proxy if we are handling the wufoo data with javascript --->
<cfajaxproxy cfc="Wufoo" jsclassname="w">

<!--- start our HTML --->
<html>
<head>
<!--- load jquery to ease life --->
<script type="text/javascript" src="/js/jquery/jquery-1.5.js"></script>
<script type="text/javascript">
// acquireEntires() is an example of using javascript to get your data
function acquireEntries()
{
// instantiate the js object which proxies back to Wufoo.cfc
var n = new w();
// call makeCall() from Wufoo.cfc with the following params:
// 1. the name or hash of the form you want to access
// 2. cf or json.
// - cf returns a coldfusion query
// - json returns a coldfusion version of json
//
// makeCall() returns an array of form entries.
var x = n.makeCall( 'z7p8s7', 'json' );

// Grab our div so that we can add our new data
var mystring = $('#q');
// loop through the array of entries, and print an entry header
for ( var iter=0;iter<x.length;iter=iter+1 ) {
mystring.append( '<h2>Entry ' + iter + '</h2>');
// loop through the field title:value pairs for an entry
for ( var iter2=0;iter2<x[iter].DATA.length;iter2=iter2+1 ) {
mystring.append( '<strong>' + x[iter].DATA[iter2][0] + ':</strong> ' + x[iter].DATA[iter2][1] + '<br />');
}
}

// push our modified HTML back into the DOM
$("#q").text( mystring );
}
</script>
</head>

<body>
<!--- This is simply to allow you to click on a button. Feels good, doesn't it? --->
<input type="submit" id="clickme" value="clickme" onClick="acquireEntries();" />
<!--- Here is our empty div which will hold our title:value pairs --->
<div id="q"></div>


<!--- This is simply a call using coldfusion, with a cfdump to show the array of entries --->
<!---
// call makeCall() from Wufoo.cfc with the following params:
// 1. the name or hash of the form you want to access
// 2. cf or json.
// - cf returns a coldfusion query
// - json returns a coldfusion version of json
//
// makeCall() returns an array of form entries.
--->
<cfset x2 = cfcWufooGetter.makeCall( 'music-camp', 'cf' ) />
<cfdump var='#x2#' />

</body>
</html>



Here is Wufoo.cfc:
 <cfcomponent name="Wufoo"
extends="class.WufooGetter"
displayname="Wufoo"
hint="Main init config for Wufoo libraries

@author Shannon Eric Peevey speeves@erikin.com '11
">

<cffunction name="init"
access="public"
output="no"
hint="
We use this to hide our api key from prying eyes.

1. Copy this file to your class directory.
2. Add your subdomain and api key below

It will automatically load WufooGetter.cfc.
">
<cfset variables.subdomain = 'SUBDOMAIN HERE' />
<cfset variables.apiKey = 'PUT YOUR API KEY HERE' />
<cfset variables.domain = 'wufoo.com' />
<cfset variables.formPath = 'api/v3/forms/' />

<cfreturn this />
</cffunction>
</cfcomponent>



And, here is WufooGetter.cfc:
<cfcomponent name="WufooGetter"
displayname="WufooGetter"
hint="Utility class for sending Wufoo API requests to wufoo
@author Shannon Eric Peevey speeves@erikin.com '11
">

<cffunction name="getFieldNames"
access="public"
returnType="query"
output="no"
hint="
Wufoo asks us to make a second query to the Fields API to acquire the field names for a given form

This function:
1. takes the name of a form, and calls the Fields API.
2. converts the JSON response into a Coldfusion object
3. loops through the array of fields, checking for subfields, and then creates a
flat query object so that we can match rows in a query of queries in makeCall()
(allowing us to remove the Field1, Field2 business, and return an actual field
title/label with the value)

">

<cfargument name="formName" type="string" required="true" hint="name of the form" />

<cfset newJson = '' />
<cfset newQuery = queryNew( "id, title") />
<cfset u = 'https://#variables.subdomain#.#variables.domain#/#variables.formPath##formName#/fields.json' />


<!--- get the field names for formName --->
<cfhttp method="get" url="#u#" username="#variables.apikey#" result="fields" password="test" throwOnError="yes" />

<cftry>
<!--- convert the fieldnames response into a CF object --->
<cfset newJson = DeserializeJSON( fields.FileContent.toString("UTF-8") ) />

<!--- loop through each field entry --->
<cfloop array="#newJson.Fields#" index="f">

<!--- it is possible that we have subfields, (for fancy pants fields) --->
<cfif IsDefined('f.Subfields')>
<cfloop array="#f.SubFields#" index="s">
<cfset queryAddRow(newQuery) />
<cfset querySetCell( newQuery, "id", s.ID) />
<cfset querySetCell( newQuery, "title", s.LABEL) />
</cfloop>
<cfelse>
<cfset queryAddRow(newQuery) />
<cfset querySetCell( newQuery, "id", f.ID) />
<cfset querySetCell( newQuery, "title", f.TITLE) />
</cfif>
</cfloop>
<cfcatch type="any">
<cfset errorMsg = "Call to Wufoo API FAILED. ERROR DETAIL: #fields.errorDetail# URL: #u# " />
<cflog application="yes" text="#DateFormat(now(),'mm-dd-yyyy')#.#TimeFormat(now(), 'HH:mm:ss')#|#errorMsg#|#CGI.CF_TEMPLATE_PATH#|#CGI.REMOTE_ADDR#|#CGI.SERVER_NAME#|#CGI.HTTP_USER_AGENT#|#CGI.HTTP_REFERER#" />
</cfcatch>
</cftry>

<cfreturn newQuery />
</cffunction>

<cffunction name="makeCall"
access="remote"
output="no"
hint="
makeCall() combines the entries and fields API calls into
either a coldfusion array of queries, or json

This function:
1. takes the name of a form, and the desired returntype, (default: json) (cf|json).
2. call the entries API.
3. call the fields API
4. loop through each entries response and convert them into a coldfusion object
- Then convert them into a query with the cols (id, fieldValue), so that
we can match them against the fields API response
5. for each entrie, use the query of queries to match field names with field values.
6. append each query of queries result to arrEntries.
7. if returntype EQ json, then we change our cf object back into json (an array of (COLUMN, DATA) values)
8. return arrEntries

">

<cfargument name="formName" type="string" required="true" hint="name of the form" />
<cfargument name="returnType" type="string" required="false" default="json" hint="cf or json" />

<cfset init() />
<cfset newJson = '' />
<cfset j = '' />
<cfset arrEntries = ArrayNew(1) />
<cfset u = 'https://#variables.subdomain#.#variables.domain#/#variables.formPath##formName#/entries.json' />

<!--- get our get our form entries --->
<cfhttp method="get" url="#u#" username="#variables.apikey#" result="entries" password="test" throwOnError="yes" />

<cftry>
<!--- get our field names for this form (a query) --->
<cfset fieldsQuery = getFieldNames( formName ) />

<!--- convert our entries response to a coldfusion object --->
<cfset newJson = DeserializeJSON( entries.FileContent.toString("UTF-8") ) />

<!--- loop through our array of form entries to match field names with values --->
<cfloop array="#newJson.Entries#" index="e">
<!--- Create a new query and loop over keys in the entry response struct. --->
<cfset entryQuery = QueryNew( "id, fieldValue" ) />
<cfloop index="strKey" list="#StructKeyList( e )#" delimiters=",">
<cfset queryAddRow(entryQuery) />
<cfset querySetCell( entryQuery, "id", strKey) />
<cfset querySetCell( entryQuery, "fieldValue", Evaluate( "e.#strKey#" ) ) />
</cfloop>

<!--- join our queries to match field names with values --->
<cfquery dbtype="query" name="j">
select fieldsQuery.title, entryQuery.fieldValue from fieldsQuery, entryQuery
WHERE fieldsQuery.id = entryQuery.id
</cfquery>

<!--- append this query object to our return result --->
<cfset ArrayAppend( arrEntries, j) />

</cfloop>
<!--- if returntype EQ json, we convert arrEntries into json --->
<cfif returnType NEQ 'cf'>
<cfset j = SerializeJSON( arrEntries ) />
</cfif>
<cfcatch type="any">
<cfset errorMsg = "Call to Wufoo API FAILED. ERROR DETAIL: #entries.errorDetail# URL: #u# " />
<cflog application="yes" text="#DateFormat(now(),'mm-dd-yyyy')#.#TimeFormat(now(), 'HH:mm:ss')#|#errorMsg#|#CGI.CF_TEMPLATE_PATH#|#CGI.REMOTE_ADDR#|#CGI.SERVER_NAME#|#CGI.HTTP_USER_AGENT#|#CGI.HTTP_REFERER#" />
</cfcatch>
</cftry>

<cfreturn arrEntries />
</cffunction>
</cfcomponent>