Skip to content

Commit

Permalink
Merge pull request #470 from wilzbach/tour-language-dir
Browse files Browse the repository at this point in the history
Allow the tour to be used from the a language directory - fixes #461
  • Loading branch information
wilzbach committed Sep 1, 2016
2 parents aded431 + abeead5 commit 5160844
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 47 deletions.
29 changes: 22 additions & 7 deletions source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,35 @@ private void doSanityCheck(ContentProvider contentProvider, IExecProvider execPr
shared static this()
{
import std.file : thisExePath;
import std.path : buildPath, dirName, isAbsolute;
import std.path;

string rootDir = thisExePath.dirName;

string configFile = buildPath(rootDir, "config.yml");
readOption("c|config", &configFile, "Configuration file");
bool sanityCheck = false;
string customLangDirectory;
string defaultLang = "en";

readOption("c|config", &configFile, "Configuration file");
readOption("sanitycheck", &sanityCheck,
"Runs sanity check before starting that checks whether all source code examples actually compile; doesn't start the service");
"Runs sanity check before starting that checks whether all source code examples actually compile; doesn't start the service");
readOption("lang-dir|l", &customLangDirectory, "Language directory");

auto config = new Config(configFile);

string publicDir = config.publicDir.isAbsolute ? config.publicDir : rootDir.buildPath(config.publicDir);
string contentDir = publicDir.buildPath("content");

auto contentProvider = new ContentProvider(contentDir);
// If the user provides a custom language directory we only add this language to the Tour
if (!customLangDirectory.empty)
{
string langDir = customLangDirectory.absolutePath.buildNormalizedPath;
contentProvider.addLanguage(langDir);
defaultLang = langDir.baseName;
}
else
contentProvider.addLanguages(contentDir);

auto contentProvider = new ContentProvider(publicDir.buildPath("content"));
auto execProvider = createExecProvider(config, contentProvider);

if (sanityCheck) {
Expand All @@ -123,7 +138,7 @@ shared static this()
settings.useCompressionIfPossible = true;
settings.errorPageHandler = (HTTPServerRequest req, HTTPServerResponse res, HTTPServerErrorInfo error) {
auto title = "Page not found";
auto t = contentProvider.getTOC("en");
auto t = contentProvider.getTOC(defaultLang);
auto toc = &t;
auto googleAnalyticsId = config.googleAnalyticsId;
auto chapterId = "";
Expand All @@ -149,7 +164,7 @@ shared static this()
auto fsettings = new HTTPFileServerSettings;
fsettings.serverPathPrefix = "/static";
urlRouter
.registerWebInterface(new WebInterface(contentProvider, config.googleAnalyticsId))
.registerWebInterface(new WebInterface(contentProvider, config.googleAnalyticsId, defaultLang))
.registerRestInterface(new ApiV1(execProvider, contentProvider))
.get("/static/*", serveStaticFiles(publicDir.buildPath("static"), fsettings));

Expand Down
84 changes: 46 additions & 38 deletions source/contentprovider.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import std.string: split, strip;
import std.typecons: Tuple;
import std.exception: enforce;
import std.string: format;
import std.path: buildPath;
import std.path: baseName, buildPath;

import yaml;
import mustache;
Expand Down Expand Up @@ -77,6 +77,9 @@ class ContentProvider
LanguageMeta[string] language_;

auto mustacheContext_ = new Mustache.Context;

/// maps the loaded languages to their file path
string[string] langWithPath;
}

/// Create or update Content structure
Expand Down Expand Up @@ -105,58 +108,63 @@ class ContentProvider
// check for existence in file system
import std.array : split;
auto parts = link.split("/");
string fileName = buildPath(contentDirectory, language, parts[0], parts[1] ~ ".md");
enforce(language in langWithPath, "Language hasn't been seen before.");
string fileName = buildPath(langWithPath[language], parts[0], parts[1] ~ ".md");
import std.stdio;
return exists(fileName);
}

this(string contentDirectory)
{
this.contentDirectory = contentDirectory;

// parse mustache template file
setupMustacheMacros(readText(buildPath(contentDirectory, "template.yml")),
mustacheContext_);
foreach(string filename; dirEntries(contentDirectory, SpanMode.depth)) {
if (isDir(filename))
continue;
auto parts = filename[contentDirectory.length .. $]
.split('/').filter!(x => !x.empty)
.array;

// ignore any top level files
if (parts.length == 1) {
continue;
}
}

// search for language-specific root file
auto language = parts[0];
if (parts[1] != "index.yml")
continue;
auto root = Loader(filename).load();
public void addLanguages(string directory)
{
foreach(string filename; dirEntries(directory, SpanMode.shallow))
addLanguage(filename);
}

// language meta information
enforce("start" in root, "'start' point required in language-specific yaml");
enforce(isValidLink(root["start"].as!string, language), "The start page must be formatted as chapter/section");
public void addLanguage(string langDirectory)
{
if (!langDirectory.isDir)
return;

LanguageMeta langMeta;
langMeta.start = ChapterAndSection(root["start"].as!string);
auto configFile = langDirectory.buildPath("index.yml");
if (!configFile.exists)
logInfo("Missing index.yml for %s", langDirectory);

import std.meta : AliasSeq;
foreach (attr; AliasSeq!("title", "repo"))
{
enforce(attr in root, "'" ~ attr ~ "' point required in language-specific yaml");
mixin("langMeta." ~ attr ~ " = root[attr].as!string;");
}
language_[language] = langMeta;
auto language = langDirectory.baseName;
auto root = Loader(configFile).load();

auto i = 0;
foreach (string chapter; root["ordering"])
{
auto chapterDir = buildPath(filename[0 .. contentDirectory.length],
language, chapter);
chapter_[language][chapter] = ChapterMeta(i++, "");
addChapter(chapter, chapterDir, language);
}
// store the directory to be able to link to other requests
langWithPath[language] = langDirectory;

// language meta information
enforce("start" in root, "'start' point required in language-specific yaml");
enforce(isValidLink(root["start"].as!string, language), "The start page must be formatted as chapter/section");

LanguageMeta langMeta;
langMeta.start = ChapterAndSection(root["start"].as!string);

import std.meta : AliasSeq;
foreach (attr; AliasSeq!("title", "repo"))
{
enforce(attr in root, "'" ~ attr ~ "' point required in language-specific yaml");
mixin("langMeta." ~ attr ~ " = root[attr].as!string;");
}
language_[language] = langMeta;

auto i = 0;
foreach (string chapter; root["ordering"])
{
auto chapterDir = buildPath(langDirectory, chapter);
chapter_[language][chapter] = ChapterMeta(i++, "");
addChapter(chapter, chapterDir, language);
}
}

Expand Down
7 changes: 5 additions & 2 deletions source/webinterface.d
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ class WebInterface
LinkCache[string][string][string] sectionLinkCache_;
///< language, chapter and section indexing
string googleAnalyticsId_; ///< ID for google analytics
string defaultLang = "en";
}

this(ContentProvider contentProvider,
string googleAnalyticsId)
string googleAnalyticsId, string defaultLang)
{
this.contentProvider_ = contentProvider;
this.googleAnalyticsId_ = googleAnalyticsId;
this.defaultLang = defaultLang;

// Fetch all table-of-contents for all supported
// languages (just 'en' for now) and generate
// the previous/next link cache for each tour page.
Expand Down Expand Up @@ -104,7 +107,7 @@ class WebInterface

void index(HTTPServerRequest req, HTTPServerResponse res)
{
getStart(req, res, "en");
getStart(req, res, defaultLang);
}

@path("/tour/:language")
Expand Down

0 comments on commit 5160844

Please sign in to comment.