Dropbox integration with Sitecore
So consider this an Alpha-release, I’ve used this Dropbox Context we build in the last blog post “introduction to the dropbox api“. I’ve changend it a little bit so I can be used to make a static .aspx file where you can build the accestoken file. The changend Dropbox context is shown below
public class DropboxContext { private OAuthToken _accessToken; private string _applicationKey; private string _applicationSecret; private string _accessTokenPath; private OAuthRestClient _apiRestClient; private OAuthRestClient _contentiRestClient; private string _version = "1.0"; public DropboxContext(DropBoxSettings settings) { _applicationKey = settings.ApplicationKey; _applicationSecret = settings.ApplicationSecret; _accessTokenPath = settings.AccessTokenPath; SetupApiRestClient(); SetupContentRestClient(); } private void SetupApiRestClient() { _apiRestClient = new OAuthRestClient("https://api.dropbox.com"); _apiRestClient.ConsumerKey = _applicationKey; _apiRestClient.ConsumerSecret = _applicationSecret; _apiRestClient.Version = _version; } private void SetupContentRestClient() { _contentiRestClient = new OAuthRestClient("https://api-content.dropbox.com"); _contentiRestClient.ConsumerKey = _applicationKey; _contentiRestClient.ConsumerSecret = _applicationSecret; _contentiRestClient.Version = _version; } public OAuthToken GetRequestToken() { return new OAuthToken(_apiRestClient.GetRequestToken("1/oauth/request_token")); } public void AuthorizeToken(OAuthToken token) { Process.Start("https://www.dropbox.com/1/oauth/authorize?oauth_token=" + token.Token); } public void SetAccesToken() { OAuthToken accesToken = GetAccesTokenFromStore(); if (accesToken == null) { OAuthToken requestToken = GetRequestToken(); AuthorizeToken(requestToken); // accesToken = UpgradeRequsetTokenToAccesToken(requestToken); } _accessToken = accesToken; } public void UpgradeRequsetTokenToAccesToken(OAuthToken requestToken) { if (requestToken == null) return; string tokenString = _apiRestClient.GetAccesToken("1/oauth/access_token", requestToken); OAuthToken token = new OAuthToken(tokenString); StoreAccessToken(tokenString); } public void StoreAccessToken(string tokenString) { FileStream fs = File.Open(_accessTokenPath, FileMode.OpenOrCreate, FileAccess.ReadWrite); StreamWriter sw = new StreamWriter(fs); sw.WriteLine(tokenString); sw.Close(); fs.Close(); } private OAuthToken GetAccesTokenFromStore() { OAuthToken token = null; string tokenString = TokenStringFromFile(); if (!string.IsNullOrEmpty(tokenString)) token = new OAuthToken(tokenString); return token; } private string TokenStringFromFile() { FileStream fs = File.Open(_accessTokenPath, FileMode.OpenOrCreate, FileAccess.ReadWrite); StreamReader sw = new StreamReader(fs); string tokenString = sw.ReadToEnd(); sw.Close(); fs.Close(); return tokenString; } public AccountInfo AccountInfo() { var response = _apiRestClient.QueryServer<AccountInfo>("1/account/info", GetAccesTokenFromStore()); return response; } public FileEntry GetMetadata(string path) { string requestPath = "1/metadata/dropbox"; if (!String.IsNullOrEmpty(path)) requestPath = String.Format("{0}{1}", requestPath, path.ToLower()); var response = _apiRestClient.QueryServer<FileEntry>(requestPath, GetAccesTokenFromStore()); return response; } public byte[] GetFile(string path) { var response = _contentiRestClient.GetStream("1/files/" + path, GetAccesTokenFromStore()); return response; } }
Since this i an early release I’ve build this simple .aspx page that provides the user with a link to allow acces to their dropbox and writes the accesToken to a flat file, the file can we then copy to the sitecore data folder for later use.
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> Token : <%# Token.Token %> <br /> Token Secret <%# Token.TokenSecret %><br /> <a href="https://www.dropbox.com/1/oauth/authorize?oauth_token=<%# Token.Token %>" target="_blank">Autherize Dropbox</a> <asp:Button ID="Button2" Text="Store Token" runat="server" onclick="Button2_Click" /> </div> </form> </body> </html>
public partial class AutherizeDropBox : System.Web.UI.Page { private OAuthToken token; private DropboxContext context; protected void Page_Load(object sender, EventArgs e) { context = new DropboxContext(SitecoreDropboxSettingsRepository.Get()); if (!IsPostBack) { DataBind(); } } protected void Page_PreRender(object sender, EventArgs e) { } protected OAuthToken Token { get { if (ViewState["token"] != null) { token = new OAuthToken(); token.Token = ViewState["token"].ToString(); token.TokenSecret = ViewState["tokenSecret"].ToString(); } if (token == null) { token = context.GetRequestToken(); ViewState["token"] = token.Token; ViewState["tokenSecret"] = token.TokenSecret; } return token; } } protected void Button2_Click(object sender, EventArgs e) { OAuthToken t = Token; context.UpgradeRequsetTokenToAccesToken(t); } }
For dropbox context to work we need a valid dropboxcontext setting datacontainer. Information for this i stored as settings in the web.config
<setting name="ApplicationKey" value="YOURAPPLIKATIONKEY" /> <setting name="ApplicationSecret" value="YOURAPPLIKATIONSECRET" /> <setting name="AccessTokenPath" value="PATHTOYOUFILETHATCONTAINSACCESSTOKEN" />
For this simple version of dropbox syncing I’ve build a simple sitecore task to run a “Download all service” from dropbox. It’s start by deleting all old the previously downloaded content from dropbox and the download from all content from dropbox root ie. “dropbox/”.
public class DropboxDownloadTask { CreateMediaItemService _createMediaItemService; public void Execute() { _createMediaItemService = new CreateMediaItemService(); GetRoot(); } private void GetRoot() { MetatadataRepository metatadataRepository = new MetatadataRepository(); FileEntry root = metatadataRepository.Get(); IterateOverChildren(root); } private void IterateOverChildren(FileEntry folderEntry) { foreach (FileEntry entry in folderEntry.Contents) { if (entry.Is_Dir) IterateOverChildren(GetFileEntry(entry)); else _createMediaItemService.CreateMediaItem(entry); } } private FileEntry GetFileEntry(FileEntry entry) { MetatadataRepository metatadataRepository = new MetatadataRepository(); return metatadataRepository.Get(entry); } }
The task uses some file- and metadata-repositories that communicate with dropboxcontext and a simple settings repository.
public static class SitecoreDropboxSettingsRepository { public static DropBoxSettings Get() { DropBoxSettings settings = new DropBoxSettings(); settings.ApplicationKey = Settings.GetSetting("ApplicationKey"); settings.ApplicationSecret = Settings.GetSetting("ApplicationSecret"); string path = Settings.GetSetting("AccessTokenPath"); settings.AccessTokenPath = HttpContext.Current.Server.MapPath(path); return settings; } }
public class MetatadataRepository { private DropboxContext _context; public MetatadataRepository() { _context = new DropboxContext(SitecoreDropboxSettingsRepository.Get()); } public FileEntry Get() { return _context.GetMetadata(String.Empty); } public FileEntry Get(FileEntry entry) { return _context.GetMetadata(entry.Path); } }
public class FileDataRepository { private DropboxContext _context; public FileDataRepository() { _context = new DropboxContext(SitecoreDropboxSettingsRepository.Get()); } public byte[] Get(FileEntry entry) { return _context.GetFile(entry.Root+entry.Path); } }
Finally the task have a simple service that is responsible for adding a media stream to the sitecore media library.
public class CreateMediaItemService { private MediaCreator _creator; private MediaCreatorOptions _mediaOptions; public void CreateMediaItem(FileEntry folderName) { using (new SecurityDisabler()) AddStreamToMediaLibrary(folderName); } private void AddStreamToMediaLibrary(FileEntry folderName) { if(folderName.Parent == "dropbox") SetMediaOptions(folderName.Parent, folderName.Name); else SetMediaOptions(String.Format("dropbox/{0}",folderName.Parent), folderName.Name); _creator = new MediaCreator(); FileDataRepository fileDataRepository = new FileDataRepository(); byte[] bytes = fileDataRepository.Get(folderName); MemoryStream theMemStream = new MemoryStream(); theMemStream.Write(bytes, 0, bytes.Length); try { _creator.CreateFromStream(theMemStream, folderName.Name, _mediaOptions); } catch (Exception e) { throw new Exception(folderName.Name, e); } } private void SetMediaOptions(string sitecorePath, string itemName) { _mediaOptions = new MediaCreatorOptions(); _mediaOptions.FileBased = false; _mediaOptions.IncludeExtensionInItemName = false; _mediaOptions.KeepExisting = false; _mediaOptions.Versioned = false; _mediaOptions.Destination = String.Format("/sitecore/media library/{0}/{1}", sitecorePath, ItemUtil.ProposeValidItemName(itemName)); _mediaOptions.Database = Database.GetDatabase("master"); } }
Offcourse there are room for improvement and the code could use some cleaning up to is more “Clean Code” Uncle Bob. You can download the source code for this project at Pentia. I’m more then interrested in hearing what features should be on the todo-list so let me know.