From f22fc3ac17a3632e0556761b343a8fed1f58a8b5 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 11:11:44 -0400 Subject: [PATCH 01/30] Clean up code. Makes the code a more readable. Made the factory methods, e.g. + (instancetype)TBPageWithURL.. into init methods, e.g. - (instancetype)initWithURL... No functional changes. --- Mac App/TBSiteDocument.m | 2 +- Shared/TBAsset.m | 25 +++- Shared/TBPage.h | 5 +- Shared/TBPage.m | 67 +++++++-- Shared/TBPost.h | 4 +- Shared/TBPost.m | 90 ++++++++---- Shared/TBSite.h | 2 +- Shared/TBSite.m | 298 +++++++++++++++++++++++++++++---------- 8 files changed, 361 insertions(+), 132 deletions(-) diff --git a/Mac App/TBSiteDocument.m b/Mac App/TBSiteDocument.m index d6086bb..e75a47b 100644 --- a/Mac App/TBSiteDocument.m +++ b/Mac App/TBSiteDocument.m @@ -156,7 +156,7 @@ - (void)windowControllerDidLoadNib:(NSWindowController *)windowController { } - (BOOL)readFromURL:(NSURL *)URL ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError { - self.site = [TBSite siteWithRoot:URL]; + self.site = [[TBSite alloc] initWithRoot:URL]; self.site.delegate = self; BOOL success = [self.site parsePosts:outError]; diff --git a/Shared/TBAsset.m b/Shared/TBAsset.m index eed85d7..af324e9 100644 --- a/Shared/TBAsset.m +++ b/Shared/TBAsset.m @@ -12,27 +12,38 @@ @implementation TBAsset + (NSArray *)assetsFromDirectory:(NSURL*)URL error:(NSError **)error { - NSMutableArray *assets = [NSMutableArray array]; + NSArray *properties = @[NSURLTypeIdentifierKey, NSURLLocalizedNameKey, NSURLIsDirectoryKey]; - NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:URL includingPropertiesForKeys:properties options:NSDirectoryEnumerationSkipsHiddenFiles|NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:nil]; + + NSDirectoryEnumerator *enumerator; + enumerator = [[NSFileManager defaultManager] enumeratorAtURL:URL + includingPropertiesForKeys:properties + options:NSDirectoryEnumerationSkipsHiddenFiles|NSDirectoryEnumerationSkipsSubdirectoryDescendants + errorHandler:nil]; + for (NSURL *assetURL in enumerator) { - NSDictionary *resourceValues = [assetURL resourceValuesForKeys:properties error:error]; - if (!resourceValues) return nil; - TBAsset *asset = [[self class] new]; + if (!resourceValues) { + return nil; + } + + TBAsset *asset = [[TBAsset alloc] init]; + asset.URL = assetURL; asset.displayName = resourceValues[NSURLLocalizedNameKey]; asset.type = resourceValues[NSURLTypeIdentifierKey]; if ([resourceValues[NSURLIsDirectoryKey] boolValue]) { asset.children = [[self class] assetsFromDirectory:assetURL error:error]; - if (!asset.children) return nil; + + if (!asset.children) { + return nil; + } } [assets addObject:asset]; - } [assets sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES]]]; diff --git a/Shared/TBPage.h b/Shared/TBPage.h index f0974f7..98e1544 100644 --- a/Shared/TBPage.h +++ b/Shared/TBPage.h @@ -26,6 +26,7 @@ /*! Create a TBPage object from a file on-disk. + @param URL A filesystem URL pointing to the page file. @param site @@ -36,9 +37,7 @@ @return An instance of TBPage, or nil if an error was encountered. */ -+ (instancetype)pageWithURL:(NSURL *)URL - inSite:(TBSite *)site - error:(NSError **)error; +- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error; /*! @property URL diff --git a/Shared/TBPage.m b/Shared/TBPage.m index f39d2e1..a0b679b 100644 --- a/Shared/TBPage.m +++ b/Shared/TBPage.m @@ -11,63 +11,100 @@ @implementation TBPage -+ (instancetype)pageWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { - TBPage *page = [super new]; - if (page) { - page.URL = URL; - page.site = site; - [page parse:error]; +- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { + if (self = [super init]) { + self.URL = URL; + self.site = site; + + [self parse:error]; } - return page; + + return self; } - (BOOL)parse:(NSError **)error { [self loadContent]; [self parseTitle]; [self parseStylesheets]; + return YES; } - (void)loadContent { - NSString *content = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; - self.content = content; + self.content = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; } +/*! + * Parse the title from the content. + * + * Titles are optional. + * They take the following form: + * + */ - (void)parseTitle { - if (!self.content || ![self.content length]) return; - // Titles are optional. They take the following form: - // + if (!self.content || ![self.content length]) { + // No content found, return + return; + } + NSMutableString *content = [self.content mutableCopy]; + NSRegularExpression *headerRegex = [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; + NSRange firstLineRange = NSMakeRange(0, [content rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location); NSString *firstLine = [content substringWithRange:firstLineRange]; + NSTextCheckingResult *titleResult = [headerRegex firstMatchInString:firstLine options:0 range:NSMakeRange(0, firstLine.length)]; + if (titleResult) { + // Found the title! self.title = [firstLine substringWithRange:[titleResult rangeAtIndex:1]]; + [content deleteCharactersInRange:NSMakeRange(firstLineRange.location, firstLineRange.length + 1)]; } + self.content = content; } +/*! + * Parse the stylesheet from the content. + * + * Stylesheets are optional. + * They take the following form: + * + * + * They are the second line in the file if there is a title; first if there is not a title. + */ - (void)parseStylesheets { - if (!self.content || ![self.content length]) return; - // Stylsheets are also optional. They are on the second line (or first if there is no title), and look like this: - // + if (!self.content || ![self.content length]) { + return; + } + NSMutableString *content = [self.content mutableCopy]; + NSRegularExpression *stylesheetsRegex = [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; + NSRange secondLineRange = NSMakeRange(0, [content rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location); NSString *secondLine = [content substringWithRange:secondLineRange]; + NSTextCheckingResult *stylesheetsResult = [stylesheetsRegex firstMatchInString:secondLine options:0 range:NSMakeRange(0, secondLine.length)]; + if (stylesheetsResult) { NSString *rawMatch = [secondLine substringWithRange:[stylesheetsResult rangeAtIndex:1]]; + NSArray *stylesheetNames = [rawMatch componentsSeparatedByString:@", "]; + NSMutableArray *stylesheetDictionaries = [NSMutableArray array]; + for (NSString *stylesheetName in stylesheetNames) { [stylesheetDictionaries addObject:@{@"stylesheetName": stylesheetName}]; } + self.stylesheets = stylesheetDictionaries; + [content deleteCharactersInRange:secondLineRange]; } + self.content = content; } diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 20f86db..7611f43 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -39,9 +39,7 @@ @return A TBPost object, or nil if an error was encountered. */ -+ (instancetype)postWithURL:(NSURL *)URL - inSite:(TBSite *)site - error:(NSError **)error; +- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error; /*! Parse the contents of the markdownContent property, saving the HTML output diff --git a/Shared/TBPost.m b/Shared/TBPost.m index f72e739..6b31c04 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -16,80 +16,122 @@ @implementation TBPost -+ (instancetype)postWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { - return (TBPost *)[super pageWithURL:URL inSite:site error:error]; +- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { + return [super initWithURL:URL inSite:site error:error]; } -- (BOOL)parse:(NSError **)error { - - [self loadMarkdownContent]; - - if (![self parseDateAndSlug:error]) +- (BOOL)parse:(NSError **)error { + if (![self parseDateAndSlug:error]) { return NO; - + } + + [self loadMarkdownContent]; [self parseTitle]; return YES; } -- (void)loadMarkdownContent; { - NSString *markdownContent = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; - self.markdownContent = markdownContent; +- (void)loadMarkdownContent { + self.markdownContent = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; } +/*! + * Extracts the title from the markdown contents. + * + * Titles are optional. + * Titles are defined as a single '#' header on the first line of the document. + * Title must have '#' on both sides, e.g. '# Title of Post #' + */ - (void)parseTitle { - // Titles are optional. A single # header on the first line of the document is regarded as the title. - if (!self.markdownContent || ![self.markdownContent length]) return; + if (!self.markdownContent || ![self.markdownContent length]) { + // No markdown content found, return + return; + } + NSMutableString *markdownContent = [self.markdownContent mutableCopy]; + static NSRegularExpression *headerRegex; - if (headerRegex == nil) + + if (headerRegex == nil) { headerRegex = [NSRegularExpression regularExpressionWithPattern:@"#[ \\t](.*)[ \\t]#" options:0 error:nil]; + } + NSRange firstLineRange = NSMakeRange(0, [markdownContent rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location); - if (firstLineRange.length == NSNotFound) return; + + if (firstLineRange.length == NSNotFound) { + // Can't find a first header + return; + } + NSString *firstLine = [markdownContent substringWithRange:firstLineRange]; + NSTextCheckingResult *titleResult = [headerRegex firstMatchInString:firstLine options:0 range:NSMakeRange(0, firstLine.length)]; + if (titleResult) { self.title = [firstLine substringWithRange:[titleResult rangeAtIndex:1]]; + [markdownContent deleteCharactersInRange:NSMakeRange(firstLineRange.location, firstLineRange.length + 1)]; } + + // Remove the first new line after the title from the content [markdownContent deleteCharactersInRange:[markdownContent rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]]]; + self.markdownContent = markdownContent; } - (BOOL)parseDateAndSlug:(NSError **)error { - // Dates and slugs are parsed from a pattern in the post file name. + // Dates and slugs are parsed from a pattern in the post file name static NSRegularExpression *fileNameRegex; - if (fileNameRegex == nil) + + if (fileNameRegex == nil) { fileNameRegex = [NSRegularExpression regularExpressionWithPattern:@"^(\\d+-\\d+-\\d+)-(.*)" options:0 error:nil]; + } + NSString *fileName = [self.URL.lastPathComponent stringByDeletingPathExtension]; + NSTextCheckingResult *fileNameResult = [fileNameRegex firstMatchInString:fileName options:0 range:NSMakeRange(0, fileName.length)]; + if (fileNameResult) { NSDateFormatter *fileNameDateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd"]; + self.date = [fileNameDateFormatter dateFromString:[fileName substringWithRange:[fileNameResult rangeAtIndex:1]]]; self.slug = [fileName substringWithRange:[fileNameResult rangeAtIndex:2]]; - } - else { - if (error) *error = TBError.badPostFileName(self.URL); + } else { + // No date found + + if (error) { + *error = TBError.badPostFileName(self.URL); + } + return NO; } + return YES; } - (void)parseMarkdownContent { - if (!self.markdownContent || ![self.markdownContent length]) return; - // Create and fill a buffer for with the raw markdown data. - if ([self.markdownContent length] == 0) return; + if (!self.markdownContent || ![self.markdownContent length]) { + // No markdown content found, return + return; + } + + // Create and fill a buffer for with the raw markdown data struct sd_callbacks callbacks; struct html_renderopt options; + const char *rawMarkdown = [self.markdownContent cStringUsingEncoding:NSUTF8StringEncoding]; struct buf *smartyPantsOutputBuffer = bufnew(1); + sdhtml_smartypants(smartyPantsOutputBuffer, (const unsigned char *)rawMarkdown, strlen(rawMarkdown)); - // Parse the markdown into a new buffer using Sundown. + // Parse the markdown into a new buffer using Sundown struct buf *outputBuffer = bufnew(64); + sdhtml_renderer(&callbacks, &options, 0); + struct sd_markdown *markdown = sd_markdown_new(0, 16, &callbacks, &options); + sd_markdown_render(outputBuffer, smartyPantsOutputBuffer->data, smartyPantsOutputBuffer->size, markdown); sd_markdown_free(markdown); diff --git a/Shared/TBSite.h b/Shared/TBSite.h index 80d04ce..2e9da67 100644 --- a/Shared/TBSite.h +++ b/Shared/TBSite.h @@ -34,7 +34,7 @@ A TBSite instance, initialized to represent the site folder at the given root directory. */ -+ (instancetype)siteWithRoot:(NSURL *)root; +- (instancetype)initWithRoot:(NSURL *)root; /*! Process the entire site, writing the output into the destination directory. diff --git a/Shared/TBSite.m b/Shared/TBSite.m index 1bc5edb..d0d26cb 100644 --- a/Shared/TBSite.m +++ b/Shared/TBSite.m @@ -23,25 +23,31 @@ @implementation TBSite #pragma mark - Initialization -+ (instancetype)siteWithRoot:(NSURL *)root { - TBSite *site = [TBSite new]; - site.root = root; - site.destination = [root URLByAppendingPathComponent:@"Output" isDirectory:YES]; - site.sourceDirectory = [root URLByAppendingPathComponent:@"Source" isDirectory:YES]; - site.postsDirectory = [root URLByAppendingPathComponent:@"Posts" isDirectory:YES]; - site.templatesDirectory = [root URLByAppendingPathComponent:@"Templates" isDirectory:YES]; - NSURL *metadataURL = [root URLByAppendingPathComponent:@"Info.plist" isDirectory:NO]; - NSData *metadataData = [NSData dataWithContentsOfURL:metadataURL]; - site.metadata = [NSPropertyListSerialization propertyListFromData:metadataData mutabilityOption:NSPropertyListMutableContainersAndLeaves format:nil errorDescription:nil]; - if (!site.metadata) - [@{} writeToURL:metadataURL atomically:NO]; - return site; +- (instancetype)initWithRoot:(NSURL *)root { + if (self = [super init]) { + self.root = root; + + self.destination = [root URLByAppendingPathComponent:@"Output" isDirectory:YES]; + self.sourceDirectory = [root URLByAppendingPathComponent:@"Source" isDirectory:YES]; + self.postsDirectory = [root URLByAppendingPathComponent:@"Posts" isDirectory:YES]; + self.templatesDirectory = [root URLByAppendingPathComponent:@"Templates" isDirectory:YES]; + + NSURL *metadataURL = [root URLByAppendingPathComponent:@"Info.plist" isDirectory:NO]; + NSData *metadataData = [NSData dataWithContentsOfURL:metadataURL]; + + self.metadata = [NSPropertyListSerialization propertyListFromData:metadataData mutabilityOption:NSPropertyListMutableContainersAndLeaves format:nil errorDescription:nil]; + + if (!self.metadata) { + [@{} writeToURL:metadataURL atomically:NO]; + } + } + + return self; } #pragma mark - Site Processing - (BOOL)process:(NSError **)error { - if (![self loadRawDefaultTemplate:error]) return NO; @@ -63,103 +69,151 @@ - (BOOL)process:(NSError **)error { if (![self processSourceDirectory:error]) return NO; - return YES; - + return YES; } #pragma mark - Template Loading - (BOOL)loadRawDefaultTemplate:(NSError **)error { NSURL *defaultTemplateURL = [self.templatesDirectory URLByAppendingPathComponent:@"Default.mustache" isDirectory:NO]; + self.rawDefaultTemplate = [NSString stringWithContentsOfURL:defaultTemplateURL encoding:NSUTF8StringEncoding error:error]; - if (!self.rawDefaultTemplate) return NO; + + if (!self.rawDefaultTemplate) { + return NO; + } + return YES; } - (BOOL)loadPostTemplate:(NSError **)error { NSURL *postPartialURL = [self.templatesDirectory URLByAppendingPathComponent:@"Post.mustache" isDirectory:NO]; - if (![[NSFileManager defaultManager] fileExistsAtPath:postPartialURL.path]) { - if (error) + + if ([[NSFileManager defaultManager] fileExistsAtPath:postPartialURL.path] == NO) { + // No post template found + + if (error) { *error = TBError.missingPostPartial(postPartialURL); + } + return NO; } + NSString *rawPostPartial = [NSString stringWithContentsOfURL:postPartialURL encoding:NSUTF8StringEncoding error:error]; - if (!rawPostPartial) return NO; + + if (!rawPostPartial) { + // No content in post template + return NO; + } + NSString *rawPostTemplate = [self.rawDefaultTemplate stringByReplacingOccurrencesOfString:@"{{{content}}}" withString:rawPostPartial]; + self.postTemplate = [GRMustacheTemplate templateFromString:rawPostTemplate error:error]; - if (!self.postTemplate) return NO; + + if (!self.postTemplate) { + // Could not create template + return NO; + } + return YES; } #pragma mark - Post Processing - (BOOL)parsePosts:(NSError **)error { - - // Verify that the Posts directory exists and is a directory. + // Verify that the Posts directory exists and is a directory BOOL postsDirectoryIsDirectory = NO; BOOL postsDirectoryExists = [[NSFileManager defaultManager] fileExistsAtPath:self.postsDirectory.path isDirectory:&postsDirectoryIsDirectory]; - if (!postsDirectoryIsDirectory || !postsDirectoryExists){ + + if (!postsDirectoryIsDirectory || !postsDirectoryExists) { if (error) { *error = TBError.missingPostsDirectory(self.postsDirectory); } + return NO; } - - // Parse the contents of the Posts directory into individual TBPost objects. + + // Parse the contents of the Posts directory into individual TBPost objects NSMutableArray *posts = [NSMutableArray array]; NSArray *postsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:self.postsDirectory includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:error]; - if (!postsDirectoryContents) return NO; + + if (!postsDirectoryContents) { + return NO; + } + for (NSURL *postURL in postsDirectoryContents) { - TBPost *post = [TBPost postWithURL:postURL inSite:self error:error]; + TBPost *post = [[TBPost alloc] initWithURL:postURL inSite:self error:error]; [post parseMarkdownContent]; - if (post) [posts addObject:post]; + + if (post) { + [posts addObject:post]; + } } - posts = [NSMutableArray arrayWithArray:[[posts reverseObjectEnumerator] allObjects]]; - self.posts = posts; + + self.posts = [NSMutableArray arrayWithArray:[[posts reverseObjectEnumerator] allObjects]]; // Prepare the asset object tree self.templateAssets = [TBAsset assetsFromDirectory:self.templatesDirectory error:error]; - if (!self.templateAssets) return NO; + + if (!self.templateAssets) { + return NO; + } + self.sourceAssets = [TBAsset assetsFromDirectory:self.sourceDirectory error:error]; - if (!self.sourceAssets) return NO; + + if (!self.sourceAssets) { + return NO; + } return YES; } - (BOOL)writePosts:(NSError **)error { - for (TBPost *post in self.posts) { - post.stylesheets = @[@{@"stylesheetName": @"post"}]; - // Create the path to the folder where we are going to write the post file. + // Create the path to the folder where we are going to write the post file // The directory structure we create is /YYYY/MM/DD/slug/ + NSDateFormatter *postPathFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy/MM/dd"]; NSString *directoryStructure = [postPathFormatter stringFromDate:post.date]; + NSURL *destinationDirectory = [[self.destination URLByAppendingPathComponent:directoryStructure isDirectory:YES] URLByAppendingPathComponent:post.slug isDirectory:YES]; - if (![[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error]) + + // Create the destination directory + if ([[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error] == NO) return NO; - // Filter the markdownContent of the post. + // Filter the markdownContent of the post NSString *originalContent = post.markdownContent; + NSString *filteredMarkdownContent = [self filteredContent:(originalContent ?: @"") fromFile:post.URL error:error]; - if (!filteredMarkdownContent) + + if (!filteredMarkdownContent) { return NO; + } + post.markdownContent = filteredMarkdownContent; + [post parseMarkdownContent]; + post.markdownContent = originalContent; - // Set up the template loader with this post's content, and then render it all into the post template. + // Set up the template loader with this post's content, and then render it all into the post template NSString *renderedContent = [self.postTemplate renderObject:post error:error]; - if (!renderedContent) + + if (!renderedContent) { return NO; + } // Write the post to the destination directory. NSURL *destinationURL = [destinationDirectory URLByAppendingPathComponent:@"index.html" isDirectory:NO]; - if (![renderedContent writeToURL:destinationURL atomically:YES encoding:NSUTF8StringEncoding error:error]) - return NO; - + + if (![renderedContent writeToURL:destinationURL atomically:YES encoding:NSUTF8StringEncoding error:error]) { + // Could not write index.html + return NO; + } } return YES; @@ -170,14 +224,31 @@ - (BOOL)writePosts:(NSError **)error { - (BOOL)writeFeed:(NSError **)error { NSURL *templateURL = [self.templatesDirectory URLByAppendingPathComponent:@"Feed.mustache"]; - if (![[NSFileManager defaultManager] fileExistsAtPath:templateURL.path]) return YES; + + if ([[NSFileManager defaultManager] fileExistsAtPath:templateURL.path] == NO) { + // Could not find feed template + return YES; + } + GRMustacheTemplate *template = [GRMustacheTemplate templateFromContentsOfURL:templateURL error:error]; - if (!template) return NO; + + if (!template) { + return NO; + } + NSString *contents = [template renderObject:self error:error]; - if (!contents) return NO; + + if (!contents) { + return NO; + } + NSURL *destination = [self.destination URLByAppendingPathComponent:@"feed.xml"]; - if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) + + if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { + // Could not write feed.xml return NO; + } + return YES; } @@ -186,141 +257,212 @@ - (BOOL)writeFeed:(NSError **)error { - (BOOL)verifySourceDirectory:(NSError **)error { BOOL sourceDirectoryIsDirectory = NO; BOOL sourceDirectoryExists = [[NSFileManager defaultManager] fileExistsAtPath:self.sourceDirectory.path isDirectory:&sourceDirectoryIsDirectory]; - if (!sourceDirectoryIsDirectory || !sourceDirectoryExists){ - if (error) *error = TBError.missingSourceDirectory(self.sourceDirectory); + + if (!sourceDirectoryIsDirectory || !sourceDirectoryExists) { + if (error) { + *error = TBError.missingSourceDirectory(self.sourceDirectory); + } + return NO; } + return YES; } - (BOOL)processSourceDirectory:(NSError **)error { - NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:self.sourceDirectory includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:^BOOL(NSURL *url, NSError *enumeratorError) { + NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:self.sourceDirectory + includingPropertiesForKeys:nil + options:NSDirectoryEnumerationSkipsHiddenFiles + errorHandler:^BOOL(NSURL *url, NSError *enumeratorError) { return YES; }]; + for (NSURL *URL in enumerator) { - BOOL URLIsDirectory = NO; + [[NSFileManager defaultManager] fileExistsAtPath:URL.path isDirectory:&URLIsDirectory]; - if (URLIsDirectory) continue; + + if (URLIsDirectory) { + continue; + } - if (![self processSourceFile:URL error:error]) + if (![self processSourceFile:URL error:error]) { return NO; - + } } + return YES; } - (BOOL)processSourceFile:(NSURL *)URL error:(NSError **)error { NSString *extension = [URL pathExtension]; NSString *relativePath = [URL.path stringByReplacingOccurrencesOfString:self.sourceDirectory.path withString:@""]; + NSURL *destinationURL = [[self.destination URLByAppendingPathComponent:relativePath] URLByStandardizingPath]; NSURL *destinationDirectory = [destinationURL URLByDeletingLastPathComponent]; - if (![[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error]) + + if (![[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error]) { + // Unable to create directory structure return NO; + } + [[NSFileManager defaultManager] removeItemAtURL:destinationURL error:nil]; if ([extension isEqualToString:@"mustache"]) { - TBPage *page = [TBPage pageWithURL:URL inSite:self error:nil]; + TBPage *page = [[TBPage alloc] initWithURL:URL inSite:self error:nil]; + NSURL *pageDestination = [[destinationURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"html"]; - if (![self writePage:page toDestination:pageDestination error:error]) + + if (![self writePage:page toDestination:pageDestination error:error]) { return NO; - } - else + } + } else { + // Not a mustache file, copy without processing [[NSFileManager defaultManager] copyItemAtURL:URL toURL:destinationURL error:error]; + } + return YES; } - (BOOL)writePage:(TBPage *)page toDestination:(NSURL *)destination error:(NSError **)error { - if (!page) return NO; + if (!page) { + return NO; + } + NSString *rawPageTemplate = [self.rawDefaultTemplate stringByReplacingOccurrencesOfString:@"{{{content}}}" withString:page.content]; + GRMustacheTemplate *pageTemplate = [GRMustacheTemplate templateFromString:rawPageTemplate error:error]; - if (!pageTemplate) return NO; + + if (!pageTemplate) { + return NO; + } + NSString *renderedPage = [pageTemplate renderObject:page error:error]; - if (!renderedPage) return NO; - if (![renderedPage writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) + + if (!renderedPage) { + return NO; + } + + if (![renderedPage writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { return NO; + } + return YES; } #pragma mark - Filters - (NSString *)filteredContent:(NSString *)content fromFile:(NSURL *)file error:(NSError **)error { - NSArray *filterPaths = self.metadata[TBSiteFilters]; - if (!filterPaths || ![filterPaths count]) + + if (!filterPaths || ![filterPaths count]) { return content; + } NSURL *scriptsURL = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationScriptsDirectory inDomains:NSUserDomainMask][0]; NSArray *arguments = @[self.root.path, file.path]; for (NSString *filterPath in filterPaths) { - NSURL *filterURL = [scriptsURL URLByAppendingPathComponent:filterPath]; + NSUserUnixTask *filter = [[NSUserUnixTask alloc] initWithURL:filterURL error:error]; - if (!filter) return content; + + if (!filter) { + return content; + } NSPipe *standardError = [NSPipe pipe]; + NSPipe *standardInput = [NSPipe pipe]; + NSPipe *standardOutput = [NSPipe pipe]; + filter.standardError = standardError.fileHandleForWriting; - NSPipe *standardInput = [NSPipe pipe]; filter.standardInput = standardInput.fileHandleForReading; - NSPipe *standardOutput = [NSPipe pipe]; filter.standardOutput = standardOutput.fileHandleForWriting; + [standardInput.fileHandleForWriting writeData:[content dataUsingEncoding:NSUTF8StringEncoding]]; [standardInput.fileHandleForWriting closeFile]; __block NSError *blockError = nil; dispatch_group_t group = dispatch_group_create(); + dispatch_async(dispatch_get_current_queue(), ^{ dispatch_group_enter(group); + [filter executeWithArguments:arguments completionHandler:^(NSError *filterError) { blockError = filterError; + dispatch_group_leave(group); }]; }); + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + if (blockError) { - if (error) *error = blockError; + if (error) { + *error = blockError; + } + return nil; } NSData *standardErrorData = [standardError.fileHandleForReading readDataToEndOfFile]; + if (standardErrorData.length > 0) { NSString *standardErrorContents = [NSString stringWithUTF8String:standardErrorData.bytes]; - if (error) *error = TBError.filterStandardError(filterURL, standardErrorContents); + + if (error) { + *error = TBError.filterStandardError(filterURL, standardErrorContents); + } + return nil; } + NSData *standardOutputData = [standardOutput.fileHandleForReading readDataToEndOfFile]; - if (standardOutputData.length > 0) + + if (standardOutputData.length > 0) { content = [[NSString alloc] initWithBytes:standardOutputData.bytes length:standardOutputData.length encoding:NSUTF8StringEncoding]; - + } } return content; - } #pragma mark - Site Modification - (NSURL *)addPostWithTitle:(NSString *)title slug:(NSString *)slug error:(NSError **)error { NSDate *currentDate = [NSDate date]; + NSDateFormatter *dateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd"]; + NSString *dateString = [dateFormatter stringFromDate:currentDate]; + NSString *filename = [NSString stringWithFormat:@"%@-%@", dateString, slug]; + NSURL *destination = [[self.postsDirectory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; + NSString *contents = [NSString stringWithFormat:@"# %@ #\n\n", title]; - if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) + + if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { return nil; - if (![self parsePosts:error]) + } + + if (![self parsePosts:error]) { return nil; + } + return destination; } - (void)setMetadata:(NSDictionary *)metadata { _metadata = metadata; + NSURL *metadataURL = [self.root URLByAppendingPathComponent:@"Info.plist" isDirectory:NO]; + [self.metadata writeToURL:metadataURL atomically:NO]; - if (self.delegate && [self.delegate respondsToSelector:@selector(metadataDidChangeForSite:)]) + + if (self.delegate && [self.delegate respondsToSelector:@selector(metadataDidChangeForSite:)]) { [self.delegate metadataDidChangeForSite:self]; + } } @end From 35d94816adc7e86e7bd84a9d2a6361914f3dc9bb Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 12:41:55 -0400 Subject: [PATCH 02/30] Create the post backend file in Post class. Site doesn't manage this anymore, only manages posts. --- Mac App/Controllers/TBSiteWindowController.m | 9 +++++-- Shared/TBPost.h | 2 ++ Shared/TBPost.m | 28 ++++++++++++++++++++ Shared/TBSite.h | 22 ++------------- Shared/TBSite.m | 24 ++--------------- 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/Mac App/Controllers/TBSiteWindowController.m b/Mac App/Controllers/TBSiteWindowController.m index 382edf8..147129a 100644 --- a/Mac App/Controllers/TBSiteWindowController.m +++ b/Mac App/Controllers/TBSiteWindowController.m @@ -88,9 +88,14 @@ - (IBAction)showAddPostSheet:(id)sender { TBSiteDocument *document = (TBSiteDocument *)self.document; [self.addPostSheetController runModalForWindow:[document windowForSheet] completionBlock:^(NSString *title, NSString *slug) { NSError *error = nil; - NSURL *siteURL = [document.site addPostWithTitle:title slug:slug error:&error]; - if (!siteURL) + + TBPost *post = [[TBPost alloc] initWithTitle:title slug:slug inSite:document.site error:&error]; + + if (post) { + [document.site addPost:post]; + } else { [self tb_presentErrorOnMainQueue:error]; + } }]; } diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 7611f43..d99e1ff 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -41,6 +41,8 @@ */ - (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error; +- (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error; + /*! Parse the contents of the markdownContent property, saving the HTML output to the content property. diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 6b31c04..6f0e61a 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -20,6 +20,34 @@ - (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError ** return [super initWithURL:URL inSite:site error:error]; } +- (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error { + if (self = [super init]) { + self.site = site; + + // Create the directory + NSDate *currentDate = [NSDate date]; + NSDateFormatter *dateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd"]; + + NSString *dateString = [dateFormatter stringFromDate:currentDate]; + + NSString *filename = [NSString stringWithFormat:@"%@-%@", dateString, slug]; + + NSURL *destination = [[site.postsDirectory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; + + NSString *contents = [NSString stringWithFormat:@"# %@ #\n\n", title]; + + if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { + return nil; + } + + self.URL = destination; + + [self parse:error]; + } + + return self; +} + - (BOOL)parse:(NSError **)error { if (![self parseDateAndSlug:error]) { return NO; diff --git a/Shared/TBSite.h b/Shared/TBSite.h index 2e9da67..93d7d37 100644 --- a/Shared/TBSite.h +++ b/Shared/TBSite.h @@ -8,6 +8,7 @@ // #import "TBConstants.h" +#import "TBPost.h" @protocol TBSiteDelegate; @@ -56,26 +57,7 @@ */ - (BOOL)parsePosts:(NSError **)error; -/*! - Add an empty post to the receiving site object. - @param title - The title of the post. Must not be nil. - @param slug - The slug of the post, i.e. the post title that will be used in URLs. - Must be URL-encoded and not nil. - @param error - If the return value is nil, then this argument will contain an NSError - object describing what went wrong. - @return - A filesystem URL pointing to the newly-created post file. - @discussion - The new post file has no content, but is pre-filled with a Markdown - title on the first line. The date of the post is automatically set to - today's date. - */ -- (NSURL *)addPostWithTitle:(NSString *)title - slug:(NSString *)slug - error:(NSError **)error; +- (void)addPost:(TBPost *)post; /*! @property root diff --git a/Shared/TBSite.m b/Shared/TBSite.m index d0d26cb..17c1375 100644 --- a/Shared/TBSite.m +++ b/Shared/TBSite.m @@ -429,28 +429,8 @@ - (NSString *)filteredContent:(NSString *)content fromFile:(NSURL *)file error:( #pragma mark - Site Modification -- (NSURL *)addPostWithTitle:(NSString *)title slug:(NSString *)slug error:(NSError **)error { - NSDate *currentDate = [NSDate date]; - - NSDateFormatter *dateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd"]; - - NSString *dateString = [dateFormatter stringFromDate:currentDate]; - - NSString *filename = [NSString stringWithFormat:@"%@-%@", dateString, slug]; - - NSURL *destination = [[self.postsDirectory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; - - NSString *contents = [NSString stringWithFormat:@"# %@ #\n\n", title]; - - if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { - return nil; - } - - if (![self parsePosts:error]) { - return nil; - } - - return destination; +- (void)addPost:(TBPost *)post { + [self.posts addObject:post]; } - (void)setMetadata:(NSDictionary *)metadata { From ae17cd04d773bb68cbee74edb2a894185be0c029 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 17:42:43 -0400 Subject: [PATCH 03/30] Place posts in their own directories. Create metadata file. Posts are now created in root/Posts/slug/ directory. The metadata file is created in the same directory. --- Shared/TBPost.h | 4 ++ Shared/TBPost.m | 19 +++++++-- Shared/TSPostMetadata.h | 27 +++++++++++++ Shared/TSPostMetadata.m | 68 +++++++++++++++++++++++++++++++++ Tribo.xcodeproj/project.pbxproj | 6 +++ 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 Shared/TSPostMetadata.h create mode 100644 Shared/TSPostMetadata.m diff --git a/Shared/TBPost.h b/Shared/TBPost.h index d99e1ff..8985f97 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -9,6 +9,8 @@ #import "TBPage.h" +#import "TSPostMetadata.h" + /*! @class TBPost @discussion A post represents a piece of writing, loaded from disk, with its @@ -72,6 +74,8 @@ */ @property (nonatomic, strong) NSString *slug; +@property (nonatomic, strong) TSPostMetadata *metadata; + /*! @property markdownContent The original content of the post, before being converted to HTML by the diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 6f0e61a..5b2f73c 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -32,15 +32,28 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB NSString *filename = [NSString stringWithFormat:@"%@-%@", dateString, slug]; - NSURL *destination = [[site.postsDirectory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; + NSURL *directory = [site.postsDirectory URLByAppendingPathComponent:slug isDirectory:YES]; + + if (![[NSFileManager defaultManager] createDirectoryAtURL:directory withIntermediateDirectories:YES attributes:nil error:error]) { + // Unable to create directory structure + return nil; + } + + // Metadata File + self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:directory]; + + [self.metadata writeWithError:error]; + + // Post File + NSURL *contentDestination = [[directory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; NSString *contents = [NSString stringWithFormat:@"# %@ #\n\n", title]; - if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { + if (![contents writeToURL:contentDestination atomically:YES encoding:NSUTF8StringEncoding error:error]) { return nil; } - self.URL = destination; + self.URL = contentDestination; [self parse:error]; } diff --git a/Shared/TSPostMetadata.h b/Shared/TSPostMetadata.h new file mode 100644 index 0000000..00ce34f --- /dev/null +++ b/Shared/TSPostMetadata.h @@ -0,0 +1,27 @@ +// +// TSPostMetadata.h +// Tribo +// +// Created by Tanner Smith on 8/27/13. +// Copyright (c) 2013 The Tribo Authors. +// See the included License.md file. +// + +#import + +@interface TSPostMetadata : NSObject + +#define METADATA_FILENAME @"metadata.json" + +@property (retain, strong) NSURL *postDirectory; +@property (retain, strong) NSURL *path; + +@property (assign) BOOL draft; +@property (retain, strong) NSDate *publishedDate; + +- (instancetype)init; +- (instancetype)initWithPostDirectory:(NSURL *)postDirectory; + +- (BOOL)writeWithError:(NSError **)error; + +@end diff --git a/Shared/TSPostMetadata.m b/Shared/TSPostMetadata.m new file mode 100644 index 0000000..8cef3b4 --- /dev/null +++ b/Shared/TSPostMetadata.m @@ -0,0 +1,68 @@ +// +// TSPostMetadata.m +// Tribo +// +// Created by Tanner Smith on 8/27/13. +// Copyright (c) 2013 The Tribo Authors. +// See the included License.md file. +// + +#import "TSPostMetadata.h" + +@implementation TSPostMetadata + +@synthesize draft, publishedDate; +@synthesize postDirectory; + +- (instancetype)init { + if (self = [super init]) { + draft = YES; + + publishedDate = nil; + } + + return self; +} + +- (instancetype)initWithPostDirectory:(NSURL *)directory { + if (self = [self init]) { + postDirectory = directory; + + _path = [directory URLByAppendingPathComponent:METADATA_FILENAME]; + } + + return self; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary { + if (self = [self init]) { + draft = [[dictionary objectForKey:@"draft"] boolValue]; + + publishedDate = [dictionary objectForKey:@"publishedDate"]; + } + + return self; +} + +- (BOOL)writeWithError:(NSError **)error { + if (!postDirectory) { + return NO; + } + + NSData *data = [NSJSONSerialization dataWithJSONObject:[self dictionary] options:0 error:error]; + + NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + return [string writeToURL:_path atomically:YES encoding:NSUTF8StringEncoding error:error]; +} + +- (NSDictionary *)dictionary { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + + [dictionary setValue:[NSNumber numberWithBool:draft] forKey:@"draft"]; + [dictionary setValue:publishedDate forKey:@"publishedDate"]; + + return dictionary; +} + +@end diff --git a/Tribo.xcodeproj/project.pbxproj b/Tribo.xcodeproj/project.pbxproj index a145f0c..85c4f9d 100644 --- a/Tribo.xcodeproj/project.pbxproj +++ b/Tribo.xcodeproj/project.pbxproj @@ -163,6 +163,7 @@ 9B6B669D14F3546C00759DC3 /* TBAsset.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B6B669B14F3546900759DC3 /* TBAsset.m */; }; 9BEF1C7A14F5CFCF0072B295 /* TBSourceViewControllerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7814F5CFCF0072B295 /* TBSourceViewControllerViewController.m */; }; 9BEF1C7B14F5CFCF0072B295 /* TBSourceViewControllerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */; }; + C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -381,6 +382,8 @@ 9BEF1C7714F5CFCF0072B295 /* TBSourceViewControllerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBSourceViewControllerViewController.h; sourceTree = ""; }; 9BEF1C7814F5CFCF0072B295 /* TBSourceViewControllerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBSourceViewControllerViewController.m; sourceTree = ""; }; 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TBSourceViewControllerView.xib; sourceTree = ""; }; + C3ACCD1617CD1EEF0002CE67 /* TSPostMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSPostMetadata.h; sourceTree = ""; }; + C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSPostMetadata.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -580,6 +583,8 @@ 4A00337A1430216500F6702F /* TBSite.m */, 4A00337C143028CA00F6702F /* TBPost.h */, 4A00337D143028CA00F6702F /* TBPost.m */, + C3ACCD1617CD1EEF0002CE67 /* TSPostMetadata.h */, + C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */, 4A72F14014368E1200A164E9 /* TBPage.h */, 4A72F14114368E1200A164E9 /* TBPage.m */, 9B6B669A14F3546900759DC3 /* TBAsset.h */, @@ -1163,6 +1168,7 @@ 4A99C42917B4091D0023C9A9 /* CZAFileWatcher.m in Sources */, 4AA86A6C173EF28200046691 /* TBSite+TemplateAdditions.m in Sources */, 4AA86A70174124C300046691 /* TBPost+TemplateAdditions.m in Sources */, + C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 3b88e83aaabae0dc18d6e85f2c5d8953a667b2b5 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 17:46:58 -0400 Subject: [PATCH 04/30] Save the post directory in an instance var. For whenever we need it. --- Shared/TBPost.h | 2 ++ Shared/TBPost.m | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 8985f97..8418268 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -76,6 +76,8 @@ @property (nonatomic, strong) TSPostMetadata *metadata; +@property (nonatomic, strong) NSURL *postDirectory; + /*! @property markdownContent The original content of the post, before being converted to HTML by the diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 5b2f73c..1857bca 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -32,20 +32,20 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB NSString *filename = [NSString stringWithFormat:@"%@-%@", dateString, slug]; - NSURL *directory = [site.postsDirectory URLByAppendingPathComponent:slug isDirectory:YES]; + self.postDirectory = [site.postsDirectory URLByAppendingPathComponent:slug isDirectory:YES]; - if (![[NSFileManager defaultManager] createDirectoryAtURL:directory withIntermediateDirectories:YES attributes:nil error:error]) { + if (![[NSFileManager defaultManager] createDirectoryAtURL:self.postDirectory withIntermediateDirectories:YES attributes:nil error:error]) { // Unable to create directory structure return nil; } // Metadata File - self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:directory]; + self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:self.postDirectory]; [self.metadata writeWithError:error]; // Post File - NSURL *contentDestination = [[directory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; + NSURL *contentDestination = [[self.postDirectory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; NSString *contents = [NSString stringWithFormat:@"# %@ #\n\n", title]; From 06981378b4aa6fb0dbf6359cf4d36643e9922071 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 17:53:01 -0400 Subject: [PATCH 05/30] Remove the date out of the filename. --- Shared/TBPost.m | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 1857bca..9d1b7fa 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -25,12 +25,7 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB self.site = site; // Create the directory - NSDate *currentDate = [NSDate date]; - NSDateFormatter *dateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd"]; - - NSString *dateString = [dateFormatter stringFromDate:currentDate]; - - NSString *filename = [NSString stringWithFormat:@"%@-%@", dateString, slug]; + NSString *filename = [NSString stringWithString:slug]; self.postDirectory = [site.postsDirectory URLByAppendingPathComponent:slug isDirectory:YES]; From 4dde97d9c5ec8f93305d71a34df2c06b813c8c08 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 18:07:18 -0400 Subject: [PATCH 06/30] Don't extract the date from the filename anymore. --- Shared/TBPost.m | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 9d1b7fa..bdba666 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -57,7 +57,7 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB } - (BOOL)parse:(NSError **)error { - if (![self parseDateAndSlug:error]) { + if (![self parseSlug:error]) { return NO; } @@ -116,34 +116,16 @@ - (void)parseTitle { self.markdownContent = markdownContent; } -- (BOOL)parseDateAndSlug:(NSError **)error { - // Dates and slugs are parsed from a pattern in the post file name - static NSRegularExpression *fileNameRegex; +- (BOOL)parseSlug:(NSError **)error { + NSString *filename = [[self.URL lastPathComponent] stringByDeletingPathExtension]; - if (fileNameRegex == nil) { - fileNameRegex = [NSRegularExpression regularExpressionWithPattern:@"^(\\d+-\\d+-\\d+)-(.*)" options:0 error:nil]; - } - - NSString *fileName = [self.URL.lastPathComponent stringByDeletingPathExtension]; - - NSTextCheckingResult *fileNameResult = [fileNameRegex firstMatchInString:fileName options:0 range:NSMakeRange(0, fileName.length)]; - - if (fileNameResult) { - NSDateFormatter *fileNameDateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd"]; - - self.date = [fileNameDateFormatter dateFromString:[fileName substringWithRange:[fileNameResult rangeAtIndex:1]]]; - self.slug = [fileName substringWithRange:[fileNameResult rangeAtIndex:2]]; - } else { - // No date found + if (filename && [filename length] > 0) { + self.slug = filename; - if (error) { - *error = TBError.badPostFileName(self.URL); - } - - return NO; - } + return YES; + } - return YES; + return NO; } - (void)parseMarkdownContent { From 12d00dfb42950f8004d0eaca530e94038b1ee61c Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 18:27:26 -0400 Subject: [PATCH 07/30] Update the initWithURL method for TBPost. The method now takes in the post's directory, e.g. root/Post/slug/. --- Shared/TBPost.h | 4 ++-- Shared/TBPost.m | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 8418268..4389564 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -30,9 +30,9 @@ @interface TBPost : TBPage /*! - Create a TBPost object from a file on-disk. + Create a TBPost object from a directory. @param URL - A filesystem URL pointing to the post file. + A filesystem URL pointing to the post directory. @param site The TBSite object which contains the post. @param error diff --git a/Shared/TBPost.m b/Shared/TBPost.m index bdba666..efe025a 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -17,6 +17,12 @@ @implementation TBPost - (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { + self.postDirectory = URL; + + self.slug = [URL lastPathComponent]; + + URL = [URL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.md", self.slug]]; + return [super initWithURL:URL inSite:site error:error]; } From da07eb8c2a57e9c5fdb45ed4c420a6b2f7b7cdb5 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Tue, 27 Aug 2013 18:42:39 -0400 Subject: [PATCH 08/30] When creating an existing TSPostMetadata file, load the data in. --- Shared/TBPost.m | 2 +- Shared/TSPostMetadata.h | 5 ++++- Shared/TSPostMetadata.m | 32 ++++++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index efe025a..23916ca 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -41,7 +41,7 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB } // Metadata File - self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:self.postDirectory]; + self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:self.postDirectory withError:error]; [self.metadata writeWithError:error]; diff --git a/Shared/TSPostMetadata.h b/Shared/TSPostMetadata.h index 00ce34f..7dabefe 100644 --- a/Shared/TSPostMetadata.h +++ b/Shared/TSPostMetadata.h @@ -20,8 +20,11 @@ @property (retain, strong) NSDate *publishedDate; - (instancetype)init; -- (instancetype)initWithPostDirectory:(NSURL *)postDirectory; +- (instancetype)initWithPostDirectory:(NSURL *)directory withError:(NSError **)error; +- (void)extractDataFromDictionary:(NSDictionary *)dictionary; + +- (BOOL)readWithError:(NSError **)error; - (BOOL)writeWithError:(NSError **)error; @end diff --git a/Shared/TSPostMetadata.m b/Shared/TSPostMetadata.m index 8cef3b4..fd015f9 100644 --- a/Shared/TSPostMetadata.m +++ b/Shared/TSPostMetadata.m @@ -24,11 +24,13 @@ - (instancetype)init { return self; } -- (instancetype)initWithPostDirectory:(NSURL *)directory { +- (instancetype)initWithPostDirectory:(NSURL *)directory withError:(NSError **)error { if (self = [self init]) { postDirectory = directory; _path = [directory URLByAppendingPathComponent:METADATA_FILENAME]; + + [self readWithError:error]; } return self; @@ -36,14 +38,36 @@ - (instancetype)initWithPostDirectory:(NSURL *)directory { - (instancetype)initWithDictionary:(NSDictionary *)dictionary { if (self = [self init]) { - draft = [[dictionary objectForKey:@"draft"] boolValue]; - - publishedDate = [dictionary objectForKey:@"publishedDate"]; + [self extractDataFromDictionary:dictionary]; } return self; } +- (void)extractDataFromDictionary:(NSDictionary *)dictionary { + draft = [[dictionary objectForKey:@"draft"] boolValue]; + + publishedDate = [dictionary objectForKey:@"publishedDate"]; +} + +- (BOOL)readWithError:(NSError **)error { + if ([[NSFileManager defaultManager] fileExistsAtPath:[_path absoluteString]] == NO) { + return NO; + } + + NSData *data = [[NSData alloc] initWithContentsOfURL:_path]; + + NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:error]; + + if (data) { + [self extractDataFromDictionary:dictionary]; + + return YES; + } + + return NO; +} + - (BOOL)writeWithError:(NSError **)error { if (!postDirectory) { return NO; From ca846666675e1e7e4a49e61e054ef671001ac0a3 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Wed, 28 Aug 2013 11:05:04 -0400 Subject: [PATCH 09/30] Place a draft label in the UI cells for posts. --- Mac App/Controllers/TBPostsView.xib | 540 ++++++++++++++++++---------- Shared/TBPost.h | 2 + Shared/TBPost.m | 20 +- 3 files changed, 378 insertions(+), 184 deletions(-) diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index f2f1e78..268dab3 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -3,12 +3,12 @@ 1070 12E55 - 4488.1 + 3084 1187.39 626.00 com.apple.InterfaceBuilder.CocoaPlugin - 4488.1 + 3084 IBNSLayoutConstraint @@ -276,6 +276,7 @@ title dateString markdownContent + draft TBPost YES @@ -397,7 +398,7 @@ 266 {{17, 66}, {270, 17}} - + _NS:78 {250, 750} YES @@ -427,7 +428,7 @@ 268 {{289, 66}, {101, 17}} - + _NS:3944 {750, 750} YES @@ -448,12 +449,36 @@ NO - + 268 {{289, 66}, {101, 17}} - + + _NS:3944 + {750, 750} + YES + + 68157504 + 71304256 + Draft + + _NS:3944 + + + + 1 + MSAwIDAAA + + + NO + + + + 268 + {{287, 66}, {101, 17}} + + _NS:3944 {750, 750} YES @@ -503,7 +528,7 @@ 268 {{17, 0}, {373, 63}} - + _NS:360 {250, 750} YES @@ -590,19 +615,19 @@ - hidden: backgroundStyle + hidden: objectValue.draft - hidden: backgroundStyle + hidden: objectValue.draft hidden - backgroundStyle + objectValue.draft 2 - 208 + 524 @@ -696,6 +721,26 @@ 410 + + + hidden: objectValue.draft + + + + + + hidden: objectValue.draft + hidden + objectValue.draft + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 523 + @@ -777,7 +822,6 @@ 9 40 3 - NO @@ -791,10 +835,9 @@ 1000 - 0 + 8 29 3 - NO @@ -808,10 +851,9 @@ 1000 - 0 + 8 29 3 - NO @@ -825,10 +867,9 @@ 1000 - 0 + 8 29 3 - NO @@ -875,46 +916,44 @@ 178 - - - 5 + + + 11 0 - - 6 + + 11 1 - - 8 + + 0.0 1000 6 24 - 3 - NO + 2 - - - 6 + + + 11 0 - 6 + 11 1 - - 20 + + 0.0 1000 - 0 - 29 - 3 - NO + 6 + 24 + 2 - - - 11 + + + 6 0 - - 11 + + 6 1 0.0 @@ -924,24 +963,22 @@ 6 24 2 - NO - - - 11 + + + 5 0 - - 11 + + 6 1 - - 0.0 + + 8 1000 6 24 - 2 - NO + 3 @@ -955,10 +992,9 @@ 1000 - 0 + 8 29 3 - NO @@ -975,7 +1011,22 @@ 6 24 3 - NO + + + + 6 + 0 + + 6 + 1 + + 22 + + 1000 + + 3 + 9 + 3 @@ -989,10 +1040,9 @@ 1000 - 0 + 8 29 3 - NO @@ -1006,10 +1056,9 @@ 1000 - 0 + 8 29 3 - NO @@ -1026,7 +1075,6 @@ 6 24 2 - NO @@ -1040,10 +1088,9 @@ 1000 - 0 + 8 29 3 - NO @@ -1057,10 +1104,9 @@ 1000 - 0 + 8 29 3 - NO @@ -1074,10 +1120,9 @@ 1000 - 0 + 8 29 3 - NO @@ -1091,50 +1136,64 @@ 1000 - 0 + 8 29 3 - NO - + - 3 + 11 0 - - 3 + + 11 1 - 6 + 0.0 1000 - 3 - 9 - 3 - NO + 6 + 24 + 2 - + 5 0 - + 5 1 - - 20 + + 0.0 1000 - 0 - 29 + 6 + 24 + 2 + + + + 3 + 0 + + 3 + 1 + + 6 + + 1000 + + 3 + 9 3 - NO + @@ -1148,16 +1207,6 @@ - - 181 - - - - - 182 - - - 183 @@ -1166,21 +1215,6 @@ - - 184 - - - - - 185 - - - - - 186 - - - 187 @@ -1191,31 +1225,27 @@ - - 189 - - - - - 190 - - - - - 191 - - - - - 192 - - - 193 + + + 7 + 0 + + 0 + 1 + + 95 + + 1000 + + 3 + 9 + 1 + @@ -1224,30 +1254,8 @@ - - - - - 195 - - - - - 196 - - - - - 197 - - - - - 198 - - - - + + 8 0 @@ -1257,12 +1265,23 @@ 63 1000 - + 3 9 1 - NO + + + + + 197 + + + + + 198 + + @@ -1280,11 +1299,6 @@ - - 201 - - - 202 @@ -1335,6 +1349,96 @@ + + 412 + + + + + + Static Text - Draft + + + 413 + + + Text Field Cell - Draft + + + 181 + + + + + 190 + + + + + 189 + + + + + 184 + + + + + 426 + + + + + 195 + + + + + 186 + + + + + 529 + + + + + 536 + + + + + 540 + + + + + 541 + + + + + 542 + + + + + 543 + + + + + 544 + + + + + 545 + + + @@ -1356,9 +1460,10 @@ - - + + + @@ -1366,46 +1471,44 @@ + + + + - - - com.apple.InterfaceBuilder.CocoaPlugin PostCell com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - - - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1416,6 +1519,18 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1430,20 +1545,83 @@ - 411 + 545 + + + + + TBPostsViewController + TBViewController + + id + id + id + + + + editPost: + id + + + previewPost: + id + + + revealPost: + id + + + + postTableView + NSTableView + + + postTableView + + postTableView + NSTableView + + + + IBProjectSource + ./Classes/TBPostsViewController.h + + + + TBTableView + NSTableView + + deleteSelectedRows: + id + + + deleteSelectedRows: + + deleteSelectedRows: + id + + + + IBProjectSource + ./Classes/TBTableView.h + + + + TBViewController + NSViewController + + IBProjectSource + ./Classes/TBViewController.h + + + - 0 IBCocoaFramework - YES com.apple.InterfaceBuilder.CocoaPlugin.macosx - - com.apple.InterfaceBuilder.CocoaPlugin.macosx - - com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 4389564..7ea3cdc 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -76,6 +76,8 @@ @property (nonatomic, strong) TSPostMetadata *metadata; +@property (nonatomic, assign) BOOL draft; + @property (nonatomic, strong) NSURL *postDirectory; /*! diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 23916ca..a96a52e 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -21,9 +21,15 @@ - (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError ** self.slug = [URL lastPathComponent]; - URL = [URL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.md", self.slug]]; - - return [super initWithURL:URL inSite:site error:error]; + self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:URL withError:error]; + + if (self.metadata) { + URL = [URL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.md", self.slug]]; + + return [super initWithURL:URL inSite:site error:error]; + } else { + return nil; + } } - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error { @@ -165,4 +171,12 @@ - (void)parseMarkdownContent { bufrelease(outputBuffer); } +- (BOOL)draft { + return [self.metadata draft]; +} + +- (void)setDraft:(BOOL)draft { + [self.metadata setDraft:draft]; +} + @end From 0d5dc8016fd968132966f2181dffbae7a22d5deb Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Wed, 28 Aug 2013 16:06:23 -0400 Subject: [PATCH 10/30] Show the "Draft" label when the cell is selected. --- Mac App/Controllers/TBPostsView.xib | 267 +++++++++++++++++++++++----- 1 file changed, 222 insertions(+), 45 deletions(-) diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index 268dab3..9c3a029 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -428,7 +428,7 @@ 268 {{289, 66}, {101, 17}} - + _NS:3944 {750, 750} YES @@ -449,7 +449,7 @@ NO - + 268 {{289, 66}, {101, 17}} @@ -458,6 +458,27 @@ _NS:3944 {750, 750} YES + + 68157504 + 71304256 + Draft + + _NS:3944 + + + + + NO + + + + 268 + {{289, 66}, {101, 17}} + + + _NS:3944 + {750, 750} + YES 68157504 71304256 @@ -528,7 +549,7 @@ 268 {{17, 0}, {373, 63}} - + _NS:360 {250, 750} YES @@ -726,7 +747,7 @@ hidden: objectValue.draft - + hidden: objectValue.draft @@ -741,6 +762,49 @@ 523 + + + hidden2: backgroundStyle + + + + + + hidden2: backgroundStyle + hidden2 + backgroundStyle + + + + + + + + 2 + + + 564 + + + + hidden: backgroundStyle + + + + + + hidden: backgroundStyle + hidden + backgroundStyle + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 559 + @@ -916,8 +980,24 @@ 178 + + + 6 + 0 + + 6 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + - + 11 0 @@ -932,7 +1012,39 @@ 24 2 - + + + 11 + 0 + + 11 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + + + + 5 + 0 + + 6 + 1 + + 8 + + 1000 + + 6 + 24 + 3 + + 11 0 @@ -964,7 +1076,7 @@ 24 2 - + 5 0 @@ -1140,27 +1252,11 @@ 29 3 - - - 11 - 0 - - 11 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - 5 0 - + 5 1 @@ -1188,12 +1284,29 @@ 9 3 + + + 11 + 0 + + 11 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + + @@ -1212,6 +1325,22 @@ + + + 7 + 0 + + 0 + 1 + + 95 + + 1000 + + 3 + 9 + 1 + @@ -1273,11 +1402,6 @@ - - 197 - - - 198 @@ -1399,19 +1523,14 @@ - - 529 - - - 536 - + 540 - + @@ -1431,14 +1550,59 @@ 544 - + + + + + 546 + + + + + + Static Text - Selected - Draft + + + 547 + + + Text Field Cell - Draft + + + 553 + + + + + 565 + + + + + 566 + - 545 + 568 + + + + + 571 + + + + + 572 + + 573 + + + @@ -1461,9 +1625,9 @@ + - @@ -1474,16 +1638,22 @@ - + - + + + + com.apple.InterfaceBuilder.CocoaPlugin PostCell com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1503,7 +1673,6 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1523,14 +1692,22 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1545,7 +1722,7 @@ - 545 + 573 From 1cc40953228adf55c6af41d2c89c96c0cb2ea9e8 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 09:22:13 -0400 Subject: [PATCH 11/30] When reading the metadata file, use the path versus the URL. The URL actually works. --- Shared/TSPostMetadata.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/TSPostMetadata.m b/Shared/TSPostMetadata.m index fd015f9..458e4c6 100644 --- a/Shared/TSPostMetadata.m +++ b/Shared/TSPostMetadata.m @@ -51,7 +51,7 @@ - (void)extractDataFromDictionary:(NSDictionary *)dictionary { } - (BOOL)readWithError:(NSError **)error { - if ([[NSFileManager defaultManager] fileExistsAtPath:[_path absoluteString]] == NO) { + if ([[NSFileManager defaultManager] fileExistsAtPath:[_path path]] == NO) { return NO; } From 0c4a6c32510128a49b086a34c9b3285a3d5ae039 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 09:23:01 -0400 Subject: [PATCH 12/30] When setting the draft, write the data to the metadata file. --- Shared/TBPost.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index a96a52e..0d0fa89 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -177,6 +177,14 @@ - (BOOL)draft { - (void)setDraft:(BOOL)draft { [self.metadata setDraft:draft]; + + NSError *error = nil; + + [self.metadata writeWithError:&error]; + + if (error) { + [self.metadata setDraft:!draft]; + } } @end From a9fce6557e86e99d93287378d3c1d1fa3af401f5 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 12:46:13 -0400 Subject: [PATCH 13/30] Add right-click menu items for marking/unmarking as draft. --- Mac App/Controllers/TBPostsView.xib | 77 ++++++++++++++++++--- Mac App/Controllers/TBPostsViewController.h | 2 + Mac App/Controllers/TBPostsViewController.m | 12 ++++ 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index 9c3a029..8a2f5a3 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -235,6 +235,22 @@ NSMenuMixedState + + + Unmark as Draft + + 2147483647 + + + + + + Mark as Draft + + 2147483647 + + + Preview in Safari @@ -330,6 +346,22 @@ 251 + + + unmarkDraft: + + + + 576 + + + + markDraft: + + + + 577 + deleteSelectedRows: @@ -428,7 +460,7 @@ 268 {{289, 66}, {101, 17}} - + _NS:3944 {750, 750} YES @@ -454,7 +486,7 @@ 268 {{289, 66}, {101, 17}} - + _NS:3944 {750, 750} YES @@ -549,7 +581,7 @@ 268 {{17, 0}, {373, 63}} - + _NS:360 {250, 750} YES @@ -841,6 +873,8 @@ + + Post Context Menu @@ -980,7 +1014,7 @@ 178 - + 6 0 @@ -1012,7 +1046,7 @@ 24 2 - + 11 0 @@ -1590,7 +1624,7 @@ 571 - + @@ -1600,9 +1634,20 @@ 573 - + + + 574 + + + + + 575 + + + Menu Item - Mark as Draft + @@ -1642,9 +1687,9 @@ - + - + com.apple.InterfaceBuilder.CocoaPlugin PostCell @@ -1708,6 +1753,8 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1722,7 +1769,7 @@ - 573 + 584 @@ -1731,14 +1778,20 @@ TBViewController id + id id id + id editPost: id + + markDraft: + id + previewPost: id @@ -1747,6 +1800,10 @@ revealPost: id + + unmarkDraft: + id + postTableView diff --git a/Mac App/Controllers/TBPostsViewController.h b/Mac App/Controllers/TBPostsViewController.h index f40afb0..654ce81 100644 --- a/Mac App/Controllers/TBPostsViewController.h +++ b/Mac App/Controllers/TBPostsViewController.h @@ -13,6 +13,8 @@ @interface TBPostsViewController : TBViewController @property (nonatomic, assign) IBOutlet NSTableView *postTableView; - (IBAction)editPost:(id)sender; +- (IBAction)unmarkDraft:(id)sender; +- (IBAction)markDraft:(id)sender; - (IBAction)previewPost:(id)sender; - (IBAction)revealPost:(id)sender; @end diff --git a/Mac App/Controllers/TBPostsViewController.m b/Mac App/Controllers/TBPostsViewController.m index 9538122..6d58f79 100644 --- a/Mac App/Controllers/TBPostsViewController.m +++ b/Mac App/Controllers/TBPostsViewController.m @@ -46,6 +46,18 @@ - (IBAction)editPost:(id)sender { [[NSWorkspace sharedWorkspace] openURL:clickedPost.URL]; } +- (IBAction)unmarkDraft:(id)sender { + TBPost *clickedPost = (self.document.site.posts)[[self.postTableView clickedRow]]; + + [clickedPost setDraft:NO]; +} + +- (IBAction)markDraft:(id)sender { + TBPost *clickedPost = (self.document.site.posts)[[self.postTableView clickedRow]]; + + [clickedPost setDraft:YES]; +} + - (IBAction)previewPost:(id)sender { TBPost *clickedPost = (self.document.site.posts)[[self.postTableView clickedRow]]; NSDateFormatter *formatter = [NSDateFormatter new]; From 804a3f40e6e3a94747b3d9310fe127bb8fddb118 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 13:17:56 -0400 Subject: [PATCH 14/30] Only show the appropriate menu items for unmarking/marking as draft. --- Mac App/Controllers/TBPostsView.xib | 50 +++++++++++++++++---- Mac App/Controllers/TBPostsViewController.h | 6 ++- Mac App/Controllers/TBPostsViewController.m | 12 +++++ 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index 8a2f5a3..7c16c79 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -362,6 +362,22 @@ 577 + + + markDraftMenuItem + + + + 586 + + + + unmarkDraftMenuItem + + + + 587 + deleteSelectedRows: @@ -370,6 +386,14 @@ 405 + + + delegate + + + + 588 + contentArray: document.site.posts @@ -1769,7 +1793,7 @@ - 584 + 588 @@ -1805,17 +1829,25 @@ id - - postTableView - NSTableView - - - postTableView - + + NSMenuItem + NSTableView + NSMenuItem + + + + markDraftMenuItem + NSMenuItem + + postTableView NSTableView - + + unmarkDraftMenuItem + NSMenuItem + + IBProjectSource ./Classes/TBPostsViewController.h diff --git a/Mac App/Controllers/TBPostsViewController.h b/Mac App/Controllers/TBPostsViewController.h index 654ce81..1ec8315 100644 --- a/Mac App/Controllers/TBPostsViewController.h +++ b/Mac App/Controllers/TBPostsViewController.h @@ -10,8 +10,12 @@ #import "TBViewController.h" #import -@interface TBPostsViewController : TBViewController +@interface TBPostsViewController : TBViewController @property (nonatomic, assign) IBOutlet NSTableView *postTableView; + +@property (assign) IBOutlet NSMenuItem *unmarkDraftMenuItem; +@property (assign) IBOutlet NSMenuItem *markDraftMenuItem; + - (IBAction)editPost:(id)sender; - (IBAction)unmarkDraft:(id)sender; - (IBAction)markDraft:(id)sender; diff --git a/Mac App/Controllers/TBPostsViewController.m b/Mac App/Controllers/TBPostsViewController.m index 6d58f79..39cd40b 100644 --- a/Mac App/Controllers/TBPostsViewController.m +++ b/Mac App/Controllers/TBPostsViewController.m @@ -23,6 +23,8 @@ - (void)undoMoveToTrashForURLs:(NSDictionary *)URLs; @implementation TBPostsViewController +@synthesize markDraftMenuItem, unmarkDraftMenuItem; + #pragma mark - View Controller Configuration - (NSString *)defaultNibName { @@ -38,6 +40,16 @@ - (void)viewDidLoad { self.postTableView.doubleAction = @selector(editPost:); } +#pragma mark - NSMenuDelegate + +- (void)menuNeedsUpdate:(NSMenu *)menu { + TBSiteDocument *document = (TBSiteDocument *)self.document; + TBPost *clickedPost = (document.site.posts)[[self.postTableView clickedRow]]; + + [markDraftMenuItem setHidden:[clickedPost draft] == YES]; + [unmarkDraftMenuItem setHidden:[clickedPost draft] == NO]; +} + #pragma mark - Actions - (IBAction)editPost:(id)sender { From 4647a7a80249b164e642c1a45349c5512b0811e0 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 13:23:08 -0400 Subject: [PATCH 15/30] When "publishing a post" set the publish date to now. --- Shared/TBPost.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 0d0fa89..b3d7a7d 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -178,12 +178,17 @@ - (BOOL)draft { - (void)setDraft:(BOOL)draft { [self.metadata setDraft:draft]; + if (draft == NO) { + [self.metadata setPublishedDate:[NSDate date]]; + } + NSError *error = nil; [self.metadata writeWithError:&error]; if (error) { [self.metadata setDraft:!draft]; + [self.metadata setPublishedDate:nil]; } } From 19fbdfafb503376edcce44b7293cecbef88b4a16 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 15:30:55 -0400 Subject: [PATCH 16/30] Write the date to the JSON as a ISO 8601 compliant string. --- Shared/TSPostMetadata.m | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Shared/TSPostMetadata.m b/Shared/TSPostMetadata.m index 458e4c6..ed2d022 100644 --- a/Shared/TSPostMetadata.m +++ b/Shared/TSPostMetadata.m @@ -9,6 +9,8 @@ #import "TSPostMetadata.h" +#import "NSDateFormatter+TBAdditions.h" + @implementation TSPostMetadata @synthesize draft, publishedDate; @@ -47,7 +49,11 @@ - (instancetype)initWithDictionary:(NSDictionary *)dictionary { - (void)extractDataFromDictionary:(NSDictionary *)dictionary { draft = [[dictionary objectForKey:@"draft"] boolValue]; - publishedDate = [dictionary objectForKey:@"publishedDate"]; + // Must convert back into NSDate from string + NSDateFormatter *dateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd'T'hh:mm:ssZ"]; + NSString *dateString = [dictionary objectForKey:@"publishedDate"]; + + publishedDate = [dateFormatter dateFromString:dateString]; } - (BOOL)readWithError:(NSError **)error { @@ -84,7 +90,12 @@ - (NSDictionary *)dictionary { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setValue:[NSNumber numberWithBool:draft] forKey:@"draft"]; - [dictionary setValue:publishedDate forKey:@"publishedDate"]; + + // Must format published date into string for JSON + NSDateFormatter *dateFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd'T'hh:mm:ssZ"]; + NSString *dateString = [dateFormatter stringFromDate:publishedDate]; + + [dictionary setValue:dateString forKey:@"publishedDate"]; return dictionary; } From 3e3b4fd5e870847372d22e9fdf937ddaab01e6de Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 15:32:02 -0400 Subject: [PATCH 17/30] Remove the published time from the post if it's a draft. --- Shared/TBPost.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index b3d7a7d..96b3aa1 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -180,6 +180,8 @@ - (void)setDraft:(BOOL)draft { if (draft == NO) { [self.metadata setPublishedDate:[NSDate date]]; + } else { + [self.metadata setPublishedDate:nil]; } NSError *error = nil; From e7fd999de799fa499458bb9d73e3d60df40325c8 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 29 Aug 2013 16:05:59 -0400 Subject: [PATCH 18/30] Update Default.tribo to new directory structure. Also adds metadata file. --- .../{2012-03-08-example-post.md => example-post/example-post.md} | 0 Default.tribo/Posts/example-post/metadata.json | 1 + 2 files changed, 1 insertion(+) rename Default.tribo/Posts/{2012-03-08-example-post.md => example-post/example-post.md} (100%) create mode 100644 Default.tribo/Posts/example-post/metadata.json diff --git a/Default.tribo/Posts/2012-03-08-example-post.md b/Default.tribo/Posts/example-post/example-post.md similarity index 100% rename from Default.tribo/Posts/2012-03-08-example-post.md rename to Default.tribo/Posts/example-post/example-post.md diff --git a/Default.tribo/Posts/example-post/metadata.json b/Default.tribo/Posts/example-post/metadata.json new file mode 100644 index 0000000..74db01d --- /dev/null +++ b/Default.tribo/Posts/example-post/metadata.json @@ -0,0 +1 @@ +{"draft":true} \ No newline at end of file From 8c67ab847e3a829b6eab28196255b98feb47253c Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Mon, 2 Sep 2013 15:46:14 -0400 Subject: [PATCH 19/30] Show the date in the UI. --- Mac App/Controllers/TBPostsView.xib | 384 +++++++++++------- Shared/TBPost.h | 2 +- Shared/TBPost.m | 4 + .../TBPost+TemplateAdditions.m | 2 +- 4 files changed, 234 insertions(+), 158 deletions(-) diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index 7c16c79..d244e22 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -452,9 +452,9 @@ 266 - {{17, 66}, {270, 17}} + {{17, 66}, {201, 17}} - + _NS:78 {250, 750} YES @@ -482,9 +482,9 @@ 268 - {{289, 66}, {101, 17}} + {{220, 66}, {170, 17}} - + _NS:3944 {750, 750} YES @@ -553,9 +553,9 @@ 268 - {{287, 66}, {101, 17}} + {{220, 66}, {168, 17}} - + _NS:3944 {750, 750} YES @@ -659,7 +659,7 @@ hidden: backgroundStyle - + hidden: backgroundStyle @@ -674,6 +674,29 @@ 213 + + + hidden2: objectValue.draft + + + + + + hidden2: objectValue.draft + hidden2 + objectValue.draft + + + + + + + + 2 + + + 590 + value: objectValue.dateString @@ -692,19 +715,42 @@ - hidden: objectValue.draft + hidden: backgroundStyle - + - hidden: objectValue.draft + hidden: backgroundStyle hidden + backgroundStyle + 2 + + + 660 + + + + hidden2: objectValue.draft + + + + + + hidden2: objectValue.draft + hidden2 objectValue.draft + + + + + + + 2 - 524 + 661 @@ -846,7 +892,7 @@ hidden: backgroundStyle - + hidden: backgroundStyle @@ -861,6 +907,30 @@ 559 + + + hidden2: objectValue.draft + + + + + + hidden2: objectValue.draft + hidden2 + objectValue.draft + + + + + + NSNegateBoolean + + + 2 + + + 657 + @@ -1038,12 +1108,12 @@ 178 - - - 6 + + + 5 0 - - 6 + + 5 1 0.0 @@ -1054,11 +1124,11 @@ 24 2 - - + + 11 0 - + 11 1 @@ -1070,11 +1140,11 @@ 24 2 - - + + 11 0 - + 11 1 @@ -1087,7 +1157,7 @@ 2 - + 5 0 @@ -1102,11 +1172,11 @@ 24 3 - - + + 11 0 - + 11 1 @@ -1119,10 +1189,10 @@ 2 - + 6 0 - + 6 1 @@ -1134,8 +1204,8 @@ 24 2 - - + + 5 0 @@ -1150,53 +1220,37 @@ 24 3 - - - 6 - 0 - - 6 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - + - 5 + 6 0 - + 6 1 - - 8 + + 0.0 1000 6 24 - 3 + 2 - - + + 6 0 - + 6 1 - 22 + 0.0 1000 - 3 - 9 - 3 + 6 + 24 + 2 @@ -1310,11 +1364,11 @@ 29 3 - + 5 0 - + 5 1 @@ -1373,17 +1427,12 @@ - - 180 - - - 183 - + 7 0 @@ -1391,7 +1440,7 @@ 0 1 - 95 + 164 1000 @@ -1417,7 +1466,7 @@ - + 7 0 @@ -1425,7 +1474,7 @@ 0 1 - 95 + 162 1000 @@ -1536,6 +1585,22 @@ + + + 7 + 0 + + 0 + 1 + + 95 + + 1000 + + 3 + 9 + 1 + Static Text - Draft @@ -1546,11 +1611,6 @@ Text Field Cell - Draft - - 181 - - - 190 @@ -1566,11 +1626,6 @@ - - 426 - - - 195 @@ -1582,95 +1637,110 @@ - 536 - + 546 + + + + + Static Text - Selected - Draft - 540 - - + 547 + + + Text Field Cell - Draft - 541 - + 566 + - 542 - - + 574 + + - 543 - - + 575 + + + Menu Item - Mark as Draft - 544 - + 536 + - 546 - - - - + 632 + - Static Text - Selected - Draft - 547 - - - Text Field Cell - Draft + 637 + + - 553 - - + 654 + + - 565 - + 676 + - 566 - + 679 + - 568 - + 684 + - 571 - + 685 + + + + + 642 + + + + + 682 + - 572 - + 680 + - 573 - + 681 + - 574 - - + 678 + + - 575 - - - Menu Item - Mark as Draft + 663 + + + + + 683 + + @@ -1696,7 +1766,7 @@ - + @@ -1704,24 +1774,21 @@ - - - - + + + - + - - - + + + com.apple.InterfaceBuilder.CocoaPlugin PostCell com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - + + com.apple.InterfaceBuilder.CocoaPlugin @@ -1731,8 +1798,8 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - - + + com.apple.InterfaceBuilder.CocoaPlugin @@ -1757,28 +1824,33 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1793,7 +1865,7 @@ - 588 + 692 diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 7ea3cdc..87ebb61 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -63,7 +63,7 @@ The publishing date of the post. Derived from the filename of the post, which must begin with a calendar date in the form YYYY-MM-DD. */ -@property (nonatomic, strong) NSDate *date; +@property (nonatomic, strong, readonly) NSDate *date; /*! @property slug diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 96b3aa1..8cda19a 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -171,6 +171,10 @@ - (void)parseMarkdownContent { bufrelease(outputBuffer); } +- (NSDate *)date { + return [self.metadata publishedDate]; +} + - (BOOL)draft { return [self.metadata draft]; } diff --git a/Shared/Template Additions/TBPost+TemplateAdditions.m b/Shared/Template Additions/TBPost+TemplateAdditions.m index ae863d8..7d62e2c 100644 --- a/Shared/Template Additions/TBPost+TemplateAdditions.m +++ b/Shared/Template Additions/TBPost+TemplateAdditions.m @@ -11,7 +11,7 @@ @implementation TBPost (TemplateAdditions) - (NSString *)dateString { - NSDateFormatter *dateStringFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"d MMM yyyy"]; + NSDateFormatter *dateStringFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy-MM-dd hh:mm a z"]; return [dateStringFormatter stringFromDate:self.date]; } - (NSString *)XMLDate { From dbd3f0dbe03fa1d9d063a3f4b69f5307ea0587c1 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Wed, 4 Sep 2013 22:41:25 -0400 Subject: [PATCH 20/30] Move text label color changing on selection into code. Having two instances of each label in IB was making things hard to do as far as aligning text. Everything still works the same though. --- Mac App/Controllers/TBPostsView.xib | 686 ++++------------------ Mac App/Controllers/TSPostTableCellView.h | 18 + Mac App/Controllers/TSPostTableCellView.m | 42 ++ Tribo.xcodeproj/project.pbxproj | 6 + 4 files changed, 196 insertions(+), 556 deletions(-) create mode 100644 Mac App/Controllers/TSPostTableCellView.h create mode 100644 Mac App/Controllers/TSPostTableCellView.m diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index d244e22..81a8afc 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -135,7 +135,7 @@ 3 2 - + 3 MQA @@ -189,7 +189,6 @@ {{1, 119}, {223, 15}} - _NS:1847 NO 1 @@ -452,9 +451,9 @@ 266 - {{17, 66}, {201, 17}} + {{17, 66}, {178, 17}} - + _NS:78 {250, 750} YES @@ -479,59 +478,12 @@ NO - - - 268 - {{220, 66}, {170, 17}} - - - _NS:3944 - {750, 750} - YES - - 68157504 - 71304256 - Selected - Date - - _NS:3944 - - - - 6 - System - highlightColor - - - - NO - - - - 268 - {{289, 66}, {101, 17}} - - - _NS:3944 - {750, 750} - YES - - 68157504 - 71304256 - Draft - - _NS:3944 - - - - - NO - 268 {{289, 66}, {101, 17}} - + _NS:3944 {750, 750} YES @@ -553,9 +505,9 @@ 268 - {{220, 66}, {168, 17}} + {{199, 66}, {191, 17}} - + _NS:3944 {750, 750} YES @@ -579,27 +531,6 @@ NO - - - 268 - {{17, 0}, {373, 63}} - - - _NS:360 - {250, 750} - YES - - 67108864 - 272630016 - Post excerpt - selected - - _NS:360 - - - - - NO - 268 @@ -639,63 +570,36 @@ 206 - - value: objectValue.dateString - - - - - - value: objectValue.dateString - value - objectValue.dateString - 2 - + + postExcerpt + + - 212 + 696 - - hidden: backgroundStyle - - - - - - hidden: backgroundStyle - hidden - backgroundStyle - - NSValueTransformerName - NSNegateBoolean - - 2 - + + date + + - 213 + 697 - - hidden2: objectValue.draft - - - - - - hidden2: objectValue.draft - hidden2 - objectValue.draft - - - - - - - - 2 - + + draft + + - 590 + 698 + + + + title + + + + 699 @@ -788,42 +692,6 @@ 411 - - - hidden: backgroundStyle - - - - - - hidden: backgroundStyle - hidden - backgroundStyle - - NSValueTransformerName - NSNegateBoolean - - 2 - - - 214 - - - - value: objectValue.markdownContent - - - - - - value: objectValue.markdownContent - value - objectValue.markdownContent - 2 - - - 215 - value: objectValue.title @@ -887,50 +755,6 @@ 564 - - - hidden: backgroundStyle - - - - - - hidden: backgroundStyle - hidden - backgroundStyle - - NSValueTransformerName - NSNegateBoolean - - 2 - - - 559 - - - - hidden2: objectValue.draft - - - - - - hidden2: objectValue.draft - hidden2 - objectValue.draft - - - - - - NSNegateBoolean - - - 2 - - - 657 - @@ -1108,75 +932,11 @@ 178 - - - 5 - 0 - - 5 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - 11 0 - - 11 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - - - - 11 - 0 - - 11 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - - - - 5 - 0 - - 6 - 1 - - 8 - - 1000 - - 6 - 24 - 3 - - - - 11 - 0 - + 11 1 @@ -1189,7 +949,7 @@ 2 - + 6 0 @@ -1204,40 +964,8 @@ 24 2 - - - 5 - 0 - - 6 - 1 - - 8 - - 1000 - - 6 - 24 - 3 - - - - 6 - 0 - - 6 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - - - + + 6 0 @@ -1252,8 +980,8 @@ 24 2 - - + + 4 0 @@ -1268,8 +996,8 @@ 29 3 - - + + 5 0 @@ -1284,27 +1012,11 @@ 29 3 - - - 3 - 0 - - 3 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - - + 6 0 - + 6 1 @@ -1316,53 +1028,21 @@ 29 3 - - - 4 + + + 11 0 - - 4 + + 11 1 0.0 1000 - 8 - 29 - 3 - - - - 5 - 0 - - 5 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - - - 6 - 0 - - 6 - 1 - - 20 - - 1000 - - 8 - 29 - 3 + 6 + 24 + 2 @@ -1396,29 +1076,10 @@ 9 3 - - - 11 - 0 - - 11 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - - - - - + @@ -1427,46 +1088,12 @@ - - 183 - - - - - - 7 - 0 - - 0 - 1 - - 164 - - 1000 - - 3 - 9 - 1 - - - - - - 187 - - - - - 188 - - - 193 - + 7 0 @@ -1474,7 +1101,7 @@ 0 1 - 162 + 185 1000 @@ -1509,19 +1136,27 @@ - - 198 - - - - - - 199 + + + 7 + 0 + + 0 + 1 + + 172 + + 1000 + + 3 + 9 + 1 + @@ -1530,11 +1165,6 @@ - - 202 - - - 203 @@ -1545,11 +1175,6 @@ - - 205 - - - 376 @@ -1621,36 +1246,11 @@ - - 184 - - - 195 - - 186 - - - - - 546 - - - - - - Static Text - Selected - Draft - - - 547 - - - Text Field Cell - Draft - 566 @@ -1667,79 +1267,49 @@ Menu Item - Mark as Draft - - 536 - - - - - 632 - - - - - 637 - - - 654 - - 676 - - - - - 679 - - - - - 684 - - - - - 685 - - - 642 - 682 - + 663 + - 680 - + 700 + - 681 - + 701 + - 678 - - + 703 + + - 663 - + 707 + + + + + 708 + - 683 - + 709 + @@ -1763,43 +1333,25 @@ + TSPostTableCellView - + - - - - - - - + - - - - com.apple.InterfaceBuilder.CocoaPlugin PostCell com.apple.InterfaceBuilder.CocoaPlugin - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - + com.apple.InterfaceBuilder.CocoaPlugin @@ -1809,15 +1361,14 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1830,27 +1381,18 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1865,7 +1407,7 @@ - 692 + 709 @@ -1952,6 +1494,38 @@ ./Classes/TBViewController.h + + TSPostTableCellView + NSTableCellView + + NSTextField + NSTextField + NSTextField + NSTextField + + + + date + NSTextField + + + draft + NSTextField + + + postExcerpt + NSTextField + + + title + NSTextField + + + + IBProjectSource + ./Classes/TSPostTableCellView.h + + 0 diff --git a/Mac App/Controllers/TSPostTableCellView.h b/Mac App/Controllers/TSPostTableCellView.h new file mode 100644 index 0000000..2f54431 --- /dev/null +++ b/Mac App/Controllers/TSPostTableCellView.h @@ -0,0 +1,18 @@ +// +// TSPostTableCellView.h +// Tribo +// +// Created by Tanner Smith on 9/4/13. +// Copyright (c) 2013 Opt-6 Products, LLC. All rights reserved. +// + +#import + +@interface TSPostTableCellView : NSTableCellView + +@property (assign) IBOutlet NSTextField *title; +@property (assign) IBOutlet NSTextField *draft; +@property (assign) IBOutlet NSTextField *date; +@property (assign) IBOutlet NSTextField *postExcerpt; + +@end diff --git a/Mac App/Controllers/TSPostTableCellView.m b/Mac App/Controllers/TSPostTableCellView.m new file mode 100644 index 0000000..378270f --- /dev/null +++ b/Mac App/Controllers/TSPostTableCellView.m @@ -0,0 +1,42 @@ +// +// TSPostTableCellView.m +// Tribo +// +// Created by Tanner Smith on 9/4/13. +// Copyright (c) 2013 Opt-6 Products, LLC. All rights reserved. +// + +#import "TSPostTableCellView.h" + +@implementation TSPostTableCellView + +@synthesize title, date, draft, postExcerpt; + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + // Initialization code here. + } + + return self; +} + +- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle { + switch (backgroundStyle) { + case NSBackgroundStyleDark: + [title setTextColor:[NSColor whiteColor]]; + [date setTextColor:[NSColor whiteColor]]; + [draft setTextColor:[NSColor whiteColor]]; + [postExcerpt setTextColor:[NSColor whiteColor]]; + break; + default: + [title setTextColor:[NSColor blackColor]]; + [date setTextColor:[NSColor blueColor]]; + [draft setTextColor:[NSColor redColor]]; + [postExcerpt setTextColor:[NSColor grayColor]]; + break; + } +} + +@end diff --git a/Tribo.xcodeproj/project.pbxproj b/Tribo.xcodeproj/project.pbxproj index 85c4f9d..85203e3 100644 --- a/Tribo.xcodeproj/project.pbxproj +++ b/Tribo.xcodeproj/project.pbxproj @@ -163,6 +163,7 @@ 9B6B669D14F3546C00759DC3 /* TBAsset.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B6B669B14F3546900759DC3 /* TBAsset.m */; }; 9BEF1C7A14F5CFCF0072B295 /* TBSourceViewControllerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7814F5CFCF0072B295 /* TBSourceViewControllerViewController.m */; }; 9BEF1C7B14F5CFCF0072B295 /* TBSourceViewControllerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */; }; + C35C2A9717D8100800A6BE68 /* TSPostTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */; }; C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */; }; /* End PBXBuildFile section */ @@ -382,6 +383,8 @@ 9BEF1C7714F5CFCF0072B295 /* TBSourceViewControllerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBSourceViewControllerViewController.h; sourceTree = ""; }; 9BEF1C7814F5CFCF0072B295 /* TBSourceViewControllerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBSourceViewControllerViewController.m; sourceTree = ""; }; 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TBSourceViewControllerView.xib; sourceTree = ""; }; + C35C2A9517D8100800A6BE68 /* TSPostTableCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSPostTableCellView.h; path = Controllers/TSPostTableCellView.h; sourceTree = ""; }; + C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSPostTableCellView.m; path = Controllers/TSPostTableCellView.m; sourceTree = ""; }; C3ACCD1617CD1EEF0002CE67 /* TSPostMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSPostMetadata.h; sourceTree = ""; }; C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSPostMetadata.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -879,6 +882,8 @@ isa = PBXGroup; children = ( 4AAC8E5E1456616F00AE978A /* TBPostsView.xib */, + C35C2A9517D8100800A6BE68 /* TSPostTableCellView.h */, + C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */, 4AAC8E5B1456609300AE978A /* TBPostsViewController.h */, 4AAC8E5C1456609300AE978A /* TBPostsViewController.m */, 4ABD6A5014417DBE007E2F5D /* TBQLPreviewView.xib */, @@ -1169,6 +1174,7 @@ 4AA86A6C173EF28200046691 /* TBSite+TemplateAdditions.m in Sources */, 4AA86A70174124C300046691 /* TBPost+TemplateAdditions.m in Sources */, C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */, + C35C2A9717D8100800A6BE68 /* TSPostTableCellView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From bb9a1932d741842ebecf3960e1f0c7966229553d Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Wed, 4 Sep 2013 22:51:49 -0400 Subject: [PATCH 21/30] When clicking on an empty row, don't crash when showing the menu. --- Mac App/Controllers/TBPostsViewController.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Mac App/Controllers/TBPostsViewController.m b/Mac App/Controllers/TBPostsViewController.m index 39cd40b..a08d08b 100644 --- a/Mac App/Controllers/TBPostsViewController.m +++ b/Mac App/Controllers/TBPostsViewController.m @@ -44,7 +44,17 @@ - (void)viewDidLoad { - (void)menuNeedsUpdate:(NSMenu *)menu { TBSiteDocument *document = (TBSiteDocument *)self.document; - TBPost *clickedPost = (document.site.posts)[[self.postTableView clickedRow]]; + + NSInteger clickedRow = [self.postTableView clickedRow]; + + if (clickedRow < 0) { + [markDraftMenuItem setHidden:YES]; + [unmarkDraftMenuItem setHidden:YES]; + + return; + } + + TBPost *clickedPost = (document.site.posts)[clickedRow]; [markDraftMenuItem setHidden:[clickedPost draft] == YES]; [unmarkDraftMenuItem setHidden:[clickedPost draft] == NO]; From 6d64315a5538b7e31810628dd50436b96211fbd0 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Wed, 4 Sep 2013 23:39:19 -0400 Subject: [PATCH 22/30] Show drafts in preview mode, but don't include for publishing. Changes process and write post methods to include a parameter on whether or not to include drafts. --- Mac App/Publishing/TBFTPPublisher.m | 2 +- Mac App/Publishing/TBSFTPPublisher.m | 2 +- Mac App/TBSiteDocument.m | 4 ++-- Shared/TBSite.h | 4 +++- Shared/TBSite.m | 10 +++++++--- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Mac App/Publishing/TBFTPPublisher.m b/Mac App/Publishing/TBFTPPublisher.m index a049a48..b6e0135 100644 --- a/Mac App/Publishing/TBFTPPublisher.m +++ b/Mac App/Publishing/TBFTPPublisher.m @@ -22,7 +22,7 @@ @implementation TBFTPPublisher - (void)publish { self.site.published = YES; - [self.site process:nil]; + [self.site processIncludingDrafts:NO error:nil]; self.site.published = NO; NSString *hostname = (self.site.metadata)[TBSiteServerKey]; diff --git a/Mac App/Publishing/TBSFTPPublisher.m b/Mac App/Publishing/TBSFTPPublisher.m index 632a449..41e9af7 100644 --- a/Mac App/Publishing/TBSFTPPublisher.m +++ b/Mac App/Publishing/TBSFTPPublisher.m @@ -68,7 +68,7 @@ - (NSString *)passwordFromKeychain { - (void)publish { self.site.published = YES; - [self.site process:nil]; + [self.site processIncludingDrafts:NO error:nil]; self.site.published = NO; NSTask *rsync = [[NSTask alloc] init]; diff --git a/Mac App/TBSiteDocument.m b/Mac App/TBSiteDocument.m index e75a47b..6d178fe 100644 --- a/Mac App/TBSiteDocument.m +++ b/Mac App/TBSiteDocument.m @@ -32,7 +32,7 @@ - (void)startPreview:(TBSiteDocumentPreviewCallback)callback { MAWeakSelfImport(); [[NSProcessInfo processInfo] disableSuddenTermination]; - if (![self.site process:&error]) { + if (![self.site processIncludingDrafts:YES error:&error]) { callback(nil, error); return; } @@ -95,7 +95,7 @@ - (void)reloadSite { NSError *error; [[NSProcessInfo processInfo] disableSuddenTermination]; - if (![self.site process:&error]) { + if (![self.site processIncludingDrafts:YES error:&error]) { [NSApp tb_presentErrorOnMainQueue:error]; return; } diff --git a/Shared/TBSite.h b/Shared/TBSite.h index 93d7d37..1565455 100644 --- a/Shared/TBSite.h +++ b/Shared/TBSite.h @@ -39,13 +39,15 @@ /*! Process the entire site, writing the output into the destination directory. + @param includeDrafts + If set to YES, the generated sites will include any posts that are drafts. @param error If the return value is NO, then this argument will contain an NSError object describing what went wrong. @return YES on successful processing, NO if an error was encountered. */ -- (BOOL)process:(NSError **)error; +- (BOOL)processIncludingDrafts:(BOOL)includeDrafts error:(NSError **)error; /*! Parse all post files into TBPost objects. diff --git a/Shared/TBSite.m b/Shared/TBSite.m index 17c1375..3e44fb4 100644 --- a/Shared/TBSite.m +++ b/Shared/TBSite.m @@ -47,7 +47,7 @@ - (instancetype)initWithRoot:(NSURL *)root { #pragma mark - Site Processing -- (BOOL)process:(NSError **)error { +- (BOOL)processIncludingDrafts:(BOOL)includeDrafts error:(NSError **)error { if (![self loadRawDefaultTemplate:error]) return NO; @@ -57,7 +57,7 @@ - (BOOL)process:(NSError **)error { if (![self parsePosts:error]) return NO; - if (![self writePosts:error]) + if (![self writePostsIncludingDrafts:includeDrafts error:error]) return NO; if (![self writeFeed:error]) @@ -169,8 +169,12 @@ - (BOOL)parsePosts:(NSError **)error { } -- (BOOL)writePosts:(NSError **)error { +- (BOOL)writePostsIncludingDrafts:(BOOL)includeDrafts error:(NSError **)error { for (TBPost *post in self.posts) { + if (includeDrafts && post.draft) { + continue; + } + post.stylesheets = @[@{@"stylesheetName": @"post"}]; // Create the path to the folder where we are going to write the post file From 904a84d08f7a2ea26ea6ca7d23f843aecb7345a2 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Wed, 4 Sep 2013 23:50:37 -0400 Subject: [PATCH 23/30] Document new code. --- Shared/TBPost.h | 28 ++++++++++++++++++ Shared/TSPostMetadata.h | 63 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 87ebb61..65e62fd 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -43,6 +43,20 @@ */ - (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error; +/*! + Create a TBPost object with a title and slug. + @param title + The title of the post. + @param slug + The slug of the post. + @param site + The TBSite object which contains the post. + @param error + If the return value is nil, then this argument will contain an NSError + object describing what went wrong. + @return + A TBPost object, or nil if an error was encountered. + */ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error; /*! @@ -74,10 +88,24 @@ */ @property (nonatomic, strong) NSString *slug; +/*! + @property metadata + Any metadata that goes along with the post. Examaples include the post + date. + */ @property (nonatomic, strong) TSPostMetadata *metadata; +/*! + @property draft + The state of the post, i.e. is it a draft (unfinished). + */ @property (nonatomic, assign) BOOL draft; +/*! + @property postDirectory + The directory the post resides in. Contains the post markdown file (known + as slug.md) and metadata file. + */ @property (nonatomic, strong) NSURL *postDirectory; /*! diff --git a/Shared/TSPostMetadata.h b/Shared/TSPostMetadata.h index 7dabefe..360f3c1 100644 --- a/Shared/TSPostMetadata.h +++ b/Shared/TSPostMetadata.h @@ -9,22 +9,85 @@ #import +/*! + @class TSPostMetdata + @discussion Post metadata represents any extra data, i.e. metadata, about + a post (data other than the post itself). Metadata is written to the disk + in a standized format. + */ + @interface TSPostMetadata : NSObject #define METADATA_FILENAME @"metadata.json" +/*! + @property postDirectory + The directory the post resides in. Contains the post markdown file (known + as slug.md) and metadata file. + */ @property (retain, strong) NSURL *postDirectory; + +/*! + @property path + The complete path to the metadata file. + */ @property (retain, strong) NSURL *path; +/*! + @property draft + The state of the post, i.e. is it a draft (unfinished). + */ @property (assign) BOOL draft; + +/*! + @property publishedDate + The date when the post was published and was no longer a draft. + */ @property (retain, strong) NSDate *publishedDate; +/*! + Create an empty metadata object. + */ - (instancetype)init; + +/*! + Create an metadata object from a post directory. + @param error + If the return value is nil, then this argument will contain an NSError + object describing what went wrong. + @return + A TSPostMetadata object, or nil if an error was encountered. + */ - (instancetype)initWithPostDirectory:(NSURL *)directory withError:(NSError **)error; +/*! + Extract the metadata data from the given directory. + + Populates the member variables with this data. + + @param dictionary + Dictionary containing data. + */ - (void)extractDataFromDictionary:(NSDictionary *)dictionary; +/*! + Read the metadata file and store the data in the member variables. + @param error + If the return value is nil, then this argument will contain an NSError + object describing what went wrong. + @return + YES if an error was encountered. + */ - (BOOL)readWithError:(NSError **)error; + +/*! + Write the metadata file from data in the member variables. + @param error + If the return value is nil, then this argument will contain an NSError + object describing what went wrong. + @return + YES if an error was encountered. + */ - (BOOL)writeWithError:(NSError **)error; @end From daa1ce95f110c959aa0534d3c355cfb2b77fe19f Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 5 Sep 2013 08:37:33 -0400 Subject: [PATCH 24/30] Use the correct post label colors. The placeholder text for content was not being set a color. Changes the date to be the correct color blue (as used previously). Drafts are now gray instead of red. --- Mac App/Controllers/TBPostsView.xib | 1 + Mac App/Controllers/TSPostTableCellView.m | 18 +++++++++--- .../NSTextField+TBAdditions.h | 15 ++++++++++ .../NSTextField+TBAdditions.m | 29 +++++++++++++++++++ Tribo.xcodeproj/project.pbxproj | 6 ++++ 5 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 Shared/System Additions/NSTextField+TBAdditions.h create mode 100644 Shared/System Additions/NSTextField+TBAdditions.m diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index 81a8afc..32f9d0c 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -189,6 +189,7 @@ {{1, 119}, {223, 15}} + _NS:1847 NO 1 diff --git a/Mac App/Controllers/TSPostTableCellView.m b/Mac App/Controllers/TSPostTableCellView.m index 378270f..7a07794 100644 --- a/Mac App/Controllers/TSPostTableCellView.m +++ b/Mac App/Controllers/TSPostTableCellView.m @@ -8,6 +8,8 @@ #import "TSPostTableCellView.h" +#import "NSTextField+TBAdditions.h" + @implementation TSPostTableCellView @synthesize title, date, draft, postExcerpt; @@ -24,18 +26,26 @@ - (id)initWithFrame:(NSRect)frame - (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle { switch (backgroundStyle) { - case NSBackgroundStyleDark: + case NSBackgroundStyleDark: { [title setTextColor:[NSColor whiteColor]]; [date setTextColor:[NSColor whiteColor]]; [draft setTextColor:[NSColor whiteColor]]; [postExcerpt setTextColor:[NSColor whiteColor]]; + + [postExcerpt tb_setPlaceholderTextColor:[NSColor alternateSelectedControlColor]]; + break; - default: + } + default: { [title setTextColor:[NSColor blackColor]]; - [date setTextColor:[NSColor blueColor]]; - [draft setTextColor:[NSColor redColor]]; + [date setTextColor:[NSColor alternateSelectedControlColor]]; + [draft setTextColor:[NSColor grayColor]]; [postExcerpt setTextColor:[NSColor grayColor]]; + + [postExcerpt tb_setPlaceholderTextColor:[NSColor grayColor]]; + break; + } } } diff --git a/Shared/System Additions/NSTextField+TBAdditions.h b/Shared/System Additions/NSTextField+TBAdditions.h new file mode 100644 index 0000000..748c712 --- /dev/null +++ b/Shared/System Additions/NSTextField+TBAdditions.h @@ -0,0 +1,15 @@ +// +// NSTextField+TBAdditions.h +// Tribo +// +// Created by Tanner Smith on 9/5/13. +// Copyright (c) 2013 Opt-6 Products, LLC. All rights reserved. +// + +#import + +@interface NSTextField (TBAdditions) + +- (void)tb_setPlaceholderTextColor:(NSColor *)aColor; + +@end diff --git a/Shared/System Additions/NSTextField+TBAdditions.m b/Shared/System Additions/NSTextField+TBAdditions.m new file mode 100644 index 0000000..21e6972 --- /dev/null +++ b/Shared/System Additions/NSTextField+TBAdditions.m @@ -0,0 +1,29 @@ +// +// NSTextField+TBAdditions.m +// Tribo +// +// Created by Tanner Smith on 9/5/13. +// Copyright (c) 2013 Opt-6 Products, LLC. All rights reserved. +// + +#import "NSTextField+TBAdditions.h" + +@implementation NSTextField (TBAdditions) + +- (void)tb_setPlaceholderTextColor:(NSColor *)aColor { + NSString *placeholderString = [[self cell] placeholderString]; + + if (!placeholderString) { + placeholderString = [[[self cell] placeholderAttributedString] string]; + + if (!placeholderString) { + return; + } + } + + NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:placeholderString attributes:@{aColor : NSForegroundColorAttributeName}]; + + [[self cell] setPlaceholderAttributedString:attributedString]; +} + +@end diff --git a/Tribo.xcodeproj/project.pbxproj b/Tribo.xcodeproj/project.pbxproj index 85203e3..10cabf7 100644 --- a/Tribo.xcodeproj/project.pbxproj +++ b/Tribo.xcodeproj/project.pbxproj @@ -164,6 +164,7 @@ 9BEF1C7A14F5CFCF0072B295 /* TBSourceViewControllerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7814F5CFCF0072B295 /* TBSourceViewControllerViewController.m */; }; 9BEF1C7B14F5CFCF0072B295 /* TBSourceViewControllerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */; }; C35C2A9717D8100800A6BE68 /* TSPostTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */; }; + C35C2A9A17D8B14B00A6BE68 /* NSTextField+TBAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = C35C2A9917D8B14B00A6BE68 /* NSTextField+TBAdditions.m */; }; C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */; }; /* End PBXBuildFile section */ @@ -385,6 +386,8 @@ 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TBSourceViewControllerView.xib; sourceTree = ""; }; C35C2A9517D8100800A6BE68 /* TSPostTableCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSPostTableCellView.h; path = Controllers/TSPostTableCellView.h; sourceTree = ""; }; C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSPostTableCellView.m; path = Controllers/TSPostTableCellView.m; sourceTree = ""; }; + C35C2A9817D8B14B00A6BE68 /* NSTextField+TBAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextField+TBAdditions.h"; sourceTree = ""; }; + C35C2A9917D8B14B00A6BE68 /* NSTextField+TBAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextField+TBAdditions.m"; sourceTree = ""; }; C3ACCD1617CD1EEF0002CE67 /* TSPostMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSPostMetadata.h; sourceTree = ""; }; C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSPostMetadata.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -464,6 +467,8 @@ 4A3DE5FD176CF16C00C20026 /* NSDateFormatter+TBAdditions.m */, 4AF2BFEB17B88AB6008D67AB /* NSResponder+TBAdditions.h */, 4AF2BFEC17B88AB6008D67AB /* NSResponder+TBAdditions.m */, + C35C2A9817D8B14B00A6BE68 /* NSTextField+TBAdditions.h */, + C35C2A9917D8B14B00A6BE68 /* NSTextField+TBAdditions.m */, ); path = "System Additions"; sourceTree = ""; @@ -1175,6 +1180,7 @@ 4AA86A70174124C300046691 /* TBPost+TemplateAdditions.m in Sources */, C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */, C35C2A9717D8100800A6BE68 /* TSPostTableCellView.m in Sources */, + C35C2A9A17D8B14B00A6BE68 /* NSTextField+TBAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 582cf067714365d35cb7620dcb0f8d7173d7c3a2 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 5 Sep 2013 08:52:12 -0400 Subject: [PATCH 25/30] Only have one menu item to change draft status. Set the state rather than changing the menu item. --- Mac App/Controllers/TBPostsView.xib | 67 +++++---------------- Mac App/Controllers/TBPostsViewController.h | 6 +- Mac App/Controllers/TBPostsViewController.m | 24 ++++---- 3 files changed, 28 insertions(+), 69 deletions(-) diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index 32f9d0c..c108b19 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -237,15 +237,7 @@ - Unmark as Draft - - 2147483647 - - - - - - Mark as Draft + Draft 2147483647 @@ -347,36 +339,20 @@ 251 - - unmarkDraft: + + draftMenuItem - 576 + 713 - markDraft: - - - - 577 - - - - markDraftMenuItem - - - - 586 - - - - unmarkDraftMenuItem + draft: - 587 + 714 @@ -793,7 +769,6 @@ - Post Context Menu @@ -1262,12 +1237,6 @@ - - 575 - - - Menu Item - Mark as Draft - 654 @@ -1384,7 +1353,6 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1408,7 +1376,7 @@ - 709 + 714 @@ -1416,19 +1384,19 @@ TBPostsViewController TBViewController + id id - id id id id - - editPost: + + draft: id - - markDraft: + + editPost: id @@ -1445,23 +1413,18 @@ - NSMenuItem + NSMenuItem NSTableView - NSMenuItem - - markDraftMenuItem + + draftMenuItem NSMenuItem postTableView NSTableView - - unmarkDraftMenuItem - NSMenuItem - IBProjectSource diff --git a/Mac App/Controllers/TBPostsViewController.h b/Mac App/Controllers/TBPostsViewController.h index 1ec8315..10af741 100644 --- a/Mac App/Controllers/TBPostsViewController.h +++ b/Mac App/Controllers/TBPostsViewController.h @@ -13,12 +13,10 @@ @interface TBPostsViewController : TBViewController @property (nonatomic, assign) IBOutlet NSTableView *postTableView; -@property (assign) IBOutlet NSMenuItem *unmarkDraftMenuItem; -@property (assign) IBOutlet NSMenuItem *markDraftMenuItem; +@property (assign) IBOutlet NSMenuItem *draftMenuItem; - (IBAction)editPost:(id)sender; -- (IBAction)unmarkDraft:(id)sender; -- (IBAction)markDraft:(id)sender; +- (IBAction)draft:(id)sender; - (IBAction)previewPost:(id)sender; - (IBAction)revealPost:(id)sender; @end diff --git a/Mac App/Controllers/TBPostsViewController.m b/Mac App/Controllers/TBPostsViewController.m index a08d08b..26e3d23 100644 --- a/Mac App/Controllers/TBPostsViewController.m +++ b/Mac App/Controllers/TBPostsViewController.m @@ -23,7 +23,7 @@ - (void)undoMoveToTrashForURLs:(NSDictionary *)URLs; @implementation TBPostsViewController -@synthesize markDraftMenuItem, unmarkDraftMenuItem; +@synthesize draftMenuItem; #pragma mark - View Controller Configuration @@ -48,16 +48,20 @@ - (void)menuNeedsUpdate:(NSMenu *)menu { NSInteger clickedRow = [self.postTableView clickedRow]; if (clickedRow < 0) { - [markDraftMenuItem setHidden:YES]; - [unmarkDraftMenuItem setHidden:YES]; + [draftMenuItem setHidden:YES]; return; } TBPost *clickedPost = (document.site.posts)[clickedRow]; - [markDraftMenuItem setHidden:[clickedPost draft] == YES]; - [unmarkDraftMenuItem setHidden:[clickedPost draft] == NO]; + [draftMenuItem setHidden:NO]; + + if ([clickedPost draft]) { + [draftMenuItem setState:NSOnState]; + } else { + [draftMenuItem setState:NSOffState]; + } } #pragma mark - Actions @@ -68,16 +72,10 @@ - (IBAction)editPost:(id)sender { [[NSWorkspace sharedWorkspace] openURL:clickedPost.URL]; } -- (IBAction)unmarkDraft:(id)sender { - TBPost *clickedPost = (self.document.site.posts)[[self.postTableView clickedRow]]; - - [clickedPost setDraft:NO]; -} - -- (IBAction)markDraft:(id)sender { +- (IBAction)draft:(id)sender { TBPost *clickedPost = (self.document.site.posts)[[self.postTableView clickedRow]]; - [clickedPost setDraft:YES]; + [clickedPost setDraft:![clickedPost draft]]; } - (IBAction)previewPost:(id)sender { From d9bfcba189b24e3e02491287eddd892185372637 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 5 Sep 2013 10:11:05 -0400 Subject: [PATCH 26/30] Change "TS" prefix to "TB". I realized that "TB" is the class prefix for "Tribo". --- ...tTableCellView.h => TBPostTableCellView.h} | 2 +- ...tTableCellView.m => TBPostTableCellView.m} | 4 +- Mac App/Controllers/TBPostsView.xib | 44 +------------------ Shared/TBPost.h | 4 +- Shared/TBPost.m | 4 +- Shared/{TSPostMetadata.h => TBPostMetadata.h} | 2 +- Shared/{TSPostMetadata.m => TBPostMetadata.m} | 4 +- Tribo.xcodeproj/project.pbxproj | 24 +++++----- 8 files changed, 24 insertions(+), 64 deletions(-) rename Mac App/Controllers/{TSPostTableCellView.h => TBPostTableCellView.h} (88%) rename Mac App/Controllers/{TSPostTableCellView.m => TBPostTableCellView.m} (95%) rename Shared/{TSPostMetadata.h => TBPostMetadata.h} (98%) rename Shared/{TSPostMetadata.m => TBPostMetadata.m} (97%) diff --git a/Mac App/Controllers/TSPostTableCellView.h b/Mac App/Controllers/TBPostTableCellView.h similarity index 88% rename from Mac App/Controllers/TSPostTableCellView.h rename to Mac App/Controllers/TBPostTableCellView.h index 2f54431..bbe0844 100644 --- a/Mac App/Controllers/TSPostTableCellView.h +++ b/Mac App/Controllers/TBPostTableCellView.h @@ -8,7 +8,7 @@ #import -@interface TSPostTableCellView : NSTableCellView +@interface TBPostTableCellView : NSTableCellView @property (assign) IBOutlet NSTextField *title; @property (assign) IBOutlet NSTextField *draft; diff --git a/Mac App/Controllers/TSPostTableCellView.m b/Mac App/Controllers/TBPostTableCellView.m similarity index 95% rename from Mac App/Controllers/TSPostTableCellView.m rename to Mac App/Controllers/TBPostTableCellView.m index 7a07794..810f422 100644 --- a/Mac App/Controllers/TSPostTableCellView.m +++ b/Mac App/Controllers/TBPostTableCellView.m @@ -6,11 +6,11 @@ // Copyright (c) 2013 Opt-6 Products, LLC. All rights reserved. // -#import "TSPostTableCellView.h" +#import "TBPostTableCellView.h" #import "NSTextField+TBAdditions.h" -@implementation TSPostTableCellView +@implementation TBPostTableCellView @synthesize title, date, draft, postExcerpt; diff --git a/Mac App/Controllers/TBPostsView.xib b/Mac App/Controllers/TBPostsView.xib index c108b19..d11885a 100644 --- a/Mac App/Controllers/TBPostsView.xib +++ b/Mac App/Controllers/TBPostsView.xib @@ -43,7 +43,7 @@ NSApplication - + 256 @@ -60,7 +60,6 @@ {410, 360} - _NS:1828 YES @@ -163,7 +162,6 @@ {410, 360} - _NS:1826 @@ -175,7 +173,6 @@ -2147483392 {{224, 17}, {15, 102}} - _NS:1845 NO @@ -188,8 +185,6 @@ -2147483392 {{1, 119}, {223, 15}} - - _NS:1847 NO 1 @@ -200,7 +195,6 @@ {410, 360} - _NS:1824 133680 @@ -214,8 +208,6 @@ {410, 360} - - @@ -1303,7 +1295,7 @@ - TSPostTableCellView + TBPostTableCellView @@ -1458,38 +1450,6 @@ ./Classes/TBViewController.h - - TSPostTableCellView - NSTableCellView - - NSTextField - NSTextField - NSTextField - NSTextField - - - - date - NSTextField - - - draft - NSTextField - - - postExcerpt - NSTextField - - - title - NSTextField - - - - IBProjectSource - ./Classes/TSPostTableCellView.h - - 0 diff --git a/Shared/TBPost.h b/Shared/TBPost.h index 65e62fd..e07d344 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -9,7 +9,7 @@ #import "TBPage.h" -#import "TSPostMetadata.h" +#import "TBPostMetadata.h" /*! @class TBPost @@ -93,7 +93,7 @@ Any metadata that goes along with the post. Examaples include the post date. */ -@property (nonatomic, strong) TSPostMetadata *metadata; +@property (nonatomic, strong) TBPostMetadata *metadata; /*! @property draft diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 8cda19a..46ca621 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -21,7 +21,7 @@ - (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError ** self.slug = [URL lastPathComponent]; - self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:URL withError:error]; + self.metadata = [[TBPostMetadata alloc] initWithPostDirectory:URL withError:error]; if (self.metadata) { URL = [URL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.md", self.slug]]; @@ -47,7 +47,7 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB } // Metadata File - self.metadata = [[TSPostMetadata alloc] initWithPostDirectory:self.postDirectory withError:error]; + self.metadata = [[TBPostMetadata alloc] initWithPostDirectory:self.postDirectory withError:error]; [self.metadata writeWithError:error]; diff --git a/Shared/TSPostMetadata.h b/Shared/TBPostMetadata.h similarity index 98% rename from Shared/TSPostMetadata.h rename to Shared/TBPostMetadata.h index 360f3c1..3f4acb7 100644 --- a/Shared/TSPostMetadata.h +++ b/Shared/TBPostMetadata.h @@ -16,7 +16,7 @@ in a standized format. */ -@interface TSPostMetadata : NSObject +@interface TBPostMetadata : NSObject #define METADATA_FILENAME @"metadata.json" diff --git a/Shared/TSPostMetadata.m b/Shared/TBPostMetadata.m similarity index 97% rename from Shared/TSPostMetadata.m rename to Shared/TBPostMetadata.m index ed2d022..0a11b5e 100644 --- a/Shared/TSPostMetadata.m +++ b/Shared/TBPostMetadata.m @@ -7,11 +7,11 @@ // See the included License.md file. // -#import "TSPostMetadata.h" +#import "TBPostMetadata.h" #import "NSDateFormatter+TBAdditions.h" -@implementation TSPostMetadata +@implementation TBPostMetadata @synthesize draft, publishedDate; @synthesize postDirectory; diff --git a/Tribo.xcodeproj/project.pbxproj b/Tribo.xcodeproj/project.pbxproj index 10cabf7..787bfeb 100644 --- a/Tribo.xcodeproj/project.pbxproj +++ b/Tribo.xcodeproj/project.pbxproj @@ -163,9 +163,9 @@ 9B6B669D14F3546C00759DC3 /* TBAsset.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B6B669B14F3546900759DC3 /* TBAsset.m */; }; 9BEF1C7A14F5CFCF0072B295 /* TBSourceViewControllerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7814F5CFCF0072B295 /* TBSourceViewControllerViewController.m */; }; 9BEF1C7B14F5CFCF0072B295 /* TBSourceViewControllerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */; }; - C35C2A9717D8100800A6BE68 /* TSPostTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */; }; + C35C2A9717D8100800A6BE68 /* TBPostTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = C35C2A9617D8100800A6BE68 /* TBPostTableCellView.m */; }; C35C2A9A17D8B14B00A6BE68 /* NSTextField+TBAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = C35C2A9917D8B14B00A6BE68 /* NSTextField+TBAdditions.m */; }; - C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */; }; + C3ACCD1817CD1EF00002CE67 /* TBPostMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = C3ACCD1717CD1EEF0002CE67 /* TBPostMetadata.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -384,12 +384,12 @@ 9BEF1C7714F5CFCF0072B295 /* TBSourceViewControllerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBSourceViewControllerViewController.h; sourceTree = ""; }; 9BEF1C7814F5CFCF0072B295 /* TBSourceViewControllerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBSourceViewControllerViewController.m; sourceTree = ""; }; 9BEF1C7914F5CFCF0072B295 /* TBSourceViewControllerView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TBSourceViewControllerView.xib; sourceTree = ""; }; - C35C2A9517D8100800A6BE68 /* TSPostTableCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSPostTableCellView.h; path = Controllers/TSPostTableCellView.h; sourceTree = ""; }; - C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSPostTableCellView.m; path = Controllers/TSPostTableCellView.m; sourceTree = ""; }; + C35C2A9517D8100800A6BE68 /* TBPostTableCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TBPostTableCellView.h; path = Controllers/TBPostTableCellView.h; sourceTree = ""; }; + C35C2A9617D8100800A6BE68 /* TBPostTableCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TBPostTableCellView.m; path = Controllers/TBPostTableCellView.m; sourceTree = ""; }; C35C2A9817D8B14B00A6BE68 /* NSTextField+TBAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextField+TBAdditions.h"; sourceTree = ""; }; C35C2A9917D8B14B00A6BE68 /* NSTextField+TBAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextField+TBAdditions.m"; sourceTree = ""; }; - C3ACCD1617CD1EEF0002CE67 /* TSPostMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSPostMetadata.h; sourceTree = ""; }; - C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSPostMetadata.m; sourceTree = ""; }; + C3ACCD1617CD1EEF0002CE67 /* TBPostMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBPostMetadata.h; sourceTree = ""; }; + C3ACCD1717CD1EEF0002CE67 /* TBPostMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBPostMetadata.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -591,8 +591,8 @@ 4A00337A1430216500F6702F /* TBSite.m */, 4A00337C143028CA00F6702F /* TBPost.h */, 4A00337D143028CA00F6702F /* TBPost.m */, - C3ACCD1617CD1EEF0002CE67 /* TSPostMetadata.h */, - C3ACCD1717CD1EEF0002CE67 /* TSPostMetadata.m */, + C3ACCD1617CD1EEF0002CE67 /* TBPostMetadata.h */, + C3ACCD1717CD1EEF0002CE67 /* TBPostMetadata.m */, 4A72F14014368E1200A164E9 /* TBPage.h */, 4A72F14114368E1200A164E9 /* TBPage.m */, 9B6B669A14F3546900759DC3 /* TBAsset.h */, @@ -887,8 +887,8 @@ isa = PBXGroup; children = ( 4AAC8E5E1456616F00AE978A /* TBPostsView.xib */, - C35C2A9517D8100800A6BE68 /* TSPostTableCellView.h */, - C35C2A9617D8100800A6BE68 /* TSPostTableCellView.m */, + C35C2A9517D8100800A6BE68 /* TBPostTableCellView.h */, + C35C2A9617D8100800A6BE68 /* TBPostTableCellView.m */, 4AAC8E5B1456609300AE978A /* TBPostsViewController.h */, 4AAC8E5C1456609300AE978A /* TBPostsViewController.m */, 4ABD6A5014417DBE007E2F5D /* TBQLPreviewView.xib */, @@ -1178,8 +1178,8 @@ 4A99C42917B4091D0023C9A9 /* CZAFileWatcher.m in Sources */, 4AA86A6C173EF28200046691 /* TBSite+TemplateAdditions.m in Sources */, 4AA86A70174124C300046691 /* TBPost+TemplateAdditions.m in Sources */, - C3ACCD1817CD1EF00002CE67 /* TSPostMetadata.m in Sources */, - C35C2A9717D8100800A6BE68 /* TSPostTableCellView.m in Sources */, + C3ACCD1817CD1EF00002CE67 /* TBPostMetadata.m in Sources */, + C35C2A9717D8100800A6BE68 /* TBPostTableCellView.m in Sources */, C35C2A9A17D8B14B00A6BE68 /* NSTextField+TBAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; From e1711fe957de893c2c69957efdc24ed6663ac9f9 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 5 Sep 2013 17:06:11 -0400 Subject: [PATCH 27/30] Revert "Clean up code." This reverts commit f22fc3ac17a3632e0556761b343a8fed1f58a8b5. Conflicts: Shared/TBPost.m Shared/TBSite.m --- Mac App/TBSiteDocument.m | 2 +- Shared/TBAsset.m | 25 +--- Shared/TBPage.h | 5 +- Shared/TBPage.m | 67 +++------ Shared/TBPost.h | 4 +- Shared/TBPost.m | 87 +++++------- Shared/TBSite.h | 2 +- Shared/TBSite.m | 283 ++++++++++----------------------------- 8 files changed, 136 insertions(+), 339 deletions(-) diff --git a/Mac App/TBSiteDocument.m b/Mac App/TBSiteDocument.m index 6d178fe..0cef1fb 100644 --- a/Mac App/TBSiteDocument.m +++ b/Mac App/TBSiteDocument.m @@ -156,7 +156,7 @@ - (void)windowControllerDidLoadNib:(NSWindowController *)windowController { } - (BOOL)readFromURL:(NSURL *)URL ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError { - self.site = [[TBSite alloc] initWithRoot:URL]; + self.site = [TBSite siteWithRoot:URL]; self.site.delegate = self; BOOL success = [self.site parsePosts:outError]; diff --git a/Shared/TBAsset.m b/Shared/TBAsset.m index af324e9..eed85d7 100644 --- a/Shared/TBAsset.m +++ b/Shared/TBAsset.m @@ -12,38 +12,27 @@ @implementation TBAsset + (NSArray *)assetsFromDirectory:(NSURL*)URL error:(NSError **)error { + NSMutableArray *assets = [NSMutableArray array]; - NSArray *properties = @[NSURLTypeIdentifierKey, NSURLLocalizedNameKey, NSURLIsDirectoryKey]; - - NSDirectoryEnumerator *enumerator; - enumerator = [[NSFileManager defaultManager] enumeratorAtURL:URL - includingPropertiesForKeys:properties - options:NSDirectoryEnumerationSkipsHiddenFiles|NSDirectoryEnumerationSkipsSubdirectoryDescendants - errorHandler:nil]; - + NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:URL includingPropertiesForKeys:properties options:NSDirectoryEnumerationSkipsHiddenFiles|NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:nil]; for (NSURL *assetURL in enumerator) { + NSDictionary *resourceValues = [assetURL resourceValuesForKeys:properties error:error]; + if (!resourceValues) return nil; - if (!resourceValues) { - return nil; - } - - TBAsset *asset = [[TBAsset alloc] init]; - + TBAsset *asset = [[self class] new]; asset.URL = assetURL; asset.displayName = resourceValues[NSURLLocalizedNameKey]; asset.type = resourceValues[NSURLTypeIdentifierKey]; if ([resourceValues[NSURLIsDirectoryKey] boolValue]) { asset.children = [[self class] assetsFromDirectory:assetURL error:error]; - - if (!asset.children) { - return nil; - } + if (!asset.children) return nil; } [assets addObject:asset]; + } [assets sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES]]]; diff --git a/Shared/TBPage.h b/Shared/TBPage.h index 98e1544..f0974f7 100644 --- a/Shared/TBPage.h +++ b/Shared/TBPage.h @@ -26,7 +26,6 @@ /*! Create a TBPage object from a file on-disk. - @param URL A filesystem URL pointing to the page file. @param site @@ -37,7 +36,9 @@ @return An instance of TBPage, or nil if an error was encountered. */ -- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error; ++ (instancetype)pageWithURL:(NSURL *)URL + inSite:(TBSite *)site + error:(NSError **)error; /*! @property URL diff --git a/Shared/TBPage.m b/Shared/TBPage.m index a0b679b..f39d2e1 100644 --- a/Shared/TBPage.m +++ b/Shared/TBPage.m @@ -11,100 +11,63 @@ @implementation TBPage -- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { - if (self = [super init]) { - self.URL = URL; - self.site = site; - - [self parse:error]; ++ (instancetype)pageWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { + TBPage *page = [super new]; + if (page) { + page.URL = URL; + page.site = site; + [page parse:error]; } - - return self; + return page; } - (BOOL)parse:(NSError **)error { [self loadContent]; [self parseTitle]; [self parseStylesheets]; - return YES; } - (void)loadContent { - self.content = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; + NSString *content = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; + self.content = content; } -/*! - * Parse the title from the content. - * - * Titles are optional. - * They take the following form: - * - */ - (void)parseTitle { - if (!self.content || ![self.content length]) { - // No content found, return - return; - } - + if (!self.content || ![self.content length]) return; + // Titles are optional. They take the following form: + // NSMutableString *content = [self.content mutableCopy]; - NSRegularExpression *headerRegex = [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; - NSRange firstLineRange = NSMakeRange(0, [content rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location); NSString *firstLine = [content substringWithRange:firstLineRange]; - NSTextCheckingResult *titleResult = [headerRegex firstMatchInString:firstLine options:0 range:NSMakeRange(0, firstLine.length)]; - if (titleResult) { - // Found the title! self.title = [firstLine substringWithRange:[titleResult rangeAtIndex:1]]; - [content deleteCharactersInRange:NSMakeRange(firstLineRange.location, firstLineRange.length + 1)]; } - self.content = content; } -/*! - * Parse the stylesheet from the content. - * - * Stylesheets are optional. - * They take the following form: - * - * - * They are the second line in the file if there is a title; first if there is not a title. - */ - (void)parseStylesheets { - if (!self.content || ![self.content length]) { - return; - } - + if (!self.content || ![self.content length]) return; + // Stylsheets are also optional. They are on the second line (or first if there is no title), and look like this: + // NSMutableString *content = [self.content mutableCopy]; - NSRegularExpression *stylesheetsRegex = [NSRegularExpression regularExpressionWithPattern:@"" options:0 error:nil]; - NSRange secondLineRange = NSMakeRange(0, [content rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location); NSString *secondLine = [content substringWithRange:secondLineRange]; - NSTextCheckingResult *stylesheetsResult = [stylesheetsRegex firstMatchInString:secondLine options:0 range:NSMakeRange(0, secondLine.length)]; - if (stylesheetsResult) { NSString *rawMatch = [secondLine substringWithRange:[stylesheetsResult rangeAtIndex:1]]; - NSArray *stylesheetNames = [rawMatch componentsSeparatedByString:@", "]; - NSMutableArray *stylesheetDictionaries = [NSMutableArray array]; - for (NSString *stylesheetName in stylesheetNames) { [stylesheetDictionaries addObject:@{@"stylesheetName": stylesheetName}]; } - self.stylesheets = stylesheetDictionaries; - [content deleteCharactersInRange:secondLineRange]; } - self.content = content; } diff --git a/Shared/TBPost.h b/Shared/TBPost.h index e07d344..c1d535e 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -41,7 +41,9 @@ @return A TBPost object, or nil if an error was encountered. */ -- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error; ++ (instancetype)postWithURL:(NSURL *)URL + inSite:(TBSite *)site + error:(NSError **)error; /*! Create a TBPost object with a title and slug. diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 46ca621..e4a1fca 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -16,20 +16,28 @@ @implementation TBPost -- (instancetype)initWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { - self.postDirectory = URL; ++ (instancetype)postWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { + NSString *slug = [URL lastPathComponent]; - self.slug = [URL lastPathComponent]; + NSURL *postURL = [URL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.md", slug]]; - self.metadata = [[TBPostMetadata alloc] initWithPostDirectory:URL withError:error]; + TBPost *post = [super pageWithURL:postURL inSite:site error:error]; - if (self.metadata) { - URL = [URL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.md", self.slug]]; - - return [super initWithURL:URL inSite:site error:error]; - } else { + if (post) { + post.postDirectory = URL; + + post.slug = slug; + + post.metadata = [[TBPostMetadata alloc] initWithPostDirectory:URL withError:error]; + + if (post.metadata) { + return post; + } + return nil; } + + return nil; } - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error { @@ -68,63 +76,40 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB return self; } -- (BOOL)parse:(NSError **)error { +- (BOOL)parse:(NSError **)error { + [self loadMarkdownContent]; + if (![self parseSlug:error]) { return NO; } - - [self loadMarkdownContent]; + [self parseTitle]; return YES; } -- (void)loadMarkdownContent { - self.markdownContent = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; +- (void)loadMarkdownContent; { + NSString *markdownContent = [NSString stringWithContentsOfURL:self.URL encoding:NSUTF8StringEncoding error:nil]; + self.markdownContent = markdownContent; } -/*! - * Extracts the title from the markdown contents. - * - * Titles are optional. - * Titles are defined as a single '#' header on the first line of the document. - * Title must have '#' on both sides, e.g. '# Title of Post #' - */ - (void)parseTitle { - if (!self.markdownContent || ![self.markdownContent length]) { - // No markdown content found, return - return; - } - + // Titles are optional. A single # header on the first line of the document is regarded as the title. + if (!self.markdownContent || ![self.markdownContent length]) return; NSMutableString *markdownContent = [self.markdownContent mutableCopy]; - static NSRegularExpression *headerRegex; - - if (headerRegex == nil) { + if (headerRegex == nil) headerRegex = [NSRegularExpression regularExpressionWithPattern:@"#[ \\t](.*)[ \\t]#" options:0 error:nil]; - } - NSRange firstLineRange = NSMakeRange(0, [markdownContent rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location); - - if (firstLineRange.length == NSNotFound) { - // Can't find a first header - return; - } - + if (firstLineRange.length == NSNotFound) return; NSString *firstLine = [markdownContent substringWithRange:firstLineRange]; - NSTextCheckingResult *titleResult = [headerRegex firstMatchInString:firstLine options:0 range:NSMakeRange(0, firstLine.length)]; - if (titleResult) { self.title = [firstLine substringWithRange:[titleResult rangeAtIndex:1]]; - [markdownContent deleteCharactersInRange:NSMakeRange(firstLineRange.location, firstLineRange.length + 1)]; } - - // Remove the first new line after the title from the content [markdownContent deleteCharactersInRange:[markdownContent rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]]]; - self.markdownContent = markdownContent; } @@ -141,27 +126,19 @@ - (BOOL)parseSlug:(NSError **)error { } - (void)parseMarkdownContent { - if (!self.markdownContent || ![self.markdownContent length]) { - // No markdown content found, return - return; - } - - // Create and fill a buffer for with the raw markdown data + if (!self.markdownContent || ![self.markdownContent length]) return; + // Create and fill a buffer for with the raw markdown data. + if ([self.markdownContent length] == 0) return; struct sd_callbacks callbacks; struct html_renderopt options; - const char *rawMarkdown = [self.markdownContent cStringUsingEncoding:NSUTF8StringEncoding]; struct buf *smartyPantsOutputBuffer = bufnew(1); - sdhtml_smartypants(smartyPantsOutputBuffer, (const unsigned char *)rawMarkdown, strlen(rawMarkdown)); - // Parse the markdown into a new buffer using Sundown + // Parse the markdown into a new buffer using Sundown. struct buf *outputBuffer = bufnew(64); - sdhtml_renderer(&callbacks, &options, 0); - struct sd_markdown *markdown = sd_markdown_new(0, 16, &callbacks, &options); - sd_markdown_render(outputBuffer, smartyPantsOutputBuffer->data, smartyPantsOutputBuffer->size, markdown); sd_markdown_free(markdown); diff --git a/Shared/TBSite.h b/Shared/TBSite.h index 1565455..80f05c5 100644 --- a/Shared/TBSite.h +++ b/Shared/TBSite.h @@ -35,7 +35,7 @@ A TBSite instance, initialized to represent the site folder at the given root directory. */ -- (instancetype)initWithRoot:(NSURL *)root; ++ (instancetype)siteWithRoot:(NSURL *)root; /*! Process the entire site, writing the output into the destination directory. diff --git a/Shared/TBSite.m b/Shared/TBSite.m index 3e44fb4..d9901df 100644 --- a/Shared/TBSite.m +++ b/Shared/TBSite.m @@ -23,26 +23,19 @@ @implementation TBSite #pragma mark - Initialization -- (instancetype)initWithRoot:(NSURL *)root { - if (self = [super init]) { - self.root = root; - - self.destination = [root URLByAppendingPathComponent:@"Output" isDirectory:YES]; - self.sourceDirectory = [root URLByAppendingPathComponent:@"Source" isDirectory:YES]; - self.postsDirectory = [root URLByAppendingPathComponent:@"Posts" isDirectory:YES]; - self.templatesDirectory = [root URLByAppendingPathComponent:@"Templates" isDirectory:YES]; - - NSURL *metadataURL = [root URLByAppendingPathComponent:@"Info.plist" isDirectory:NO]; - NSData *metadataData = [NSData dataWithContentsOfURL:metadataURL]; - - self.metadata = [NSPropertyListSerialization propertyListFromData:metadataData mutabilityOption:NSPropertyListMutableContainersAndLeaves format:nil errorDescription:nil]; - - if (!self.metadata) { - [@{} writeToURL:metadataURL atomically:NO]; - } - } - - return self; ++ (instancetype)siteWithRoot:(NSURL *)root { + TBSite *site = [TBSite new]; + site.root = root; + site.destination = [root URLByAppendingPathComponent:@"Output" isDirectory:YES]; + site.sourceDirectory = [root URLByAppendingPathComponent:@"Source" isDirectory:YES]; + site.postsDirectory = [root URLByAppendingPathComponent:@"Posts" isDirectory:YES]; + site.templatesDirectory = [root URLByAppendingPathComponent:@"Templates" isDirectory:YES]; + NSURL *metadataURL = [root URLByAppendingPathComponent:@"Info.plist" isDirectory:NO]; + NSData *metadataData = [NSData dataWithContentsOfURL:metadataURL]; + site.metadata = [NSPropertyListSerialization propertyListFromData:metadataData mutabilityOption:NSPropertyListMutableContainersAndLeaves format:nil errorDescription:nil]; + if (!site.metadata) + [@{} writeToURL:metadataURL atomically:NO]; + return site; } #pragma mark - Site Processing @@ -69,101 +62,65 @@ - (BOOL)processIncludingDrafts:(BOOL)includeDrafts error:(NSError **)error { if (![self processSourceDirectory:error]) return NO; - return YES; + return YES; + } #pragma mark - Template Loading - (BOOL)loadRawDefaultTemplate:(NSError **)error { NSURL *defaultTemplateURL = [self.templatesDirectory URLByAppendingPathComponent:@"Default.mustache" isDirectory:NO]; - self.rawDefaultTemplate = [NSString stringWithContentsOfURL:defaultTemplateURL encoding:NSUTF8StringEncoding error:error]; - - if (!self.rawDefaultTemplate) { - return NO; - } - + if (!self.rawDefaultTemplate) return NO; return YES; } - (BOOL)loadPostTemplate:(NSError **)error { NSURL *postPartialURL = [self.templatesDirectory URLByAppendingPathComponent:@"Post.mustache" isDirectory:NO]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:postPartialURL.path] == NO) { - // No post template found - - if (error) { + if (![[NSFileManager defaultManager] fileExistsAtPath:postPartialURL.path]) { + if (error) *error = TBError.missingPostPartial(postPartialURL); - } - return NO; } - NSString *rawPostPartial = [NSString stringWithContentsOfURL:postPartialURL encoding:NSUTF8StringEncoding error:error]; - - if (!rawPostPartial) { - // No content in post template - return NO; - } - + if (!rawPostPartial) return NO; NSString *rawPostTemplate = [self.rawDefaultTemplate stringByReplacingOccurrencesOfString:@"{{{content}}}" withString:rawPostPartial]; - self.postTemplate = [GRMustacheTemplate templateFromString:rawPostTemplate error:error]; - - if (!self.postTemplate) { - // Could not create template - return NO; - } - + if (!self.postTemplate) return NO; return YES; } #pragma mark - Post Processing - (BOOL)parsePosts:(NSError **)error { - // Verify that the Posts directory exists and is a directory + + // Verify that the Posts directory exists and is a directory. BOOL postsDirectoryIsDirectory = NO; BOOL postsDirectoryExists = [[NSFileManager defaultManager] fileExistsAtPath:self.postsDirectory.path isDirectory:&postsDirectoryIsDirectory]; - - if (!postsDirectoryIsDirectory || !postsDirectoryExists) { + if (!postsDirectoryIsDirectory || !postsDirectoryExists){ if (error) { *error = TBError.missingPostsDirectory(self.postsDirectory); } - return NO; } - - // Parse the contents of the Posts directory into individual TBPost objects + + // Parse the contents of the Posts directory into individual TBPost objects. NSMutableArray *posts = [NSMutableArray array]; NSArray *postsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:self.postsDirectory includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:error]; - - if (!postsDirectoryContents) { - return NO; - } - + if (!postsDirectoryContents) return NO; for (NSURL *postURL in postsDirectoryContents) { - TBPost *post = [[TBPost alloc] initWithURL:postURL inSite:self error:error]; + TBPost *post = [TBPost postWithURL:postURL inSite:self error:error]; [post parseMarkdownContent]; - - if (post) { - [posts addObject:post]; - } + if (post) [posts addObject:post]; } - - self.posts = [NSMutableArray arrayWithArray:[[posts reverseObjectEnumerator] allObjects]]; + posts = [NSMutableArray arrayWithArray:[[posts reverseObjectEnumerator] allObjects]]; + self.posts = posts; // Prepare the asset object tree self.templateAssets = [TBAsset assetsFromDirectory:self.templatesDirectory error:error]; - - if (!self.templateAssets) { - return NO; - } - + if (!self.templateAssets) return NO; self.sourceAssets = [TBAsset assetsFromDirectory:self.sourceDirectory error:error]; - - if (!self.sourceAssets) { - return NO; - } + if (!self.sourceAssets) return NO; return YES; @@ -174,50 +131,36 @@ - (BOOL)writePostsIncludingDrafts:(BOOL)includeDrafts error:(NSError **)error { if (includeDrafts && post.draft) { continue; } - + post.stylesheets = @[@{@"stylesheetName": @"post"}]; - // Create the path to the folder where we are going to write the post file + // Create the path to the folder where we are going to write the post file. // The directory structure we create is /YYYY/MM/DD/slug/ - NSDateFormatter *postPathFormatter = [NSDateFormatter tb_cachedDateFormatterFromString:@"yyyy/MM/dd"]; NSString *directoryStructure = [postPathFormatter stringFromDate:post.date]; - NSURL *destinationDirectory = [[self.destination URLByAppendingPathComponent:directoryStructure isDirectory:YES] URLByAppendingPathComponent:post.slug isDirectory:YES]; - - // Create the destination directory - if ([[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error] == NO) + if (![[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error]) return NO; - // Filter the markdownContent of the post + // Filter the markdownContent of the post. NSString *originalContent = post.markdownContent; - NSString *filteredMarkdownContent = [self filteredContent:(originalContent ?: @"") fromFile:post.URL error:error]; - - if (!filteredMarkdownContent) { + if (!filteredMarkdownContent) return NO; - } - post.markdownContent = filteredMarkdownContent; - [post parseMarkdownContent]; - post.markdownContent = originalContent; - // Set up the template loader with this post's content, and then render it all into the post template + // Set up the template loader with this post's content, and then render it all into the post template. NSString *renderedContent = [self.postTemplate renderObject:post error:error]; - - if (!renderedContent) { + if (!renderedContent) return NO; - } // Write the post to the destination directory. NSURL *destinationURL = [destinationDirectory URLByAppendingPathComponent:@"index.html" isDirectory:NO]; - - if (![renderedContent writeToURL:destinationURL atomically:YES encoding:NSUTF8StringEncoding error:error]) { - // Could not write index.html - return NO; - } + if (![renderedContent writeToURL:destinationURL atomically:YES encoding:NSUTF8StringEncoding error:error]) + return NO; + } return YES; @@ -228,31 +171,14 @@ - (BOOL)writePostsIncludingDrafts:(BOOL)includeDrafts error:(NSError **)error { - (BOOL)writeFeed:(NSError **)error { NSURL *templateURL = [self.templatesDirectory URLByAppendingPathComponent:@"Feed.mustache"]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:templateURL.path] == NO) { - // Could not find feed template - return YES; - } - + if (![[NSFileManager defaultManager] fileExistsAtPath:templateURL.path]) return YES; GRMustacheTemplate *template = [GRMustacheTemplate templateFromContentsOfURL:templateURL error:error]; - - if (!template) { - return NO; - } - + if (!template) return NO; NSString *contents = [template renderObject:self error:error]; - - if (!contents) { - return NO; - } - + if (!contents) return NO; NSURL *destination = [self.destination URLByAppendingPathComponent:@"feed.xml"]; - - if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { - // Could not write feed.xml + if (![contents writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) return NO; - } - return YES; } @@ -261,174 +187,117 @@ - (BOOL)writeFeed:(NSError **)error { - (BOOL)verifySourceDirectory:(NSError **)error { BOOL sourceDirectoryIsDirectory = NO; BOOL sourceDirectoryExists = [[NSFileManager defaultManager] fileExistsAtPath:self.sourceDirectory.path isDirectory:&sourceDirectoryIsDirectory]; - - if (!sourceDirectoryIsDirectory || !sourceDirectoryExists) { - if (error) { - *error = TBError.missingSourceDirectory(self.sourceDirectory); - } - + if (!sourceDirectoryIsDirectory || !sourceDirectoryExists){ + if (error) *error = TBError.missingSourceDirectory(self.sourceDirectory); return NO; } - return YES; } - (BOOL)processSourceDirectory:(NSError **)error { - NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:self.sourceDirectory - includingPropertiesForKeys:nil - options:NSDirectoryEnumerationSkipsHiddenFiles - errorHandler:^BOOL(NSURL *url, NSError *enumeratorError) { + NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:self.sourceDirectory includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:^BOOL(NSURL *url, NSError *enumeratorError) { return YES; }]; - for (NSURL *URL in enumerator) { + BOOL URLIsDirectory = NO; - [[NSFileManager defaultManager] fileExistsAtPath:URL.path isDirectory:&URLIsDirectory]; - - if (URLIsDirectory) { - continue; - } + if (URLIsDirectory) continue; - if (![self processSourceFile:URL error:error]) { + if (![self processSourceFile:URL error:error]) return NO; - } + } - return YES; } - (BOOL)processSourceFile:(NSURL *)URL error:(NSError **)error { NSString *extension = [URL pathExtension]; NSString *relativePath = [URL.path stringByReplacingOccurrencesOfString:self.sourceDirectory.path withString:@""]; - NSURL *destinationURL = [[self.destination URLByAppendingPathComponent:relativePath] URLByStandardizingPath]; NSURL *destinationDirectory = [destinationURL URLByDeletingLastPathComponent]; - - if (![[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error]) { - // Unable to create directory structure + if (![[NSFileManager defaultManager] createDirectoryAtURL:destinationDirectory withIntermediateDirectories:YES attributes:nil error:error]) return NO; - } - [[NSFileManager defaultManager] removeItemAtURL:destinationURL error:nil]; if ([extension isEqualToString:@"mustache"]) { - TBPage *page = [[TBPage alloc] initWithURL:URL inSite:self error:nil]; - + TBPage *page = [TBPage pageWithURL:URL inSite:self error:nil]; NSURL *pageDestination = [[destinationURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"html"]; - - if (![self writePage:page toDestination:pageDestination error:error]) { + if (![self writePage:page toDestination:pageDestination error:error]) return NO; - } - } else { - // Not a mustache file, copy without processing + } + else [[NSFileManager defaultManager] copyItemAtURL:URL toURL:destinationURL error:error]; - } - return YES; } - (BOOL)writePage:(TBPage *)page toDestination:(NSURL *)destination error:(NSError **)error { - if (!page) { - return NO; - } - + if (!page) return NO; NSString *rawPageTemplate = [self.rawDefaultTemplate stringByReplacingOccurrencesOfString:@"{{{content}}}" withString:page.content]; - GRMustacheTemplate *pageTemplate = [GRMustacheTemplate templateFromString:rawPageTemplate error:error]; - - if (!pageTemplate) { - return NO; - } - + if (!pageTemplate) return NO; NSString *renderedPage = [pageTemplate renderObject:page error:error]; - - if (!renderedPage) { - return NO; - } - - if (![renderedPage writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) { + if (!renderedPage) return NO; + if (![renderedPage writeToURL:destination atomically:YES encoding:NSUTF8StringEncoding error:error]) return NO; - } - return YES; } #pragma mark - Filters - (NSString *)filteredContent:(NSString *)content fromFile:(NSURL *)file error:(NSError **)error { + NSArray *filterPaths = self.metadata[TBSiteFilters]; - - if (!filterPaths || ![filterPaths count]) { + if (!filterPaths || ![filterPaths count]) return content; - } NSURL *scriptsURL = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationScriptsDirectory inDomains:NSUserDomainMask][0]; NSArray *arguments = @[self.root.path, file.path]; for (NSString *filterPath in filterPaths) { + NSURL *filterURL = [scriptsURL URLByAppendingPathComponent:filterPath]; - NSUserUnixTask *filter = [[NSUserUnixTask alloc] initWithURL:filterURL error:error]; - - if (!filter) { - return content; - } + if (!filter) return content; NSPipe *standardError = [NSPipe pipe]; - NSPipe *standardInput = [NSPipe pipe]; - NSPipe *standardOutput = [NSPipe pipe]; - filter.standardError = standardError.fileHandleForWriting; + NSPipe *standardInput = [NSPipe pipe]; filter.standardInput = standardInput.fileHandleForReading; + NSPipe *standardOutput = [NSPipe pipe]; filter.standardOutput = standardOutput.fileHandleForWriting; - [standardInput.fileHandleForWriting writeData:[content dataUsingEncoding:NSUTF8StringEncoding]]; [standardInput.fileHandleForWriting closeFile]; __block NSError *blockError = nil; dispatch_group_t group = dispatch_group_create(); - dispatch_async(dispatch_get_current_queue(), ^{ dispatch_group_enter(group); - [filter executeWithArguments:arguments completionHandler:^(NSError *filterError) { blockError = filterError; - dispatch_group_leave(group); }]; }); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - if (blockError) { - if (error) { - *error = blockError; - } - + if (error) *error = blockError; return nil; } NSData *standardErrorData = [standardError.fileHandleForReading readDataToEndOfFile]; - if (standardErrorData.length > 0) { NSString *standardErrorContents = [NSString stringWithUTF8String:standardErrorData.bytes]; - - if (error) { - *error = TBError.filterStandardError(filterURL, standardErrorContents); - } - + if (error) *error = TBError.filterStandardError(filterURL, standardErrorContents); return nil; } - NSData *standardOutputData = [standardOutput.fileHandleForReading readDataToEndOfFile]; - - if (standardOutputData.length > 0) { + if (standardOutputData.length > 0) content = [[NSString alloc] initWithBytes:standardOutputData.bytes length:standardOutputData.length encoding:NSUTF8StringEncoding]; - } + } return content; + } #pragma mark - Site Modification @@ -439,14 +308,10 @@ - (void)addPost:(TBPost *)post { - (void)setMetadata:(NSDictionary *)metadata { _metadata = metadata; - NSURL *metadataURL = [self.root URLByAppendingPathComponent:@"Info.plist" isDirectory:NO]; - [self.metadata writeToURL:metadataURL atomically:NO]; - - if (self.delegate && [self.delegate respondsToSelector:@selector(metadataDidChangeForSite:)]) { + if (self.delegate && [self.delegate respondsToSelector:@selector(metadataDidChangeForSite:)]) [self.delegate metadataDidChangeForSite:self]; - } } @end From 0863920740fee36b04f32d4708ac98f2e732d462 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 5 Sep 2013 17:16:28 -0400 Subject: [PATCH 28/30] Convert init constructors to factory methods. --- Mac App/Controllers/TBSiteWindowController.m | 2 +- Shared/TBPost.h | 2 +- Shared/TBPost.m | 30 +++++++++++--------- Shared/TBPostMetadata.h | 2 +- Shared/TBPostMetadata.m | 28 +++++++++++------- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/Mac App/Controllers/TBSiteWindowController.m b/Mac App/Controllers/TBSiteWindowController.m index 147129a..d0507b0 100644 --- a/Mac App/Controllers/TBSiteWindowController.m +++ b/Mac App/Controllers/TBSiteWindowController.m @@ -89,7 +89,7 @@ - (IBAction)showAddPostSheet:(id)sender { [self.addPostSheetController runModalForWindow:[document windowForSheet] completionBlock:^(NSString *title, NSString *slug) { NSError *error = nil; - TBPost *post = [[TBPost alloc] initWithTitle:title slug:slug inSite:document.site error:&error]; + TBPost *post = [TBPost postWithTitle:title slug:slug inSite:document.site error:&error]; if (post) { [document.site addPost:post]; diff --git a/Shared/TBPost.h b/Shared/TBPost.h index c1d535e..e8efe93 100644 --- a/Shared/TBPost.h +++ b/Shared/TBPost.h @@ -59,7 +59,7 @@ @return A TBPost object, or nil if an error was encountered. */ -- (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error; ++ (instancetype)postWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error; /*! Parse the contents of the markdownContent property, saving the HTML output diff --git a/Shared/TBPost.m b/Shared/TBPost.m index e4a1fca..c819eaa 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -28,39 +28,39 @@ + (instancetype)postWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError ** post.slug = slug; - post.metadata = [[TBPostMetadata alloc] initWithPostDirectory:URL withError:error]; + post.metadata = [TBPostMetadata metadataWithPostDirectory:URL withError:error]; if (post.metadata) { return post; } - - return nil; } return nil; } -- (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error { - if (self = [super init]) { - self.site = site; ++ (instancetype)postWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error { + TBPost *post = [super init]; + + if (post) { + post.site = site; // Create the directory NSString *filename = [NSString stringWithString:slug]; - self.postDirectory = [site.postsDirectory URLByAppendingPathComponent:slug isDirectory:YES]; + post.postDirectory = [site.postsDirectory URLByAppendingPathComponent:slug isDirectory:YES]; - if (![[NSFileManager defaultManager] createDirectoryAtURL:self.postDirectory withIntermediateDirectories:YES attributes:nil error:error]) { + if (![[NSFileManager defaultManager] createDirectoryAtURL:post.postDirectory withIntermediateDirectories:YES attributes:nil error:error]) { // Unable to create directory structure return nil; } // Metadata File - self.metadata = [[TBPostMetadata alloc] initWithPostDirectory:self.postDirectory withError:error]; + post.metadata = [TBPostMetadata metadataWithPostDirectory:post.postDirectory withError:error]; - [self.metadata writeWithError:error]; + [post.metadata writeWithError:error]; // Post File - NSURL *contentDestination = [[self.postDirectory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; + NSURL *contentDestination = [[post.postDirectory URLByAppendingPathComponent:filename] URLByAppendingPathExtension:@"md"]; NSString *contents = [NSString stringWithFormat:@"# %@ #\n\n", title]; @@ -68,12 +68,14 @@ - (instancetype)initWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB return nil; } - self.URL = contentDestination; + post.URL = contentDestination; - [self parse:error]; + [post parse:error]; + + return post; } - return self; + return nil; } - (BOOL)parse:(NSError **)error { diff --git a/Shared/TBPostMetadata.h b/Shared/TBPostMetadata.h index 3f4acb7..f01d1f9 100644 --- a/Shared/TBPostMetadata.h +++ b/Shared/TBPostMetadata.h @@ -58,7 +58,7 @@ @return A TSPostMetadata object, or nil if an error was encountered. */ -- (instancetype)initWithPostDirectory:(NSURL *)directory withError:(NSError **)error; ++ (instancetype)metadataWithPostDirectory:(NSURL *)directory withError:(NSError **)error; /*! Extract the metadata data from the given directory. diff --git a/Shared/TBPostMetadata.m b/Shared/TBPostMetadata.m index 0a11b5e..33b61c7 100644 --- a/Shared/TBPostMetadata.m +++ b/Shared/TBPostMetadata.m @@ -26,24 +26,32 @@ - (instancetype)init { return self; } -- (instancetype)initWithPostDirectory:(NSURL *)directory withError:(NSError **)error { - if (self = [self init]) { - postDirectory = directory; ++ (instancetype)metadataWithPostDirectory:(NSURL *)directory withError:(NSError **)error { + TBPostMetadata *metadata = [TBPostMetadata new]; + + if (metadata) { + metadata.postDirectory = directory; + + metadata.path = [directory URLByAppendingPathComponent:METADATA_FILENAME]; - _path = [directory URLByAppendingPathComponent:METADATA_FILENAME]; + [metadata readWithError:error]; - [self readWithError:error]; + return metadata; } - return self; + return nil; } -- (instancetype)initWithDictionary:(NSDictionary *)dictionary { - if (self = [self init]) { - [self extractDataFromDictionary:dictionary]; ++ (instancetype)metadataWithDictionary:(NSDictionary *)dictionary { + TBPostMetadata *metadata = [TBPostMetadata new]; + + if (metadata) { + [metadata extractDataFromDictionary:dictionary]; + + return metadata; } - return self; + return nil; } - (void)extractDataFromDictionary:(NSDictionary *)dictionary { From 1ebce6866f764a4f61135b98246618790d5515c7 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 5 Sep 2013 17:24:42 -0400 Subject: [PATCH 29/30] Attempt to compact code by removing whitespace. --- Shared/TBPost.m | 6 ------ Shared/TBPostMetadata.m | 4 ---- 2 files changed, 10 deletions(-) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index c819eaa..4062681 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -18,16 +18,12 @@ @implementation TBPost + (instancetype)postWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError **)error { NSString *slug = [URL lastPathComponent]; - NSURL *postURL = [URL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.md", slug]]; - TBPost *post = [super pageWithURL:postURL inSite:site error:error]; if (post) { post.postDirectory = URL; - post.slug = slug; - post.metadata = [TBPostMetadata metadataWithPostDirectory:URL withError:error]; if (post.metadata) { @@ -56,7 +52,6 @@ + (instancetype)postWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB // Metadata File post.metadata = [TBPostMetadata metadataWithPostDirectory:post.postDirectory withError:error]; - [post.metadata writeWithError:error]; // Post File @@ -69,7 +64,6 @@ + (instancetype)postWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TB } post.URL = contentDestination; - [post parse:error]; return post; diff --git a/Shared/TBPostMetadata.m b/Shared/TBPostMetadata.m index 33b61c7..647d14c 100644 --- a/Shared/TBPostMetadata.m +++ b/Shared/TBPostMetadata.m @@ -19,7 +19,6 @@ @implementation TBPostMetadata - (instancetype)init { if (self = [super init]) { draft = YES; - publishedDate = nil; } @@ -31,7 +30,6 @@ + (instancetype)metadataWithPostDirectory:(NSURL *)directory withError:(NSError if (metadata) { metadata.postDirectory = directory; - metadata.path = [directory URLByAppendingPathComponent:METADATA_FILENAME]; [metadata readWithError:error]; @@ -70,7 +68,6 @@ - (BOOL)readWithError:(NSError **)error { } NSData *data = [[NSData alloc] initWithContentsOfURL:_path]; - NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:error]; if (data) { @@ -88,7 +85,6 @@ - (BOOL)writeWithError:(NSError **)error { } NSData *data = [NSJSONSerialization dataWithJSONObject:[self dictionary] options:0 error:error]; - NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; return [string writeToURL:_path atomically:YES encoding:NSUTF8StringEncoding error:error]; From 4264ec6d51d03579027b52ee5b88d273fcb37025 Mon Sep 17 00:00:00 2001 From: Tanner Smith Date: Thu, 5 Sep 2013 17:31:33 -0400 Subject: [PATCH 30/30] Don't call init when we don't have any memory alloc'd. Derp. --- Shared/TBPost.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/TBPost.m b/Shared/TBPost.m index 4062681..7be54a1 100644 --- a/Shared/TBPost.m +++ b/Shared/TBPost.m @@ -35,7 +35,7 @@ + (instancetype)postWithURL:(NSURL *)URL inSite:(TBSite *)site error:(NSError ** } + (instancetype)postWithTitle:(NSString *)title slug:(NSString *)slug inSite:(TBSite *)site error:(NSError **)error { - TBPost *post = [super init]; + TBPost *post = [super new]; if (post) { post.site = site;