I decided to set up Node.js on my Linux server running Apache. Getting a Node.js server running was trivially simple, but I had a bit of trouble getting the mod_rewrite stuff correct to make it pretty (i.e. no port in the URL). Basically I wanted to create a sub-directory on my site and route all the requests going there to my Node.js server, leaving everything else as-is. So, http://kevnls.com/ and http://kevnls.com/etc/ would still be handled by Apache, but http://kevnls.com/node/ would be handed-off to Node.js.
Before I began, I already had Node.js serving up content at http://kevnls.com:8000. I'm not going to address that part, because there are plenty of resources that can help you get to that point.
The first thing I needed to do was create a symlink to the directory where my Node.js server was running (/home/shared/node/). In my case my CMS has a public/ folder where I can put stuff like 'whatever.txt' and it will resolve to http://kevnls.com/whatever.txt. So, this is where I created a symlink. At this point http://kevnls.com/node/ pointed to /home/shared/node/ in the filesystem.
Then in the /home/shared/node/ folder I created my .htaccess file with this mod_rewrite rule:
-----------------------------------------------------------------
Options +FollowSymLinks -Indexes -MultiViews
RewriteEngine on
RewriteRule ^(.*)$ http://kevnls.com:8000/$1 [P]
-----------------------------------------------------------------
Then I needed to get the proper modules running in Apache to handle what I wanted to do. You can issue the following shell command and just type in the modules you want to enable:
a2enmod
In my case I needed 'proxy', 'proxy_http' and 'rewrite'.
After a quick Apache restart everything was working the way I wanted it. Now on to the coding.
Thursday, January 5, 2012
Sunday, September 25, 2011
Receiving and Processing a SAML 2.0 Response With an HttpServlet Using OpenSAML
Due to the popularity of my prior posts on processing SAML using OpenSAML I put together this code that shows a more real-world-example of how you would setup an HttpServlet to sit and listen for SAML Responses that are posted to it. Then it just shows what you might do with the Response to get what you need out of it. Again, I'm just verifying the Signature on the Response, Decrypting the Assertion and then looping through the Attribute nodes to look at the name/value pairs. There are other things that you might want to do such as receiving the signing-key from the Response itself (if it's being sent), or verifying a Signature on the Assertion if that's what's being signed. The trouble I've found with SAML implementations is that the spec is consistent, but how people use it isn't, so you'll undoubtedly need to tweak something depending on what your requirements are, but this code should get you started.
import java.io.PrintWriter;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.X509EncodedKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.opensaml.common.binding.BasicSAMLMessageContext;
import org.opensaml.saml2.binding.decoding.HTTPPostDecoder;
import org.opensaml.ws.message.MessageContext;
import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.encryption.Decrypter;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.encryption.DecryptionException;
import org.opensaml.xml.encryption.InlineEncryptedKeyResolver;
import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver;
import org.opensaml.xml.security.x509.BasicX509Credential;
import org.opensaml.xml.signature.Signature;
import org.opensaml.xml.signature.SignatureValidator;
import org.opensaml.xml.validation.ValidationException;
/**
* @author kevnls
* If you use this code as a base for your implementation please leave this comment intact.
* You should add your own name in addition.
*/
public class ProcessSAML extends HttpServlet {
/**
* Processes requests for both HTTP
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
File signatureVerificationPublicKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\IdPSigningCert.cer");
File decryptionPrivateKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\SPEncryptionCert.jks");
String decryptionPrivateKeyName = "pvktmp:bd5ba0e0-9718-48ea-b6e6-32cd9c852d76";
String decryptionPrivateKeyPassword = "!c3c0ld";
try
{
//bootstrap the opensaml stuff
org.opensaml.DefaultBootstrap.bootstrap();
// get the message context
MessageContext messageContext = new BasicSAMLMessageContext();
messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(request));
HTTPPostDecoder samlMessageDecoder = new HTTPPostDecoder();
samlMessageDecoder.decode(messageContext);
// get the SAML Response
Response samlResponse = (Response)messageContext.getInboundMessage();
//get the certificate from the file
InputStream inputStream2 = new FileInputStream(signatureVerificationPublicKeyFile);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)certificateFactory.generateCertificate(inputStream2);
inputStream2.close();
//pull out the public key part of the certificate into a KeySpec
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
//get KeyFactory object that creates key objects, specifying RSA
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//generate public key to validate signatures
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
//create credentials
BasicX509Credential publicCredential = new BasicX509Credential();
//add public key value
publicCredential.setPublicKey(publicKey);
//create SignatureValidator
SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
//get the signature to validate from the response object
Signature signature = samlResponse.getSignature();
//try to validate
try
{
signatureValidator.validate(signature);
}
catch (ValidationException ve)
{
out.println("Signature is not valid.");
out.println(ve.getMessage());
return;
}
//no validation exception was thrown
out.println("Signature is valid.");
//start decryption of assertion
//load up a KeyStore
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(decryptionPrivateKeyFile), decryptionPrivateKeyPassword.toCharArray());
RSAPrivateKey privateKey = (RSAPrivateKey) keyStore.getKey(decryptionPrivateKeyName, decryptionPrivateKeyPassword.toCharArray());
//create the credential
BasicX509Credential decryptionCredential = new BasicX509Credential();
decryptionCredential.setPrivateKey(privateKey);
StaticKeyInfoCredentialResolver skicr = new StaticKeyInfoCredentialResolver(decryptionCredential);
//create a decrypter
Decrypter decrypter = new Decrypter(null, skicr, new InlineEncryptedKeyResolver());
//decrypt the first (and only) assertion
Assertion decryptedAssertion;
try
{
decryptedAssertion = decrypter.decrypt(samlResponse.getEncryptedAssertions().get(0));
}
catch (DecryptionException de)
{
out.println("Assertion decryption failed.");
out.println(de.getMessage());
return;
}
out.println("Assertion decryption succeeded.");
//loop through the nodes to get the Attributes
//this is where you would do something with these elements
//to tie this user with your environment
List attributeStatements = decryptedAssertion.getAttributeStatements();
for (int i = 0; i < attributeStatements.size(); i++)
{
List attributes = attributeStatements.get(i).getAttributes();
for (int x = 0; x < attributes.size(); x++)
{
String strAttributeName = attributes.get(x).getDOM().getAttribute("Name");
List attributeValues = attributes.get(x).getAttributeValues();
for (int y = 0; y < attributeValues.size(); y++)
{
String strAttributeValue = attributeValues.get(y).getDOM().getTextContent();
out.println(strAttributeName + ": " + strAttributeValue + " ");
}
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
//
/**
* Handles the HTTP
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "This servlet processes a SAML 2.0 Response. It verifies the signature, " +
"decrypts an assertion, and parses out the data in the attribute statements. " +
"If you use this code as a base for your implementation please leave the @author comment intact. " +
"You should add your own name in addition.";
}//
}
Anyway, hope this is useful to someone.
import java.io.PrintWriter;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.X509EncodedKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.opensaml.common.binding.BasicSAMLMessageContext;
import org.opensaml.saml2.binding.decoding.HTTPPostDecoder;
import org.opensaml.ws.message.MessageContext;
import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.encryption.Decrypter;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.encryption.DecryptionException;
import org.opensaml.xml.encryption.InlineEncryptedKeyResolver;
import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver;
import org.opensaml.xml.security.x509.BasicX509Credential;
import org.opensaml.xml.signature.Signature;
import org.opensaml.xml.signature.SignatureValidator;
import org.opensaml.xml.validation.ValidationException;
/**
* @author kevnls
* If you use this code as a base for your implementation please leave this comment intact.
* You should add your own name in addition.
*/
public class ProcessSAML extends HttpServlet {
/**
* Processes requests for both HTTP
GET
and POST
methods.* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
File signatureVerificationPublicKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\IdPSigningCert.cer");
File decryptionPrivateKeyFile = new File("C:\\Documents and Settings\\kevnls\\My Documents\\NetBeansProjects\\SAMLReceiver\\files\\SPEncryptionCert.jks");
String decryptionPrivateKeyName = "pvktmp:bd5ba0e0-9718-48ea-b6e6-32cd9c852d76";
String decryptionPrivateKeyPassword = "!c3c0ld";
try
{
//bootstrap the opensaml stuff
org.opensaml.DefaultBootstrap.bootstrap();
// get the message context
MessageContext messageContext = new BasicSAMLMessageContext();
messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(request));
HTTPPostDecoder samlMessageDecoder = new HTTPPostDecoder();
samlMessageDecoder.decode(messageContext);
// get the SAML Response
Response samlResponse = (Response)messageContext.getInboundMessage();
//get the certificate from the file
InputStream inputStream2 = new FileInputStream(signatureVerificationPublicKeyFile);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)certificateFactory.generateCertificate(inputStream2);
inputStream2.close();
//pull out the public key part of the certificate into a KeySpec
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(certificate.getPublicKey().getEncoded());
//get KeyFactory object that creates key objects, specifying RSA
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//generate public key to validate signatures
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
//create credentials
BasicX509Credential publicCredential = new BasicX509Credential();
//add public key value
publicCredential.setPublicKey(publicKey);
//create SignatureValidator
SignatureValidator signatureValidator = new SignatureValidator(publicCredential);
//get the signature to validate from the response object
Signature signature = samlResponse.getSignature();
//try to validate
try
{
signatureValidator.validate(signature);
}
catch (ValidationException ve)
{
out.println("Signature is not valid.");
out.println(ve.getMessage());
return;
}
//no validation exception was thrown
out.println("Signature is valid.");
//start decryption of assertion
//load up a KeyStore
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(decryptionPrivateKeyFile), decryptionPrivateKeyPassword.toCharArray());
RSAPrivateKey privateKey = (RSAPrivateKey) keyStore.getKey(decryptionPrivateKeyName, decryptionPrivateKeyPassword.toCharArray());
//create the credential
BasicX509Credential decryptionCredential = new BasicX509Credential();
decryptionCredential.setPrivateKey(privateKey);
StaticKeyInfoCredentialResolver skicr = new StaticKeyInfoCredentialResolver(decryptionCredential);
//create a decrypter
Decrypter decrypter = new Decrypter(null, skicr, new InlineEncryptedKeyResolver());
//decrypt the first (and only) assertion
Assertion decryptedAssertion;
try
{
decryptedAssertion = decrypter.decrypt(samlResponse.getEncryptedAssertions().get(0));
}
catch (DecryptionException de)
{
out.println("Assertion decryption failed.");
out.println(de.getMessage());
return;
}
out.println("Assertion decryption succeeded.");
//loop through the nodes to get the Attributes
//this is where you would do something with these elements
//to tie this user with your environment
List
for (int i = 0; i < attributeStatements.size(); i++)
{
List
for (int x = 0; x < attributes.size(); x++)
{
String strAttributeName = attributes.get(x).getDOM().getAttribute("Name");
List
for (int y = 0; y < attributeValues.size(); y++)
{
String strAttributeValue = attributeValues.get(y).getDOM().getTextContent();
out.println(strAttributeName + ": " + strAttributeValue + " ");
}
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
//
/**
* Handles the HTTP
GET
method.* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Handles the HTTP
POST
method.* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Returns a short description of the servlet.
* @return a String containing servlet description
*/
@Override
public String getServletInfo() {
return "This servlet processes a SAML 2.0 Response. It verifies the signature, " +
"decrypts an assertion, and parses out the data in the attribute statements. " +
"If you use this code as a base for your implementation please leave the @author comment intact. " +
"You should add your own name in addition.";
}//
}
Labels:
Assertion,
Decrypting,
HttpServlet,
Java,
OpenSAML,
Processing,
Receiving,
Response,
SAML 2.0,
Verifying Signature
Saturday, September 24, 2011
Dark Arduino IDE Theme
I'm totally having fun playing around with my Arduino UNO, but the Arduino IDE is a little too bright for my tastes, so I created a dark theme and posted it here.
Here's a screenshot:
All you need to do is find your 'theme' folder (it may be nested a bit but it should be in the application folder somewhere under /lib/) and replace it with this folder. I created this on a mac and it's theming version 1.0 of the IDE, but I'm guessing the theme folder is the same for all platforms. Make a backup of yours before you drop this one in just in case though.
You'll need to restart the Arduino IDE for the changes to take effect, but then you can sit back and let your eyes thank you!
Hope you like it.
Wednesday, June 8, 2011
Using Javascript to Manipulate DOM Elements in Hype
I’ve been having a lot of fun playing with Tumult’s recently-released tool called Hype for the Mac. It’s a time-line based tool for creating rich animated HTML5 content that runs natively in most modern browsers. We’ve been hearing for quite some time that Flash is no longer needed with all of HTML5 and CSS3’s capabilities, but no one had come up with a design-centric tool for creating that content. That’s where Hype has stepped in. For anyone familiar with working in Adobe’s Flash design tools, Hype should be fairly straightforward to work with. I had an interactive mobile site targeted at the iPhone finished in a few hours, with stuff animating into view, and custom buttons triggering animations between scenes. The problem I ran into though came when I wanted to start thinking about getting dynamic content from the outside world into the site instead of coding everything inside the Hype-generated container (most of the stuff lives in an ugly javascript file so don’t plan on working with content you’ve put into Hype after you’ve ‘compiled’ it down). That’s the one place I think it falls a little short. Although it’s nice that, unlike Flash, it exports to pure javascript/css, it’s still kind of an enclosed blob for all intents and purposes once you’ve moved out of the Hype application.
So, here’s the design I was trying to achieve. I had a home page that I wanted to populate with some dynamic content from my FriendFeed account via RSS. I decided to use Google’s javascript API for the RSS communication, and that’s pretty straightforward and well-documented on Google’s site. The problem I ran into was getting that code integrated with Hype. This is how I got it working.
Basically Hype has some event-hooks where you can trigger a javascript function. For what I was trying to achieve it made the most sense to tap into the hook that occurs after the animation for the home page has completed. I added a function called loadRSSContent and did the event-wiring in the GUI, which is trivially simple. Then I added a couple of lines of javascript to that function that would call another function that lives on the .html page that houses the Hype container. It’s not very well documented on the Hype site, but you can use any javascript functions/libraries within Hype that will eventually be available on the enclosing page at runtime. There is currently no support for importing these assets at design-time though (although that seems like an easy feature to add) so you’re flying-blind until you run the export and embed the container on a page. This is why I would suggest getting everything running on a test page and then only moving the pieces into Hype that are necessary to manipulate the DOM elements on the timeline. You’ll understand what I mean when you see the code.
So, on my home scene I added a Hype text box, and within it I created a div with id=“rssDiv”.
On the ‘On Animation Complete” event for the scene, the javascript in Hype looks like this:
function loadRSSContent(hypeDocument, element)
//get the content for the rssDiv from a function outside of Hype
var rssDiv = document.getElementById("rssDiv");
rssDiv.innerHTML = getRSSContent();
}
The javascript that does the work lives on the .html page (outside of the Hype container). I should also point out what I’m doing here. I’m using the initialize() function to put the RSS html into a hidden element on the page, and then the getRSSContent() function takes that content and hands it back inside the Hype container when it requests it. The reason I’m doing it this way is because the DOM gets built dynamically with Hype, so you don’t want to try to manipulate it directly from outside of the Hype container. Also, the initialize() function can run as soon as the Google API is loaded and get the content ready while Hype is doing its animation stuff.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv=“Content-Type” content=“text/html; charset=utf-8”>
<meta http-equiv=“X-UA-Compatible” content=“chrome=1”>
<meta name=“viewport” content=“user-scalable=yes, width=320px” />
<script type=“text/javascript” src=“https://www.google.com/jsapi?key=yourkey”></script>
<script src=“https://www.google.com/uds/?file=feeds&v=1” type=“text/javascript”></script>
<script type=“text/javascript”>
function initialize() {
var feed = new google.feeds.Feed(“http://friendfeed.com/kevnls?format=atom&num=4”);
feed.setNumEntries(4);
feed.load(function(result) {
if (!result.error) {
var html = ‘’;
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
html += ‘<a href=“’ + entry.link + ‘” target=“_blank”>’ + entry.title + ‘</a><br/><br/>’;
}
document.getElementById(“hiddenElement”).value = html;
}
});
}
google.setOnLoadCallback(initialize);
function getRSSContent() {
var hiddenElement = document.getElementById(“hiddenElement”);
var rssResult = hiddenElement.value;
return rssResult;
}
</script>
<title>kevnls</title>
<style>
body {
background-color: #FFFFFF;
margin: 0px;
}
</style>
</head>
<body>
<input type=“hidden” id=“hiddenElement” value=“” />
<div style =“margin:auto;width:320px”>
<!-- copy these lines to your document: -->
<div id=“kevnls_hype_container” style=“position:relative;overflow:hidden;width:320px;height:396px;”>
<script type=“text/javascript” src=“mobile_Resources/kevnls_hype_generated_script.js?43552”></script>
</div>
<!-- end copy -->
</div>
</body>
</html>
The result of all of this can be seen here: http://kevnls.com/mobile
Hopefully this design will prove useful to someone (besides me).
Until next time.
So, here’s the design I was trying to achieve. I had a home page that I wanted to populate with some dynamic content from my FriendFeed account via RSS. I decided to use Google’s javascript API for the RSS communication, and that’s pretty straightforward and well-documented on Google’s site. The problem I ran into was getting that code integrated with Hype. This is how I got it working.
Basically Hype has some event-hooks where you can trigger a javascript function. For what I was trying to achieve it made the most sense to tap into the hook that occurs after the animation for the home page has completed. I added a function called loadRSSContent and did the event-wiring in the GUI, which is trivially simple. Then I added a couple of lines of javascript to that function that would call another function that lives on the .html page that houses the Hype container. It’s not very well documented on the Hype site, but you can use any javascript functions/libraries within Hype that will eventually be available on the enclosing page at runtime. There is currently no support for importing these assets at design-time though (although that seems like an easy feature to add) so you’re flying-blind until you run the export and embed the container on a page. This is why I would suggest getting everything running on a test page and then only moving the pieces into Hype that are necessary to manipulate the DOM elements on the timeline. You’ll understand what I mean when you see the code.
So, on my home scene I added a Hype text box, and within it I created a div with id=“rssDiv”.
On the ‘On Animation Complete” event for the scene, the javascript in Hype looks like this:
function loadRSSContent(hypeDocument, element)
//get the content for the rssDiv from a function outside of Hype
var rssDiv = document.getElementById("rssDiv");
rssDiv.innerHTML = getRSSContent();
}
The javascript that does the work lives on the .html page (outside of the Hype container). I should also point out what I’m doing here. I’m using the initialize() function to put the RSS html into a hidden element on the page, and then the getRSSContent() function takes that content and hands it back inside the Hype container when it requests it. The reason I’m doing it this way is because the DOM gets built dynamically with Hype, so you don’t want to try to manipulate it directly from outside of the Hype container. Also, the initialize() function can run as soon as the Google API is loaded and get the content ready while Hype is doing its animation stuff.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv=“Content-Type” content=“text/html; charset=utf-8”>
<meta http-equiv=“X-UA-Compatible” content=“chrome=1”>
<meta name=“viewport” content=“user-scalable=yes, width=320px” />
<script type=“text/javascript” src=“https://www.google.com/jsapi?key=yourkey”></script>
<script src=“https://www.google.com/uds/?file=feeds&v=1” type=“text/javascript”></script>
<script type=“text/javascript”>
function initialize() {
var feed = new google.feeds.Feed(“http://friendfeed.com/kevnls?format=atom&num=4”);
feed.setNumEntries(4);
feed.load(function(result) {
if (!result.error) {
var html = ‘’;
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
html += ‘<a href=“’ + entry.link + ‘” target=“_blank”>’ + entry.title + ‘</a><br/><br/>’;
}
document.getElementById(“hiddenElement”).value = html;
}
});
}
google.setOnLoadCallback(initialize);
function getRSSContent() {
var hiddenElement = document.getElementById(“hiddenElement”);
var rssResult = hiddenElement.value;
return rssResult;
}
</script>
<title>kevnls</title>
<style>
body {
background-color: #FFFFFF;
margin: 0px;
}
</style>
</head>
<body>
<input type=“hidden” id=“hiddenElement” value=“” />
<div style =“margin:auto;width:320px”>
<!-- copy these lines to your document: -->
<div id=“kevnls_hype_container” style=“position:relative;overflow:hidden;width:320px;height:396px;”>
<script type=“text/javascript” src=“mobile_Resources/kevnls_hype_generated_script.js?43552”></script>
</div>
<!-- end copy -->
</div>
</body>
</html>
The result of all of this can be seen here: http://kevnls.com/mobile
Hopefully this design will prove useful to someone (besides me).
Until next time.
Tuesday, April 19, 2011
Using jQuery to Disable a Link on a Specific Day of the Week
My company has a regular scheduled downtime of our messaging-bus each Sunday. This brings down some functionality on our site, so I needed to disable the links for those resources automatically, but instead of hiding them or pointing them somewhere else, I decided it would be nice to give the user some information about what's going on right there on the page when they click on one of the disabled links. I came up with the following piece of code which will do that for any link with class="TibcoRequired":
<!--script to disable links to Tibco-reliant tests on Sundays-->
<link href="/Resources/Scripts/jQuery/css/redmond/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css"/>
<script src="/Resources/Scripts/jQuery/js/jquery-1.4.2.min.js"></script>
<script src="/Resources/Scripts/jQuery/js/jquery-ui-1.8.1.custom.min.js"></script>
<div id="dialog" title="Dialog Title" style="display:none;"><p>This test cannot be taken on Sundays due to a regular
scheduled downtime of the result-tracking system.</p></div>
<script type="text/javascript">
$('.TibcoRequired').click(function () {
var d = new Date();
var today = d.getDay();
if (today == 0) {
$('#dialog').dialog({ title: 'Message' });
return false;
}
});
</script>
<!--script-->
<!--script to disable links to Tibco-reliant tests on Sundays-->
<link href="/Resources/Scripts/jQuery/css/redmond/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css"/>
<script src="/Resources/Scripts/jQuery/js/jquery-1.4.2.min.js"></script>
<script src="/Resources/Scripts/jQuery/js/jquery-ui-1.8.1.custom.min.js"></script>
<div id="dialog" title="Dialog Title" style="display:none;"><p>This test cannot be taken on Sundays due to a regular
scheduled downtime of the result-tracking system.</p></div>
<script type="text/javascript">
$('.TibcoRequired').click(function () {
var d = new Date();
var today = d.getDay();
if (today == 0) {
$('#dialog').dialog({ title: 'Message' });
return false;
}
});
</script>
<!--script-->
Thursday, January 13, 2011
Creating a custom Model and passing a Linq to SQL query to a View in .net MVC
Another one of my 'I was trying to do this and having no luck finding solutions' posts, so I'll post my solution and hopefully it will help someone else in the same predicament.
This time I was working on a .NET MVC project, and trying to do some reporting. Basically it's a simple app that records users' steps-per-day. The users are assigned to teams and I wanted to create a page that showed the ranking of the different teams each month based on their total number of steps. As you can imagine, I had three tables. tblTeams, tblUsers, and tblSteps. So, my problem was, I have a model built using Linq to SQL, and it works fine if you're working with one of the objects that relates to its table. But, in this case I just wanted to do a simple join query, with some grouping, and return that to the view to create an html table of the different teams and their totals. So, here's how I made it work:
First off, I created a new very simple model containing the class to hold the results of the query:
namespace StepsChallenge.Models
{
public class TeamTotalsModel
{
public string TeamName { get; set; }
public string Sum { get; set; }
}
}
Then, in my view I added a reference to this model:
Inherits="System.Web.Mvc.ViewPage<IEnumerable<StepsChallenge.Models.TeamTotalsModel>>"
OK, now we're wired-up. On to the data.
I wrote a Linq to SQL query that looked like this in my controller and passed it into an instance of the model class:
public class HomeController : Controller
{
public ActionResult Index()
{
//get monthly steps for the teams
var allTeamsMonthlySteps = from stepRecord in homeModel.Steps
join userRecord in homeModel.Users
on stepRecord.EmpID equals userRecord.EmpID
join teamRecord in homeModel.Teams
on userRecord.TeamID equals teamRecord.TeamID
where stepRecord.Date.Month == DateTime.Now.Month
group stepRecord by teamRecord.TeamName into result
orderby result.Key ascending
select new TeamTotalsModel
{
TeamName = result.Key.ToString(),
Sum = result.Sum(r => r.Steps).ToString()
};
return View(allTeamsMonthlySteps);
}
}
Then finally, back at the view I wrote something like this:
<table>
<tr>
<th>
TeamName
</th>
<th>
Sum
</th>
</tr>
<% foreach (var item in Model) { %>
<tr>
<td>
<%= Html.Encode(item.TeamName) %>
</td>
<td>
<%= Html.Encode(item.Sum) %>
</td>
</tr>
<% } %>
</table>
So, this isn't all that complicated, but the problem I was having is that every search I did on almost every piece of this pointed me back to all the Visual Studio drag-and-drop BS for working with Linq to SQL models where there's a one-to-one relationship between objects and tables. And, I couldn't find much help at all on writing a Linq to SQL query with multiple joins, grouping, and 'where' clauses, all together, and passing it to the view.
Anyway, maybe some poor slob like me will stumble across this and find what he needs. Until next time.
This time I was working on a .NET MVC project, and trying to do some reporting. Basically it's a simple app that records users' steps-per-day. The users are assigned to teams and I wanted to create a page that showed the ranking of the different teams each month based on their total number of steps. As you can imagine, I had three tables. tblTeams, tblUsers, and tblSteps. So, my problem was, I have a model built using Linq to SQL, and it works fine if you're working with one of the objects that relates to its table. But, in this case I just wanted to do a simple join query, with some grouping, and return that to the view to create an html table of the different teams and their totals. So, here's how I made it work:
First off, I created a new very simple model containing the class to hold the results of the query:
namespace StepsChallenge.Models
{
public class TeamTotalsModel
{
public string TeamName { get; set; }
public string Sum { get; set; }
}
}
Then, in my view I added a reference to this model:
Inherits="System.Web.Mvc.ViewPage<IEnumerable<StepsChallenge.Models.TeamTotalsModel>>"
OK, now we're wired-up. On to the data.
I wrote a Linq to SQL query that looked like this in my controller and passed it into an instance of the model class:
public class HomeController : Controller
{
public ActionResult Index()
{
//get monthly steps for the teams
var allTeamsMonthlySteps = from stepRecord in homeModel.Steps
join userRecord in homeModel.Users
on stepRecord.EmpID equals userRecord.EmpID
join teamRecord in homeModel.Teams
on userRecord.TeamID equals teamRecord.TeamID
where stepRecord.Date.Month == DateTime.Now.Month
group stepRecord by teamRecord.TeamName into result
orderby result.Key ascending
select new TeamTotalsModel
{
TeamName = result.Key.ToString(),
Sum = result.Sum(r => r.Steps).ToString()
};
return View(allTeamsMonthlySteps);
}
}
Then finally, back at the view I wrote something like this:
<table>
<tr>
<th>
TeamName
</th>
<th>
Sum
</th>
</tr>
<% foreach (var item in Model) { %>
<tr>
<td>
<%= Html.Encode(item.TeamName) %>
</td>
<td>
<%= Html.Encode(item.Sum) %>
</td>
</tr>
<% } %>
</table>
So, this isn't all that complicated, but the problem I was having is that every search I did on almost every piece of this pointed me back to all the Visual Studio drag-and-drop BS for working with Linq to SQL models where there's a one-to-one relationship between objects and tables. And, I couldn't find much help at all on writing a Linq to SQL query with multiple joins, grouping, and 'where' clauses, all together, and passing it to the view.
Anyway, maybe some poor slob like me will stumble across this and find what he needs. Until next time.
Labels:
.NET MVC,
Custom Model,
Linq to SQL,
Passing Query to View
Friday, December 3, 2010
Apache configuration for SimpleSAMLphp on OS X Snow Leopard
I needed to do some testing with the SimpleSAMLphp (http://simplesamlphp.org/) application today. I’m not a php developer so my mac wasn’t setup to run php in Apache. That part was trivially simple to setup, but I ran into problems trying to get the SimpleSAMLphp application running in a configuration that made sense to me. What I wanted to do was install the application into my /Sites/ directory, and create another directory under /Sites/ that would act as the php site that uses it. It may seem odd to install the app in the sites directory, but I just wanted to keep the two together. So, the directory structure looked like this:
~/Sites/simplesamlphp/
~/Sites/phpsite/
Then, I wanted to change my Apache configuration so that http://localhost/ would remain the same (pointing at /Library/WebServer/Documents/), http://phpsite/ would point to ~/Sites/phpsite/, and http://phpsite/simplesaml would point to ~/Sites/simplesamlphp/www.
It took some trial and error, but I ended up with these configurations:
First I uncommented the following line in the /etc/apache2/httpd.conf file:
Include /private/etc/apache2/extra/httpd-vhosts.conf
Then I went to that file and changed the following line from this:
NameVirtualHost *:80
to this:
NameVirtualHost 127.0.0.1:80
and, added this:
<VirtualHost phpsite>
DocumentRoot "/Users/sysadmin/Sites/phpsite"
ServerName phpsite
<Directory "/Users/sysadmin/Sites/phpsite">
Options FollowSymLinks MultiViews Includes
AllowOverride All
Order allow,deny
Allow from all
</Directory>
Alias /simplesaml /Users/sysadmin/Sites/simplesamlphp/www
</VirtualHost>
Then I went to my /etc/hosts file and added this line:
127.0.0.1 phpsite
Then, a quick Apache restart and everything was running smoothly.
http://localhost - still points to /Library/WebServer/Documents
http://localhost/~sysadmin/ - still points to ~/Sites/
http://phpsite - points to ~/Sites/phpsite
http://phpsite/simplesaml - points to ~/Sites/simplesamlphp/www
Obviously you wouldn’t want to configure it this way if this was a production server, but when I’m doing development work like this I’d prefer to keep everything under my own home folder.
~/Sites/simplesamlphp/
~/Sites/phpsite/
Then, I wanted to change my Apache configuration so that http://localhost/ would remain the same (pointing at /Library/WebServer/Documents/), http://phpsite/ would point to ~/Sites/phpsite/, and http://phpsite/simplesaml would point to ~/Sites/simplesamlphp/www.
It took some trial and error, but I ended up with these configurations:
First I uncommented the following line in the /etc/apache2/httpd.conf file:
Include /private/etc/apache2/extra/httpd-vhosts.conf
Then I went to that file and changed the following line from this:
NameVirtualHost *:80
to this:
NameVirtualHost 127.0.0.1:80
and, added this:
<VirtualHost phpsite>
DocumentRoot "/Users/sysadmin/Sites/phpsite"
ServerName phpsite
<Directory "/Users/sysadmin/Sites/phpsite">
Options FollowSymLinks MultiViews Includes
AllowOverride All
Order allow,deny
Allow from all
</Directory>
Alias /simplesaml /Users/sysadmin/Sites/simplesamlphp/www
</VirtualHost>
Then I went to my /etc/hosts file and added this line:
127.0.0.1 phpsite
Then, a quick Apache restart and everything was running smoothly.
http://localhost - still points to /Library/WebServer/Documents
http://localhost/~sysadmin/ - still points to ~/Sites/
http://phpsite - points to ~/Sites/phpsite
http://phpsite/simplesaml - points to ~/Sites/simplesamlphp/www
Obviously you wouldn’t want to configure it this way if this was a production server, but when I’m doing development work like this I’d prefer to keep everything under my own home folder.
Subscribe to:
Posts (Atom)