Huge repo reorganization

In no particular order...
- Created projects using F# generator, using Paket and FAKE
- Split "entities" into their own project, and created interface for
data functions required on those entities
- Renamed "data" project and used it as an implementation of data access
- Created "logic" layer that takes the data interface, and does the
non-persistence-related manipulation of items
- Moved "web" project to "app", and modified Nancy modules to utilize
Logic project and data interface instead of Data project and RethinkDB
connection
- Created test placeholder project; will be filling that out shortly
(TAD?)
This commit is contained in:
Daniel J. Summers 2016-08-06 13:55:49 -05:00
parent 8194649018
commit 710004dfc4
53 changed files with 1418 additions and 572 deletions

4
.gitignore vendored
View File

@ -21,6 +21,8 @@ bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Bb]uild/
[Dd]eploy/
# Visual Studio 2015 cache/options directory
.vs/
@ -241,7 +243,7 @@ FakesAssemblies/
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
**/.paket/paket.exe
paket-files/
# FAKE - F# Make

Binary file not shown.

View File

@ -1,12 +1,13 @@
namespace MyWebLog
open MyWebLog.Data.WebLog
open MyWebLog.Data
open MyWebLog.Entities
open MyWebLog.Logic.WebLog
open Nancy
open RethinkDb.Driver.Net
/// Handle /admin routes
type AdminModule(conn : IConnection) as this =
type AdminModule(data : IMyWebLogData) as this =
inherit NancyModule("/admin")
do
@ -15,6 +16,6 @@ type AdminModule(conn : IConnection) as this =
/// Admin dashboard
member this.Dashboard () =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let model = DashboardModel(this.Context, this.WebLog, findDashboardCounts conn this.WebLog.Id)
model.PageTitle <- Resources.Dashboard
let model = DashboardModel(this.Context, this.WebLog, findDashboardCounts data this.WebLog.Id)
model.PageTitle <- Resources.Dashboard
upcast this.View.["admin/dashboard", model]

View File

@ -2,9 +2,9 @@
open MyWebLog
open MyWebLog.Data
open MyWebLog.Data.SetUp
open MyWebLog.Data.WebLog
open MyWebLog.Data.RethinkDB
open MyWebLog.Entities
open MyWebLog.Logic.WebLog
open Nancy
open Nancy.Authentication.Forms
open Nancy.Bootstrapper
@ -13,6 +13,7 @@ open Nancy.Cryptography
open Nancy.Owin
open Nancy.Security
open Nancy.Session.Persistable
//open Nancy.Session.Relational
open Nancy.Session.RethinkDb
open Nancy.TinyIoc
open Nancy.ViewEngines.SuperSimpleViewEngine
@ -21,6 +22,7 @@ open RethinkDb.Driver.Net
open Suave
open Suave.Owin
open System
open System.Configuration
open System.IO
open System.Text.RegularExpressions
@ -28,9 +30,11 @@ open System.Text.RegularExpressions
let cfg = try AppConfig.FromJson (System.IO.File.ReadAllText "config.json")
with ex -> raise <| ApplicationException(Resources.ErrBadAppConfig, ex)
let data : IMyWebLogData = upcast RethinkMyWebLogData(cfg.DataConfig.Conn, cfg.DataConfig)
do
startUpCheck cfg.DataConfig
data.SetUp ()
/// Support RESX lookup via the @Translate SSVE alias
type TranslateTokenViewEngineMatcher() =
static let regex = Regex("@Translate\.(?<TranslationKey>[a-zA-Z0-9-_]+);?", RegexOptions.Compiled)
@ -83,10 +87,10 @@ type MyWebLogBootstrapper() =
override this.ApplicationStartup (container, pipelines) =
base.ApplicationStartup (container, pipelines)
// Data configuration (both config and the connection; Nancy modules just need the connection)
// Application configuration
container.Register<AppConfig>(cfg)
|> ignore
container.Register<IConnection>(cfg.DataConfig.Conn)
container.Register<IMyWebLogData>(data)
|> ignore
// NodaTime
container.Register<IClock>(SystemClock.Instance)
@ -110,6 +114,7 @@ type MyWebLogBootstrapper() =
// Sessions
let sessions = RethinkDbSessionConfiguration(cfg.DataConfig.Conn)
sessions.Database <- cfg.DataConfig.Database
//let sessions = RelationalSessionConfiguration(ConfigurationManager.ConnectionStrings.["SessionStore"].ConnectionString)
PersistableSessions.Enable (pipelines, sessions)
()
@ -127,7 +132,7 @@ type RequestEnvironment() =
member this.Initialize (pipelines, context) =
let establishEnv (ctx : NancyContext) =
ctx.Items.[Keys.RequestStart] <- DateTime.Now.Ticks
match tryFindWebLogByUrlBase cfg.DataConfig.Conn ctx.Request.Url.HostName with
match tryFindWebLogByUrlBase data ctx.Request.Url.HostName with
| Some webLog -> ctx.Items.[Keys.WebLog] <- webLog
| None -> // TODO: redirect to domain set up page
ApplicationException (sprintf "%s %s" ctx.Request.Url.HostName Resources.ErrNotConfigured)

View File

@ -1,6 +1,6 @@
namespace MyWebLog
open MyWebLog.Data
open MyWebLog.Data.RethinkDB
open Newtonsoft.Json
open System.Text

View File

@ -1,6 +1,7 @@
namespace MyWebLog
open MyWebLog.Data.Category
open MyWebLog.Data
open MyWebLog.Logic.Category
open MyWebLog.Entities
open Nancy
open Nancy.ModelBinding
@ -8,7 +9,7 @@ open Nancy.Security
open RethinkDb.Driver.Net
/// Handle /category and /categories URLs
type CategoryModule(conn : IConnection) as this =
type CategoryModule(data : IMyWebLogData) as this =
inherit NancyModule()
do
@ -21,7 +22,7 @@ type CategoryModule(conn : IConnection) as this =
member this.CategoryList () =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let model = CategoryListModel(this.Context, this.WebLog,
(getAllCategories conn this.WebLog.Id
(findAllCategories data this.WebLog.Id
|> List.map (fun cat -> IndentedCategory.Create cat (fun _ -> false))))
upcast this.View.["/admin/category/list", model]
@ -31,13 +32,13 @@ type CategoryModule(conn : IConnection) as this =
let catId = parameters.["id"].ToString ()
match (match catId with
| "new" -> Some Category.Empty
| _ -> tryFindCategory conn this.WebLog.Id catId) with
| _ -> tryFindCategory data this.WebLog.Id catId) with
| Some cat -> let model = CategoryEditModel(this.Context, this.WebLog, cat)
model.Categories <- getAllCategories conn this.WebLog.Id
model.Categories <- findAllCategories data this.WebLog.Id
|> List.map (fun cat -> IndentedCategory.Create cat
(fun c -> c = defaultArg (fst cat).ParentId ""))
upcast this.View.["admin/category/edit", model]
| None -> this.NotFound ()
| _ -> this.NotFound ()
/// Save a category
member this.SaveCategory (parameters : DynamicDictionary) =
@ -45,41 +46,43 @@ type CategoryModule(conn : IConnection) as this =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let catId = parameters.["id"].ToString ()
let form = this.Bind<CategoryForm> ()
let oldCat = match catId with "new" -> Some Category.Empty | _ -> tryFindCategory conn this.WebLog.Id catId
let oldCat = match catId with
| "new" -> Some { Category.Empty with WebLogId = this.WebLog.Id }
| _ -> tryFindCategory data this.WebLog.Id catId
match oldCat with
| Some old -> let cat = { old with Name = form.Name
Slug = form.Slug
Description = match form.Description with "" -> None | d -> Some d
ParentId = match form.ParentId with "" -> None | p -> Some p }
let newCatId = saveCategory conn this.WebLog.Id cat
let newCatId = saveCategory data cat
match old.ParentId = cat.ParentId with
| true -> ()
| _ -> match old.ParentId with
| Some parentId -> removeCategoryFromParent conn this.WebLog.Id parentId newCatId
| None -> ()
| Some parentId -> removeCategoryFromParent data this.WebLog.Id parentId newCatId
| _ -> ()
match cat.ParentId with
| Some parentId -> addCategoryToParent conn this.WebLog.Id parentId newCatId
| None -> ()
| Some parentId -> addCategoryToParent data this.WebLog.Id parentId newCatId
| _ -> ()
let model = MyWebLogModel(this.Context, this.WebLog)
{ UserMessage.Empty with
Level = Level.Info
Message = System.String.Format
(Resources.MsgCategoryEditSuccess,
(match catId with | "new" -> Resources.Added | _ -> Resources.Updated)) }
(match catId with "new" -> Resources.Added | _ -> Resources.Updated)) }
|> model.AddMessage
this.Redirect (sprintf "/category/%s/edit" newCatId) model
| None -> this.NotFound ()
| _ -> this.NotFound ()
/// Delete a category
member this.DeleteCategory (parameters : DynamicDictionary) =
this.ValidateCsrfToken ()
this.RequiresAccessLevel AuthorizationLevel.Administrator
let catId = parameters.["id"].ToString ()
match tryFindCategory conn this.WebLog.Id catId with
| Some cat -> deleteCategory conn cat
match tryFindCategory data this.WebLog.Id catId with
| Some cat -> deleteCategory data cat
let model = MyWebLogModel(this.Context, this.WebLog)
{ UserMessage.Empty with Level = Level.Info
Message = System.String.Format(Resources.MsgCategoryDeleted, cat.Name) }
|> model.AddMessage
this.Redirect "/categories" model
| None -> this.NotFound ()
| _ -> this.NotFound ()

View File

@ -0,0 +1,302 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>9cea3a8b-e8aa-44e6-9f5f-2095ceed54eb</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>MyWebLog.App</RootNamespace>
<AssemblyName>MyWebLog.App</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
<Name>MyWebLog.App</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\MyWebLog.App.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\MyWebLog.App.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
<Reference Include="System.ServiceModel" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Keys.fs" />
<Compile Include="AppConfig.fs" />
<Compile Include="ViewModels.fs" />
<Compile Include="ModuleExtensions.fs" />
<Compile Include="AdminModule.fs" />
<Compile Include="CategoryModule.fs" />
<Compile Include="PageModule.fs" />
<Compile Include="PostModule.fs" />
<Compile Include="UserModule.fs" />
<Compile Include="App.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyWebLog.Data.RethinkDB\MyWebLog.Data.RethinkDB.fsproj">
<Name>MyWebLog.Data.RethinkDB</Name>
<Project>{d6c2be5e-883a-4f34-9905-b730543ca380}</Project>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\MyWebLog.Entities\MyWebLog.Entities.fsproj">
<Name>MyWebLog.Entities</Name>
<Project>{a87f3cf5-2189-442b-8acf-929f5153ac22}</Project>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\MyWebLog.Logic\MyWebLog.Logic.fsproj">
<Name>MyWebLog.Logic</Name>
<Project>{29f6eda3-4f43-4bb3-9c63-ae238a9b7f12}</Project>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\MyWebLog.Resources\MyWebLog.Resources.csproj">
<Name>MyWebLog.Resources</Name>
<Project>{a12ea8da-88bc-4447-90cb-a0e2dcc37523}</Project>
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '11.0'">
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Common.Logging">
<HintPath>..\packages\Common.Logging\lib\net40\Common.Logging.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Common.Logging.Core">
<HintPath>..\packages\Common.Logging.Core\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.0'">
<ItemGroup>
<Reference Include="FSharp.Compiler.Service">
<HintPath>..\packages\FSharp.Compiler.Service\lib\net40\FSharp.Compiler.Service.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="FSharp.Compiler.Service">
<HintPath>..\packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="CSharpFormat">
<HintPath>..\packages\FSharp.Formatting\lib\net40\CSharpFormat.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="FSharp.CodeFormat">
<HintPath>..\packages\FSharp.Formatting\lib\net40\FSharp.CodeFormat.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="FSharp.Formatting.Common">
<HintPath>..\packages\FSharp.Formatting\lib\net40\FSharp.Formatting.Common.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="FSharp.Literate">
<HintPath>..\packages\FSharp.Formatting\lib\net40\FSharp.Literate.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="FSharp.Markdown">
<HintPath>..\packages\FSharp.Formatting\lib\net40\FSharp.Markdown.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="FSharp.MetadataFormat">
<HintPath>..\packages\FSharp.Formatting\lib\net40\FSharp.MetadataFormat.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="RazorEngine">
<HintPath>..\packages\FSharp.Formatting\lib\net40\RazorEngine.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="System.Web.Razor">
<HintPath>..\packages\FSharp.Formatting\lib\net40\System.Web.Razor.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="FSharpVSPowerTools.Core">
<HintPath>..\packages\FSharpVSPowerTools.Core\lib\net45\FSharpVSPowerTools.Core.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Nancy">
<HintPath>..\packages\Nancy\lib\net40\Nancy.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Nancy.Authentication.Forms">
<HintPath>..\packages\Nancy.Authentication.Forms\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.5.2'">
<ItemGroup>
<Reference Include="Nancy.Session.Persistable">
<HintPath>..\packages\Nancy.Session.Persistable\lib\net452\Nancy.Session.Persistable.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.5.2'">
<ItemGroup>
<Reference Include="Nancy.Session.RethinkDb">
<HintPath>..\packages\Nancy.Session.RethinkDB\lib\net452\Nancy.Session.RethinkDb.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.0'">
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="NodaTime">
<HintPath>..\packages\NodaTime\lib\net35-Client\NodaTime.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
<Reference Include="System.Xml">
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="RethinkDb.Driver">
<HintPath>..\packages\RethinkDb.Driver\lib\net45\RethinkDb.Driver.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Suave">
<HintPath>..\packages\Suave\lib\net40\Suave.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
</Project>

View File

@ -1,8 +1,9 @@
namespace MyWebLog
open FSharp.Markdown
open MyWebLog.Data.Page
open MyWebLog.Data
open MyWebLog.Entities
open MyWebLog.Logic.Page
open Nancy
open Nancy.ModelBinding
open Nancy.Security
@ -10,7 +11,7 @@ open NodaTime
open RethinkDb.Driver.Net
/// Handle /pages and /page URLs
type PageModule(conn : IConnection, clock : IClock) as this =
type PageModule(data : IMyWebLogData, clock : IClock) as this =
inherit NancyModule()
do
@ -22,7 +23,7 @@ type PageModule(conn : IConnection, clock : IClock) as this =
/// List all pages
member this.PageList () =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let model = PagesModel(this.Context, this.WebLog, (findAllPages conn this.WebLog.Id
let model = PagesModel(this.Context, this.WebLog, (findAllPages data this.WebLog.Id
|> List.map (fun p -> PageForDisplay(this.WebLog, p))))
model.PageTitle <- Resources.Pages
upcast this.View.["admin/page/list", model]
@ -33,16 +34,16 @@ type PageModule(conn : IConnection, clock : IClock) as this =
let pageId = parameters.["id"].ToString ()
match (match pageId with
| "new" -> Some Page.Empty
| _ -> tryFindPage conn this.WebLog.Id pageId) with
| _ -> tryFindPage data this.WebLog.Id pageId) with
| Some page -> let rev = match page.Revisions
|> List.sortByDescending (fun r -> r.AsOf)
|> List.tryHead with
| Some r -> r
| None -> Revision.Empty
| _ -> Revision.Empty
let model = EditPageModel(this.Context, this.WebLog, page, rev)
model.PageTitle <- match pageId with "new" -> Resources.AddNewPage | _ -> Resources.EditPage
upcast this.View.["admin/page/edit", model]
| None -> this.NotFound ()
| _ -> this.NotFound ()
/// Save a page
member this.SavePage (parameters : DynamicDictionary) =
@ -51,7 +52,7 @@ type PageModule(conn : IConnection, clock : IClock) as this =
let pageId = parameters.["id"].ToString ()
let form = this.Bind<EditPageForm> ()
let now = clock.Now.Ticks
match (match pageId with "new" -> Some Page.Empty | _ -> tryFindPage conn this.WebLog.Id pageId) with
match (match pageId with "new" -> Some Page.Empty | _ -> tryFindPage data this.WebLog.Id pageId) with
| Some p -> let page = match pageId with "new" -> { p with WebLogId = this.WebLog.Id } | _ -> p
let pId = { p with
Title = form.Title
@ -64,7 +65,7 @@ type PageModule(conn : IConnection, clock : IClock) as this =
Revisions = { AsOf = now
SourceType = form.Source
Text = form.Text } :: page.Revisions }
|> savePage conn
|> savePage data
let model = MyWebLogModel(this.Context, this.WebLog)
{ UserMessage.Empty with
Level = Level.Info
@ -73,18 +74,18 @@ type PageModule(conn : IConnection, clock : IClock) as this =
(match pageId with "new" -> Resources.Added | _ -> Resources.Updated)) }
|> model.AddMessage
this.Redirect (sprintf "/page/%s/edit" pId) model
| None -> this.NotFound ()
| _ -> this.NotFound ()
/// Delete a page
member this.DeletePage (parameters : DynamicDictionary) =
this.ValidateCsrfToken ()
this.RequiresAccessLevel AuthorizationLevel.Administrator
let pageId = parameters.["id"].ToString ()
match tryFindPageWithoutRevisions conn this.WebLog.Id pageId with
| Some page -> deletePage conn page.WebLogId page.Id
match tryFindPageWithoutRevisions data this.WebLog.Id pageId with
| Some page -> deletePage data page.WebLogId page.Id
let model = MyWebLogModel(this.Context, this.WebLog)
{ UserMessage.Empty with Level = Level.Info
Message = Resources.MsgPageDeleted }
|> model.AddMessage
this.Redirect "/pages" model
| None -> this.NotFound ()
| _ -> this.NotFound ()

View File

@ -1,10 +1,11 @@
namespace MyWebLog
open FSharp.Markdown
open MyWebLog.Data.Category
open MyWebLog.Data.Page
open MyWebLog.Data.Post
open MyWebLog.Data
open MyWebLog.Entities
open MyWebLog.Logic.Category
open MyWebLog.Logic.Page
open MyWebLog.Logic.Post
open Nancy
open Nancy.ModelBinding
open Nancy.Security
@ -15,7 +16,7 @@ open System
open System.ServiceModel.Syndication
/// Routes dealing with posts (including the home page, /tag, /category, RSS, and catch-all routes)
type PostModule(conn : IConnection, clock : IClock) as this =
type PostModule(data : IMyWebLogData, clock : IClock) as this =
inherit NancyModule()
/// Get the page number from the dictionary
@ -27,14 +28,14 @@ type PostModule(conn : IConnection, clock : IClock) as this =
/// Generate an RSS/Atom feed of the latest posts
let generateFeed format : obj =
let posts = findFeedPosts conn this.WebLog.Id 10
let posts = findFeedPosts data this.WebLog.Id 10
let feed =
SyndicationFeed(
this.WebLog.Name, defaultArg this.WebLog.Subtitle null,
Uri(sprintf "%s://%s" this.Request.Url.Scheme this.WebLog.UrlBase), null,
(match posts |> List.tryHead with
| Some (post, _) -> Instant(post.UpdatedOn).ToDateTimeOffset ()
| _ -> System.DateTimeOffset(System.DateTime.MinValue)),
| _ -> System.DateTimeOffset(System.DateTime.MinValue)),
posts
|> List.map (fun (post, user) ->
let item =
@ -76,15 +77,15 @@ type PostModule(conn : IConnection, clock : IClock) as this =
member this.PublishedPostsPage pageNbr =
let model = PostsModel(this.Context, this.WebLog)
model.PageNbr <- pageNbr
model.Posts <- findPageOfPublishedPosts conn this.WebLog.Id pageNbr 10 |> forDisplay
model.Posts <- findPageOfPublishedPosts data this.WebLog.Id pageNbr 10 |> forDisplay
model.HasNewer <- match pageNbr with
| 1 -> false
| _ -> match List.isEmpty model.Posts with
| true -> false
| _ -> Option.isSome <| tryFindNewerPost conn (List.last model.Posts).Post
| _ -> Option.isSome <| tryFindNewerPost data (List.last model.Posts).Post
model.HasOlder <- match List.isEmpty model.Posts with
| true -> false
| _ -> Option.isSome <| tryFindOlderPost conn (List.head model.Posts).Post
| _ -> Option.isSome <| tryFindOlderPost data (List.head model.Posts).Post
model.UrlPrefix <- "/posts"
model.PageTitle <- match pageNbr with 1 -> "" | _ -> sprintf "%s%i" Resources.PageHash pageNbr
this.ThemedView "index" model
@ -93,59 +94,59 @@ type PostModule(conn : IConnection, clock : IClock) as this =
member this.HomePage () =
match this.WebLog.DefaultPage with
| "posts" -> this.PublishedPostsPage 1
| pageId -> match tryFindPageWithoutRevisions conn this.WebLog.Id pageId with
| pageId -> match tryFindPageWithoutRevisions data this.WebLog.Id pageId with
| Some page -> let model = PageModel(this.Context, this.WebLog, page)
model.PageTitle <- page.Title
this.ThemedView "page" model
| None -> this.NotFound ()
| _ -> this.NotFound ()
/// Derive a post or page from the URL, or redirect from a prior URL to the current one
member this.CatchAll (parameters : DynamicDictionary) =
let url = parameters.["permalink"].ToString ()
match tryFindPostByPermalink conn this.WebLog.Id url with
match tryFindPostByPermalink data this.WebLog.Id url with
| Some post -> // Hopefully the most common result; the permalink is a permalink!
let model = PostModel(this.Context, this.WebLog, post)
model.NewerPost <- tryFindNewerPost conn post
model.OlderPost <- tryFindOlderPost conn post
model.NewerPost <- tryFindNewerPost data post
model.OlderPost <- tryFindOlderPost data post
model.PageTitle <- post.Title
this.ThemedView "single" model
| None -> // Maybe it's a page permalink instead...
match tryFindPageByPermalink conn this.WebLog.Id url with
| Some page -> // ...and it is!
let model = PageModel(this.Context, this.WebLog, page)
model.PageTitle <- page.Title
this.ThemedView "page" model
| None -> // Maybe it's an old permalink for a post
match tryFindPostByPriorPermalink conn this.WebLog.Id url with
| Some post -> // Redirect them to the proper permalink
upcast this.Response.AsRedirect(sprintf "/%s" post.Permalink)
.WithStatusCode HttpStatusCode.MovedPermanently
| None -> this.NotFound ()
| _ -> // Maybe it's a page permalink instead...
match tryFindPageByPermalink data this.WebLog.Id url with
| Some page -> // ...and it is!
let model = PageModel(this.Context, this.WebLog, page)
model.PageTitle <- page.Title
this.ThemedView "page" model
| _ -> // Maybe it's an old permalink for a post
match tryFindPostByPriorPermalink data this.WebLog.Id url with
| Some post -> // Redirect them to the proper permalink
upcast this.Response.AsRedirect(sprintf "/%s" post.Permalink)
.WithStatusCode HttpStatusCode.MovedPermanently
| _ -> this.NotFound ()
/// Display categorized posts
member this.CategorizedPosts (parameters : DynamicDictionary) =
let slug = parameters.["slug"].ToString ()
match tryFindCategoryBySlug conn this.WebLog.Id slug with
match tryFindCategoryBySlug data this.WebLog.Id slug with
| Some cat -> let pageNbr = getPage parameters
let model = PostsModel(this.Context, this.WebLog)
model.PageNbr <- pageNbr
model.Posts <- findPageOfCategorizedPosts conn this.WebLog.Id cat.Id pageNbr 10 |> forDisplay
model.Posts <- findPageOfCategorizedPosts data this.WebLog.Id cat.Id pageNbr 10 |> forDisplay
model.HasNewer <- match List.isEmpty model.Posts with
| true -> false
| _ -> Option.isSome <| tryFindNewerCategorizedPost conn cat.Id
| _ -> Option.isSome <| tryFindNewerCategorizedPost data cat.Id
(List.head model.Posts).Post
model.HasOlder <- match List.isEmpty model.Posts with
| true -> false
| _ -> Option.isSome <| tryFindOlderCategorizedPost conn cat.Id
| _ -> Option.isSome <| tryFindOlderCategorizedPost data cat.Id
(List.last model.Posts).Post
model.UrlPrefix <- sprintf "/category/%s" slug
model.PageTitle <- sprintf "\"%s\" Category%s" cat.Name
(match pageNbr with | 1 -> "" | n -> sprintf " | Page %i" n)
model.Subtitle <- Some <| match cat.Description with
| Some desc -> desc
| None -> sprintf "Posts in the \"%s\" category" cat.Name
| _ -> sprintf "Posts in the \"%s\" category" cat.Name
this.ThemedView "index" model
| None -> this.NotFound ()
| _ -> this.NotFound ()
/// Display tagged posts
member this.TaggedPosts (parameters : DynamicDictionary) =
@ -153,13 +154,13 @@ type PostModule(conn : IConnection, clock : IClock) as this =
let pageNbr = getPage parameters
let model = PostsModel(this.Context, this.WebLog)
model.PageNbr <- pageNbr
model.Posts <- findPageOfTaggedPosts conn this.WebLog.Id tag pageNbr 10 |> forDisplay
model.Posts <- findPageOfTaggedPosts data this.WebLog.Id tag pageNbr 10 |> forDisplay
model.HasNewer <- match List.isEmpty model.Posts with
| true -> false
| _ -> Option.isSome <| tryFindNewerTaggedPost conn tag (List.head model.Posts).Post
| _ -> Option.isSome <| tryFindNewerTaggedPost data tag (List.head model.Posts).Post
model.HasOlder <- match List.isEmpty model.Posts with
| true -> false
| _ -> Option.isSome <| tryFindOlderTaggedPost conn tag (List.last model.Posts).Post
| _ -> Option.isSome <| tryFindOlderTaggedPost data tag (List.last model.Posts).Post
model.UrlPrefix <- sprintf "/tag/%s" tag
model.PageTitle <- sprintf "\"%s\" Tag%s" tag (match pageNbr with 1 -> "" | n -> sprintf " | Page %i" n)
model.Subtitle <- Some <| sprintf "Posts tagged \"%s\"" tag
@ -182,7 +183,7 @@ type PostModule(conn : IConnection, clock : IClock) as this =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let model = PostsModel(this.Context, this.WebLog)
model.PageNbr <- pageNbr
model.Posts <- findPageOfAllPosts conn this.WebLog.Id pageNbr 25 |> forDisplay
model.Posts <- findPageOfAllPosts data this.WebLog.Id pageNbr 25 |> forDisplay
model.HasNewer <- pageNbr > 1
model.HasOlder <- List.length model.Posts > 24
model.UrlPrefix <- "/posts/list"
@ -193,21 +194,21 @@ type PostModule(conn : IConnection, clock : IClock) as this =
member this.EditPost (parameters : DynamicDictionary) =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let postId = parameters.["postId"].ToString ()
match (match postId with "new" -> Some Post.Empty | _ -> tryFindPost conn this.WebLog.Id postId) with
match (match postId with "new" -> Some Post.Empty | _ -> tryFindPost data this.WebLog.Id postId) with
| Some post -> let rev = match post.Revisions
|> List.sortByDescending (fun r -> r.AsOf)
|> List.tryHead with
| Some r -> r
| None -> Revision.Empty
let model = EditPostModel(this.Context, this.WebLog, post, rev)
model.Categories <- getAllCategories conn this.WebLog.Id
model.Categories <- findAllCategories data this.WebLog.Id
|> List.map (fun cat -> string (fst cat).Id,
sprintf "%s%s"
(String.replicate (snd cat) " &nbsp; &nbsp; ")
(fst cat).Name)
model.PageTitle <- match post.Id with "new" -> Resources.AddNewPost | _ -> Resources.EditPost
upcast this.View.["admin/post/edit"]
| None -> this.NotFound ()
| _ -> this.NotFound ()
/// Save a post
member this.SavePost (parameters : DynamicDictionary) =
@ -216,7 +217,7 @@ type PostModule(conn : IConnection, clock : IClock) as this =
let postId = parameters.["postId"].ToString ()
let form = this.Bind<EditPostForm>()
let now = clock.Now.Ticks
match (match postId with "new" -> Some Post.Empty | _ -> tryFindPost conn this.WebLog.Id postId) with
match (match postId with "new" -> Some Post.Empty | _ -> tryFindPost data this.WebLog.Id postId) with
| Some p -> let justPublished = p.PublishedOn = int64 0 && form.PublishNow
let post = match postId with
| "new" -> { p with
@ -242,14 +243,14 @@ type PostModule(conn : IConnection, clock : IClock) as this =
Revisions = { AsOf = now
SourceType = form.Source
Text = form.Text } :: post.Revisions }
|> savePost conn
|> savePost data
let model = MyWebLogModel(this.Context, this.WebLog)
{ UserMessage.Empty with
Level = Level.Info
Message = System.String.Format
(Resources.MsgPostEditSuccess,
(match postId with | "new" -> Resources.Added | _ -> Resources.Updated),
(match justPublished with | true -> Resources.AndPublished | _ -> "")) }
(match postId with "new" -> Resources.Added | _ -> Resources.Updated),
(match justPublished with true -> Resources.AndPublished | _ -> "")) }
|> model.AddMessage
this.Redirect (sprintf "/post/%s/edit" pId) model
| None -> this.NotFound ()
| _ -> this.NotFound ()

View File

@ -1,7 +1,8 @@
namespace MyWebLog
open MyWebLog.Data.User
open MyWebLog.Data
open MyWebLog.Entities
open MyWebLog.Logic.User
open Nancy
open Nancy.Authentication.Forms
open Nancy.Cryptography
@ -12,7 +13,7 @@ open RethinkDb.Driver.Net
open System.Text
/// Handle /user URLs
type UserModule(conn : IConnection, cfg : AppConfig) as this =
type UserModule(data : IMyWebLogData, cfg : AppConfig) as this =
inherit NancyModule("/user")
/// Hash the user's password
@ -37,7 +38,7 @@ type UserModule(conn : IConnection, cfg : AppConfig) as this =
this.ValidateCsrfToken ()
let form = this.Bind<LogOnForm> ()
let model = MyWebLogModel(this.Context, this.WebLog)
match tryUserLogOn conn form.Email (pbkdf2 form.Password) with
match tryUserLogOn data form.Email (pbkdf2 form.Password) with
| Some user -> this.Session.[Keys.User] <- user
{ UserMessage.Empty with Level = Level.Info
Message = Resources.MsgLogOnSuccess }
@ -46,10 +47,10 @@ type UserModule(conn : IConnection, cfg : AppConfig) as this =
// TODO: investigate if addMessage should update the session when it's called
upcast this.LoginAndRedirect (System.Guid.Parse user.Id,
fallbackRedirectUrl = defaultArg (Option.ofObj form.ReturnUrl) "/")
| None -> { UserMessage.Empty with Level = Level.Error
Message = Resources.ErrBadLogOnAttempt }
|> model.AddMessage
this.Redirect (sprintf "/user/logon?returnUrl=%s" form.ReturnUrl) model
| _ -> { UserMessage.Empty with Level = Level.Error
Message = Resources.ErrBadLogOnAttempt }
|> model.AddMessage
this.Redirect (sprintf "/user/logon?returnUrl=%s" form.ReturnUrl) model
/// Log a user off
member this.LogOff () =

View File

@ -1,7 +1,7 @@
namespace MyWebLog
open MyWebLog.Data.WebLog
open MyWebLog.Entities
open MyWebLog.Logic.WebLog
open Nancy
open Nancy.Session.Persistable
open Newtonsoft.Json

View File

@ -0,0 +1,7 @@
FSharp.Formatting
Nancy
Nancy.Authentication.Forms
Nancy.Session.RethinkDB
NodaTime
RethinkDb.Driver
Suave

View File

@ -4,17 +4,17 @@ open System.Reflection
open System.Runtime.CompilerServices
open System.Runtime.InteropServices
[<assembly: AssemblyTitle("MyWebLog.Data")>]
[<assembly: AssemblyDescription("Data access for myWebLog")>]
[<assembly: AssemblyTitle("MyWebLog.Data.RethinkDB")>]
[<assembly: AssemblyDescription("RethinkDB data access for myWebLog")>]
[<assembly: AssemblyConfiguration("")>]
[<assembly: AssemblyCompany("DJS Consulting")>]
[<assembly: AssemblyProduct("MyWebLog.Data")>]
[<assembly: AssemblyProduct("MyWebLog.Data.RethinkDB")>]
[<assembly: AssemblyCopyright("Copyright © 2016")>]
[<assembly: AssemblyTrademark("")>]
[<assembly: AssemblyCulture("")>]
[<assembly: ComVisible(false)>]
[<assembly: Guid("1fba0b84-b09e-4b16-b9b6-5730dea27192")>]
[<assembly: AssemblyVersion("0.9.1.0")>]
[<assembly: AssemblyVersion("0.9.2.0")>]
[<assembly: AssemblyFileVersion("1.0.0.0")>]
do

View File

@ -1,8 +1,7 @@
module MyWebLog.Data.Category
module MyWebLog.Data.RethinkDB.Category
open FSharp.Interop.Dynamic
open MyWebLog.Entities
open Rethink
open RethinkDb.Driver.Ast
open System.Dynamic
@ -14,20 +13,6 @@ let private category (webLogId : string) (catId : string) =
.Get(catId)
.Filter(fun c -> c.["WebLogId"].Eq(webLogId))
/// Sort categories by their name, with their children sorted below them, including an indent level
let sortCategories categories =
let rec getChildren (cat : Category) indent =
seq {
yield cat, indent
for child in categories |> List.filter (fun c -> c.ParentId = Some cat.Id) do
yield! getChildren child (indent + 1)
}
categories
|> List.filter (fun c -> c.ParentId.IsNone)
|> List.map (fun c -> getChildren c 0)
|> Seq.collect id
|> Seq.toList
/// Get all categories for a web log
let getAllCategories conn (webLogId : string) =
r.Table(Table.Category)
@ -36,7 +21,6 @@ let getAllCategories conn (webLogId : string) =
.RunListAsync<Category>(conn)
|> await
|> Seq.toList
|> sortCategories
/// Get a specific category by its Id
let tryFindCategory conn webLogId catId : Category option =
@ -45,52 +29,41 @@ let tryFindCategory conn webLogId catId : Category option =
| null -> None
| cat -> Some <| unbox cat
/// Save a category
let saveCategory conn webLogId (cat : Category) =
match cat.Id with
| "new" -> let newCat = { cat with Id = string <| System.Guid.NewGuid()
WebLogId = webLogId }
r.Table(Table.Category)
.Insert(newCat)
.RunResultAsync(conn) |> await |> ignore
newCat.Id
| _ -> let upd8 = ExpandoObject()
upd8?Name <- cat.Name
upd8?Slug <- cat.Slug
upd8?Description <- cat.Description
upd8?ParentId <- cat.ParentId
(category webLogId cat.Id)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
cat.Id
/// Add a category
let addCategory conn (cat : Category) =
r.Table(Table.Category)
.Insert(cat)
.RunResultAsync(conn) |> await |> ignore
/// Remove a category from a given parent
let removeCategoryFromParent conn webLogId parentId catId =
match tryFindCategory conn webLogId parentId with
| Some parent -> let upd8 = ExpandoObject()
upd8?Children <- parent.Children
|> List.filter (fun childId -> childId <> catId)
(category webLogId parentId)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
| None -> ()
/// Update a category
let updateCategory conn (cat : Category) =
let upd8 = ExpandoObject()
upd8?Name <- cat.Name
upd8?Slug <- cat.Slug
upd8?Description <- cat.Description
upd8?ParentId <- cat.ParentId
(category cat.WebLogId cat.Id)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
/// Add a category to a given parent
let addCategoryToParent conn webLogId parentId catId =
match tryFindCategory conn webLogId parentId with
| Some parent -> let upd8 = ExpandoObject()
upd8?Children <- catId :: parent.Children
(category webLogId parentId)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
| None -> ()
/// Update a category's children
let updateChildren conn webLogId parentId (children : string list) =
let upd8 = ExpandoObject()
upd8?Children <- children
(category webLogId parentId)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
/// Delete a category
let deleteCategory conn cat =
// Remove the category from its parent
match cat.ParentId with
| Some parentId -> removeCategoryFromParent conn cat.WebLogId parentId cat.Id
| None -> ()
| Some parentId -> match tryFindCategory conn cat.WebLogId parentId with
| Some parent -> parent.Children
|> List.filter (fun childId -> childId <> cat.Id)
|> updateChildren conn cat.WebLogId parentId
| _ -> ()
| _ -> ()
// Move this category's children to its parent
let newParent = ExpandoObject()
newParent?ParentId <- cat.ParentId

View File

@ -1,4 +1,4 @@
namespace MyWebLog.Data
namespace MyWebLog.Data.RethinkDB
open RethinkDb.Driver
open RethinkDb.Driver.Net

View File

@ -1,4 +1,5 @@
module MyWebLog.Data.Rethink
[<AutoOpen>]
module MyWebLog.Data.RethinkDB.Extensions
open RethinkDb.Driver.Ast
open RethinkDb.Driver.Net

View File

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>d6c2be5e-883a-4f34-9905-b730543ca380</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>MyWebLog.Data.RethinkDB</RootNamespace>
<AssemblyName>MyWebLog.Data.RethinkDB</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
<Name>MyWebLog.Data.RethinkDB</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\MyWebLog.Data.RethinkDB.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\MyWebLog.Data.RethinkDB.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions.fs" />
<Compile Include="Table.fs" />
<Compile Include="DataConfig.fs" />
<Compile Include="Category.fs" />
<Compile Include="Page.fs" />
<Compile Include="Post.fs" />
<Compile Include="User.fs" />
<Compile Include="WebLog.fs" />
<Compile Include="SetUp.fs" />
<Compile Include="RethinkMyWebLogData.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyWebLog.Entities\MyWebLog.Entities.fsproj">
<Name>MyWebLog.Entities</Name>
<Project>{a87f3cf5-2189-442b-8acf-929f5153ac22}</Project>
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '11.0'">
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Common.Logging">
<HintPath>..\packages\Common.Logging\lib\net40\Common.Logging.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Common.Logging.Core">
<HintPath>..\packages\Common.Logging.Core\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Dynamitey">
<HintPath>..\packages\Dynamitey\lib\net40\Dynamitey.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="FSharp.Interop.Dynamic">
<HintPath>..\packages\FSharp.Interop.Dynamic\lib\portable-net45+sl50+win\FSharp.Interop.Dynamic.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.0'">
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="RethinkDb.Driver">
<HintPath>..\packages\RethinkDb.Driver\lib\net45\RethinkDb.Driver.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
</Project>

View File

@ -0,0 +1,70 @@
module MyWebLog.Data.RethinkDB.Page
open FSharp.Interop.Dynamic
open MyWebLog.Entities
open RethinkDb.Driver.Ast
open System.Dynamic
let private r = RethinkDb.Driver.RethinkDB.R
/// Try to find a page by its Id, optionally including revisions
let tryFindPageById conn webLogId (pageId : string) includeRevs =
let pg = r.Table(Table.Page)
.Get(pageId)
match (match includeRevs with
| true -> pg.RunAtomAsync<Page>(conn)
| _ -> pg.Without("Revisions").RunAtomAsync<Page>(conn)
|> await |> box) with
| null -> None
| page -> let pg : Page = unbox page
match pg.WebLogId = webLogId with true -> Some pg | _ -> None
/// Find a page by its permalink
let tryFindPageByPermalink conn (webLogId : string) (permalink : string) =
r.Table(Table.Page)
.GetAll(r.Array(webLogId, permalink)).OptArg("index", "Permalink")
.Without("Revisions")
.RunCursorAsync<Page>(conn)
|> await
|> Seq.tryHead
/// Get a list of all pages (excludes page text and revisions)
let findAllPages conn (webLogId : string) =
r.Table(Table.Page)
.GetAll(webLogId).OptArg("index", "WebLogId")
.OrderBy("Title")
.Without("Text", "Revisions")
.RunListAsync<Page>(conn)
|> await
|> Seq.toList
/// Add a page
let addPage conn (page : Page) =
r.Table(Table.Page)
.Insert(page)
.RunResultAsync(conn) |> await |> ignore
/// Update a page
let updatePage conn (page : Page) =
match tryFindPageById conn page.WebLogId page.Id false with
| Some _ -> let upd8 = ExpandoObject()
upd8?Title <- page.Title
upd8?Permalink <- page.Permalink
upd8?PublishedOn <- page.PublishedOn
upd8?UpdatedOn <- page.UpdatedOn
upd8?Text <- page.Text
upd8?Revisions <- page.Revisions
r.Table(Table.Page)
.Get(page.Id)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
| _ -> ()
/// Delete a page
let deletePage conn webLogId pageId =
match tryFindPageById conn webLogId pageId false with
| Some _ -> r.Table(Table.Page)
.Get(pageId)
.Delete()
.RunResultAsync(conn) |> await |> ignore
| _ -> ()

View File

@ -1,8 +1,7 @@
module MyWebLog.Data.Post
module MyWebLog.Data.RethinkDB.Post
open FSharp.Interop.Dynamic
open MyWebLog.Entities
open Rethink
open RethinkDb.Driver.Ast
open System.Dynamic
@ -146,6 +145,22 @@ let findFeedPosts conn webLogId nbr : (Post * User option) list =
| null -> None
| user -> Some <| unbox user)
/// Add a post
let addPost conn post =
r.Table(Table.Post)
.Insert(post)
.RunResultAsync(conn)
|> ignore
/// Update a post
let updatePost conn post =
r.Table(Table.Post)
.Get(post.Id)
.Replace( { post with Categories = []
Comments = [] } )
.RunResultAsync(conn)
|> ignore
/// Save a post
let savePost conn post =
match post.Id with

View File

@ -0,0 +1,47 @@
namespace MyWebLog.Data.RethinkDB
open MyWebLog.Data
open RethinkDb.Driver.Net
/// RethinkDB implementation of myWebLog data persistence
type RethinkMyWebLogData(conn : IConnection, cfg : DataConfig) =
interface IMyWebLogData with
member this.SetUp = fun () -> SetUp.startUpCheck cfg
member this.AllCategories = Category.getAllCategories conn
member this.CategoryById = Category.tryFindCategory conn
member this.CategoryBySlug = Category.tryFindCategoryBySlug conn
member this.AddCategory = Category.addCategory conn
member this.UpdateCategory = Category.updateCategory conn
member this.UpdateChildren = Category.updateChildren conn
member this.DeleteCategory = Category.deleteCategory conn
member this.PageById = Page.tryFindPageById conn
member this.PageByPermalink = Page.tryFindPageByPermalink conn
member this.AllPages = Page.findAllPages conn
member this.AddPage = Page.addPage conn
member this.UpdatePage = Page.updatePage conn
member this.DeletePage = Page.deletePage conn
member this.PageOfPublishedPosts = Post.findPageOfPublishedPosts conn
member this.PageOfCategorizedPosts = Post.findPageOfCategorizedPosts conn
member this.PageOfTaggedPosts = Post.findPageOfTaggedPosts conn
member this.NewerPost = Post.tryFindNewerPost conn
member this.NewerCategorizedPost = Post.tryFindNewerCategorizedPost conn
member this.NewerTaggedPost = Post.tryFindNewerTaggedPost conn
member this.OlderPost = Post.tryFindOlderPost conn
member this.OlderCategorizedPost = Post.tryFindOlderCategorizedPost conn
member this.OlderTaggedPost = Post.tryFindOlderTaggedPost conn
member this.PageOfAllPosts = Post.findPageOfAllPosts conn
member this.PostById = Post.tryFindPost conn
member this.PostByPermalink = Post.tryFindPostByPermalink conn
member this.PostByPriorPermalink = Post.tryFindPostByPriorPermalink conn
member this.FeedPosts = Post.findFeedPosts conn
member this.AddPost = Post.addPost conn
member this.UpdatePost = Post.updatePost conn
member this.LogOn = User.tryUserLogOn conn
member this.WebLogByUrlBase = WebLog.tryFindWebLogByUrlBase conn
member this.DashboardCounts = WebLog.findDashboardCounts conn

View File

@ -1,6 +1,5 @@
module MyWebLog.Data.SetUp
module MyWebLog.Data.RethinkDB.SetUp
open Rethink
open RethinkDb.Driver.Ast
open System

View File

@ -1,6 +1,6 @@
/// Constants for tables used in myWebLog
[<RequireQualifiedAccess>]
module MyWebLog.Data.Table
module MyWebLog.Data.RethinkDB.Table
/// The Category table
let Category = "Category"

View File

@ -1,7 +1,6 @@
module MyWebLog.Data.User
module MyWebLog.Data.RethinkDB.User
open MyWebLog.Entities
open Rethink
let private r = RethinkDb.Driver.RethinkDB.R

View File

@ -1,20 +1,10 @@
module MyWebLog.Data.WebLog
module MyWebLog.Data.RethinkDB.WebLog
open MyWebLog.Entities
open Rethink
open RethinkDb.Driver.Ast
let private r = RethinkDb.Driver.RethinkDB.R
/// Counts of items displayed on the admin dashboard
type DashboardCounts =
{ /// The number of pages for the web log
Pages : int
/// The number of pages for the web log
Posts : int
/// The number of categories for the web log
Categories : int }
/// Detemine the web log by the URL base
let tryFindWebLogByUrlBase conn (urlBase : string) =
r.Table(Table.WebLog)

View File

@ -0,0 +1,2 @@
FSharp.Interop.Dynamic
RethinkDb.Driver

View File

@ -2,7 +2,7 @@
open Newtonsoft.Json
// ---- Constants ----
// --- Constants ---
/// Constants to use for revision source language
[<RequireQualifiedAccess>]
@ -38,7 +38,7 @@ module CommentStatus =
[<Literal>]
let Spam = "Spam"
// ---- Entities ----
// --- Entities ---
/// A revision of a post or page
type Revision =
@ -288,3 +288,14 @@ with
Revisions = []
Categories = []
Comments = [] }
// --- UI Support ---
/// Counts of items displayed on the admin dashboard
type DashboardCounts =
{ /// The number of pages for the web log
Pages : int
/// The number of pages for the web log
Posts : int
/// The number of categories for the web log
Categories : int }

View File

@ -0,0 +1,114 @@
namespace MyWebLog.Data
open MyWebLog.Entities
/// Interface required to provide data to myWebLog's logic layer
type IMyWebLogData =
/// Function to set up the data store
abstract SetUp : (unit -> unit)
// --- Category ---
/// Get all categories for a web log
abstract AllCategories : (string -> Category list)
/// Try to find a category by its Id and web log Id (web log, category Ids)
abstract CategoryById : (string -> string -> Category option)
/// Try to find a category by its slug (web log Id, slug)
abstract CategoryBySlug : (string -> string -> Category option)
/// Add a category
abstract AddCategory : (Category -> unit)
/// Update a category
abstract UpdateCategory : (Category -> unit)
/// Update a category's children
abstract UpdateChildren : (string -> string -> string list -> unit)
/// Delete a Category
abstract DeleteCategory : (Category -> unit)
// --- Page ---
/// Try to find a page by its Id and web log Id (web log, page Ids), choosing whether to include revisions
abstract PageById : (string -> string -> bool -> Page option)
/// Try to find a page by its permalink and web log Id (web log Id, permalink)
abstract PageByPermalink : (string -> string -> Page option)
/// Get all pages for a web log
abstract AllPages : (string -> Page list)
/// Add a page
abstract AddPage : (Page -> unit)
/// Update a page
abstract UpdatePage : (Page -> unit)
/// Delete a page by its Id and web log Id (web log, page Ids)
abstract DeletePage : (string -> string -> unit)
// --- Post ---
/// Find a page of published posts for the given web log (web log Id, page #, # per page)
abstract PageOfPublishedPosts : (string -> int -> int -> Post list)
/// Find a page of published posts within a given category (web log Id, cat Id, page #, # per page)
abstract PageOfCategorizedPosts : (string -> string -> int -> int -> Post list)
/// Find a page of published posts tagged with a given tag (web log Id, tag, page #, # per page)
abstract PageOfTaggedPosts : (string -> string -> int -> int -> Post list)
/// Try to find the next newer published post for the given post
abstract NewerPost : (Post -> Post option)
/// Try to find the next newer published post within a given category
abstract NewerCategorizedPost : (string -> Post -> Post option)
/// Try to find the next newer published post tagged with a given tag
abstract NewerTaggedPost : (string -> Post -> Post option)
/// Try to find the next older published post for the given post
abstract OlderPost : (Post -> Post option)
/// Try to find the next older published post within a given category
abstract OlderCategorizedPost : (string -> Post -> Post option)
/// Try to find the next older published post tagged with a given tag
abstract OlderTaggedPost : (string -> Post -> Post option)
/// Find a page of all posts for the given web log (web log Id, page #, # per page)
abstract PageOfAllPosts : (string -> int -> int -> Post list)
/// Try to find a post by its Id and web log Id (web log, post Ids)
abstract PostById : (string -> string -> Post option)
/// Try to find a post by its permalink (web log Id, permalink)
abstract PostByPermalink : (string -> string -> Post option)
/// Try to find a post by a prior permalink (web log Id, permalink)
abstract PostByPriorPermalink : (string -> string -> Post option)
/// Get posts for the RSS feed for the given web log and number of posts
abstract FeedPosts : (string -> int -> (Post * User option) list)
/// Add a post
abstract AddPost : (Post -> unit)
/// Update a post
abstract UpdatePost : (Post -> unit)
// --- User ---
/// Attempt to log on a user
abstract LogOn : (string -> string -> User option)
// --- WebLog ---
/// Get a web log by its URL base
abstract WebLogByUrlBase : (string -> WebLog option)
/// Get dashboard counts for a web log
abstract DashboardCounts : (string -> DashboardCounts)

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>a87f3cf5-2189-442b-8acf-929f5153ac22</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>MyWebLog.Entities</RootNamespace>
<AssemblyName>MyWebLog.Entities</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
<Name>MyWebLog.Entities</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\MyWebLog.Entities.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\MyWebLog.Entities.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<Compile Include="Entities.fs" />
<Compile Include="IMyWebLogData.fs" />
</ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '11.0'">
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.0'">
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
<When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.2')">
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
</Project>

View File

@ -0,0 +1 @@
Newtonsoft.Json

View File

@ -0,0 +1,56 @@
module MyWebLog.Logic.Category
open MyWebLog.Data
open MyWebLog.Entities
/// Sort categories by their name, with their children sorted below them, including an indent level
let sortCategories categories =
let rec getChildren (cat : Category) indent =
seq {
yield cat, indent
for child in categories |> List.filter (fun c -> c.ParentId = Some cat.Id) do
yield! getChildren child (indent + 1)
}
categories
|> List.filter (fun c -> c.ParentId.IsNone)
|> List.map (fun c -> getChildren c 0)
|> Seq.collect id
|> Seq.toList
/// Find all categories for a given web log
let findAllCategories (data : IMyWebLogData) webLogId =
data.AllCategories webLogId
|> sortCategories
/// Try to find a category for a given web log Id and category Id
let tryFindCategory (data : IMyWebLogData) webLogId catId = data.CategoryById webLogId catId
/// Try to find a category by its slug for a given web log
let tryFindCategoryBySlug (data : IMyWebLogData) webLogId slug = data.CategoryBySlug webLogId slug
/// Save a category
let saveCategory (data : IMyWebLogData) (cat : Category) =
match cat.Id with
| "new" -> let newCat = { cat with Id = string <| System.Guid.NewGuid() }
data.AddCategory newCat
newCat.Id
| _ -> data.UpdateCategory cat
cat.Id
/// Remove a category from its parent
let removeCategoryFromParent (data : IMyWebLogData) webLogId parentId catId =
match tryFindCategory data webLogId parentId with
| Some parent -> parent.Children
|> List.filter (fun childId -> childId <> catId)
|> data.UpdateChildren webLogId parentId
| None -> ()
/// Add a category to a given parent
let addCategoryToParent (data : IMyWebLogData) webLogId parentId catId =
match tryFindCategory data webLogId parentId with
| Some parent -> catId :: parent.Children
|> data.UpdateChildren webLogId parentId
| None -> ()
/// Delete a category
let deleteCategory (data : IMyWebLogData) cat = data.DeleteCategory cat

View File

@ -1,18 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>1fba0b84-b09e-4b16-b9b6-5730dea27192</ProjectGuid>
<ProjectGuid>29f6eda3-4f43-4bb3-9c63-ae238a9b7f12</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>myWebLog.Data</RootNamespace>
<AssemblyName>MyWebLog.Data</AssemblyName>
<RootNamespace>MyWebLog.Logic</RootNamespace>
<AssemblyName>MyWebLog.Logic</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Name>myWebLog.Data</Name>
<Name>MyWebLog.Logic</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -22,7 +21,7 @@
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\MyWebLog.Data.xml</DocumentationFile>
<DocumentationFile>bin\Debug\MyWebLog.Logic.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -31,8 +30,31 @@
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\myWebLog.Data.XML</DocumentationFile>
<DocumentationFile>bin\Release\MyWebLog.Logic.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<Compile Include="Category.fs" />
<Compile Include="Page.fs" />
<Compile Include="Post.fs" />
<Compile Include="User.fs" />
<Compile Include="WebLog.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyWebLog.Entities\MyWebLog.Entities.fsproj">
<Name>MyWebLog.Entities</Name>
<Project>{a87f3cf5-2189-442b-8acf-929f5153ac22}</Project>
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
@ -48,61 +70,12 @@
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" />
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Entities.fs" />
<Compile Include="Table.fs" />
<Compile Include="DataConfig.fs" />
<Compile Include="Rethink.fs" />
<Compile Include="SetUp.fs" />
<Compile Include="Category.fs" />
<Compile Include="Page.fs" />
<Compile Include="Post.fs" />
<Compile Include="User.fs" />
<Compile Include="WebLog.fs" />
<None Include="ConvertOld.fsx" />
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Reference Include="Common.Logging">
<HintPath>..\packages\Common.Logging.3.3.1\lib\net40\Common.Logging.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Common.Logging.Core">
<HintPath>..\packages\Common.Logging.Core.3.3.1\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dynamitey">
<HintPath>..\packages\Dynamitey.1.0.2.0\lib\net40\Dynamitey.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Interop.Dynamic">
<HintPath>..\packages\FSharp.Interop.Dynamic.3.0.0.0\lib\portable-net45+sl50+win\FSharp.Interop.Dynamic.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="RethinkDb.Driver">
<HintPath>..\packages\RethinkDb.Driver.2.3.9\lib\net45\RethinkDb.Driver.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
<Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@ -0,0 +1,29 @@
/// Logic for manipulating <see cref="Page" /> entities
module MyWebLog.Logic.Page
open MyWebLog.Data
open MyWebLog.Entities
/// Find a page by its Id and web log Id
let tryFindPage (data : IMyWebLogData) webLogId pageId = data.PageById webLogId pageId true
/// Find a page by its Id and web log Id, without the revision list
let tryFindPageWithoutRevisions (data : IMyWebLogData) webLogId pageId = data.PageById webLogId pageId false
/// Find a page by its permalink
let tryFindPageByPermalink (data : IMyWebLogData) webLogId permalink = data.PageByPermalink webLogId permalink
/// Find a list of all pages (excludes text and revisions)
let findAllPages (data : IMyWebLogData) webLogId = data.AllPages webLogId
/// Save a page
let savePage (data : IMyWebLogData) (page : Page) =
match page.Id with
| "new" -> let newPg = { page with Id = string <| System.Guid.NewGuid() }
data.AddPage newPg
newPg.Id
| _ -> data.UpdatePage page
page.Id
/// Delete a page
let deletePage (data : IMyWebLogData) webLogId pageId = data.DeletePage webLogId pageId

View File

@ -0,0 +1,60 @@
/// Logic for manipulating <see cref="Post" /> entities
module MyWebLog.Logic.Post
open MyWebLog.Data
open MyWebLog.Entities
/// Find a page of published posts
let findPageOfPublishedPosts (data : IMyWebLogData) webLogId pageNbr nbrPerPage =
data.PageOfPublishedPosts webLogId pageNbr nbrPerPage
/// Find a pages of published posts in a given category
let findPageOfCategorizedPosts (data : IMyWebLogData) webLogId catId pageNbr nbrPerPage =
data.PageOfCategorizedPosts webLogId catId pageNbr nbrPerPage
/// Find a page of published posts tagged with a given tag
let findPageOfTaggedPosts (data : IMyWebLogData) webLogId tag pageNbr nbrPerPage =
data.PageOfTaggedPosts webLogId tag pageNbr nbrPerPage
/// Find the next newer published post for the given post
let tryFindNewerPost (data : IMyWebLogData) post = data.NewerPost post
/// Find the next newer published post in a given category for the given post
let tryFindNewerCategorizedPost (data : IMyWebLogData) catId post = data.NewerCategorizedPost catId post
/// Find the next newer published post tagged with a given tag for the given post
let tryFindNewerTaggedPost (data : IMyWebLogData) tag post = data.NewerTaggedPost tag post
/// Find the next older published post for the given post
let tryFindOlderPost (data : IMyWebLogData) post = data.OlderPost post
/// Find the next older published post in a given category for the given post
let tryFindOlderCategorizedPost (data : IMyWebLogData) catId post = data.OlderCategorizedPost catId post
/// Find the next older published post tagged with a given tag for the given post
let tryFindOlderTaggedPost (data : IMyWebLogData) tag post = data.OlderTaggedPost tag post
/// Find a page of all posts for a web log
let findPageOfAllPosts (data : IMyWebLogData) webLogId pageNbr nbrPerPage =
data.PageOfAllPosts webLogId pageNbr nbrPerPage
/// Try to find a post by its Id
let tryFindPost (data : IMyWebLogData) webLogId postId = data.PostById webLogId postId
/// Try to find a post by its permalink
let tryFindPostByPermalink (data : IMyWebLogData) webLogId permalink = data.PostByPermalink webLogId permalink
/// Try to find a post by its prior permalink
let tryFindPostByPriorPermalink (data : IMyWebLogData) webLogId permalink = data.PostByPriorPermalink webLogId permalink
/// Find posts for the RSS feed
let findFeedPosts (data : IMyWebLogData) webLogId nbrOfPosts = data.FeedPosts webLogId nbrOfPosts
/// Save a post
let savePost (data : IMyWebLogData) post =
match post.Id with
| "new" -> let newPost = { post with Id = string <| System.Guid.NewGuid() }
data.AddPost newPost
newPost.Id
| _ -> data.UpdatePost post
post.Id

View File

@ -0,0 +1,7 @@
/// Logic for manipulating <see cref="User" /> entities
module MyWebLog.Logic.User
open MyWebLog.Data
/// Try to log on a user
let tryUserLogOn (data : IMyWebLogData) email passwordHash = data.LogOn email passwordHash

View File

@ -0,0 +1,11 @@
/// Logic for manipulating <see cref="WebLog" /> entities
module MyWebLog.Logic.WebLog
open MyWebLog.Data
open MyWebLog.Entities
/// Find a web log by its URL base
let tryFindWebLogByUrlBase (data : IMyWebLogData) urlBase = data.WebLogByUrlBase urlBase
/// Find the counts for the admin dashboard
let findDashboardCounts (data : IMyWebLogData) webLogId = data.DashboardCounts webLogId

View File

@ -0,0 +1,4 @@
namespace MyWebLog.Web
type Web() =
member this.X = "F#"

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>07e60874-6cf5-4d53-aee0-f17ef28228dd</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>MyWebLog.Tests</RootNamespace>
<AssemblyName>MyWebLog.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
<Name>MyWebLog.Tests</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\MyWebLog.Tests.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\MyWebLog.Tests.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<Compile Include="MyWebLog.Tests.fs" />
</ItemGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '11.0'">
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

14
src/build.cmd Normal file
View File

@ -0,0 +1,14 @@
@echo off
cls
.paket\paket.bootstrapper.exe
if errorlevel 1 (
exit /b %errorlevel%
)
.paket\paket.exe restore
if errorlevel 1 (
exit /b %errorlevel%
)
packages\FAKE\tools\FAKE.exe build.fsx %*

42
src/build.fsx Normal file
View File

@ -0,0 +1,42 @@
// include Fake libs
#r "./packages/FAKE/tools/FakeLib.dll"
open Fake
// Directories
let buildDir = "./build/"
let deployDir = "./deploy/"
// Filesets
let appReferences =
!! "/**/*.csproj"
++ "/**/*.fsproj"
// version info
let version = "0.1" // or retrieve from CI server
// Targets
Target "Clean" (fun _ ->
CleanDirs [buildDir; deployDir]
)
Target "Build" (fun _ ->
// compile all projects below src/app/
MSBuildDebug buildDir "Build" appReferences
|> Log "AppBuild-Output: "
)
Target "Deploy" (fun _ ->
!! (buildDir + "/**/*.*")
-- "*.zip"
|> Zip buildDir (deployDir + "ApplicationName." + version + ".zip")
)
// Build order
"Clean"
==> "Build"
==> "Deploy"
// start build
RunTargetOrDefault "Build"

33
src/build.sh Normal file
View File

@ -0,0 +1,33 @@
#!/bin/bash
if test "$OS" = "Windows_NT"
then
# use .Net
.paket/paket.bootstrapper.exe
exit_code=$?
if [ $exit_code -ne 0 ]; then
exit $exit_code
fi
.paket/paket.exe restore
exit_code=$?
if [ $exit_code -ne 0 ]; then
exit $exit_code
fi
packages/FAKE/tools/FAKE.exe $@ --fsiargs build.fsx
else
# use mono
mono .paket/paket.bootstrapper.exe
exit_code=$?
if [ $exit_code -ne 0 ]; then
exit $exit_code
fi
mono .paket/paket.exe restore
exit_code=$?
if [ $exit_code -ne 0 ]; then
exit $exit_code
fi
mono packages/FAKE/tools/FAKE.exe $@ --fsiargs -d:MONO build.fsx
fi

View File

@ -1,77 +0,0 @@
module MyWebLog.Data.Page
open FSharp.Interop.Dynamic
open MyWebLog.Entities
open Rethink
open RethinkDb.Driver.Ast
open System.Dynamic
let private r = RethinkDb.Driver.RethinkDB.R
/// Shorthand to get the page by its Id, filtering on web log Id
let private page (webLogId : string) (pageId : string) =
r.Table(Table.Page)
.Get(pageId)
.Filter(ReqlFunction1(fun p -> upcast p.["WebLogId"].Eq(webLogId)))
/// Get a page by its Id
let tryFindPage conn webLogId pageId =
match r.Table(Table.Page)
.Get(pageId)
.RunAtomAsync<Page>(conn) |> await |> box with
| null -> None
| page -> let pg : Page = unbox page
match pg.WebLogId = webLogId with true -> Some pg | _ -> None
/// Get a page by its Id (excluding revisions)
let tryFindPageWithoutRevisions conn webLogId pageId : Page option =
match (page webLogId pageId)
.Without("Revisions")
.RunAtomAsync<Page>(conn) |> await |> box with
| null -> None
| page -> Some <| unbox page
/// Find a page by its permalink
let tryFindPageByPermalink conn (webLogId : string) (permalink : string) =
r.Table(Table.Page)
.GetAll(r.Array(webLogId, permalink)).OptArg("index", "Permalink")
.Without("Revisions")
.RunCursorAsync<Page>(conn)
|> await
|> Seq.tryHead
/// Get a list of all pages (excludes page text and revisions)
let findAllPages conn (webLogId : string) =
r.Table(Table.Page)
.GetAll(webLogId).OptArg("index", "WebLogId")
.OrderBy("Title")
.Without("Text", "Revisions")
.RunListAsync<Page>(conn)
|> await
|> Seq.toList
/// Save a page
let savePage conn (pg : Page) =
match pg.Id with
| "new" -> let newPage = { pg with Id = string <| System.Guid.NewGuid() }
r.Table(Table.Page)
.Insert(page)
.RunResultAsync(conn) |> await |> ignore
newPage.Id
| _ -> let upd8 = ExpandoObject()
upd8?Title <- pg.Title
upd8?Permalink <- pg.Permalink
upd8?PublishedOn <- pg.PublishedOn
upd8?UpdatedOn <- pg.UpdatedOn
upd8?Text <- pg.Text
upd8?Revisions <- pg.Revisions
(page pg.WebLogId pg.Id)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
pg.Id
/// Delete a page
let deletePage conn webLogId pageId =
(page webLogId pageId)
.Delete()
.RunResultAsync(conn) |> await |> ignore

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Common.Logging" version="3.3.1" targetFramework="net452" />
<package id="Common.Logging.Core" version="3.3.1" targetFramework="net452" />
<package id="Dynamitey" version="1.0.2.0" targetFramework="net452" />
<package id="FSharp.Core" version="4.0.0.1" targetFramework="net452" />
<package id="FSharp.Interop.Dynamic" version="3.0.0.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="RethinkDb.Driver" version="2.3.9" targetFramework="net452" />
</packages>

View File

@ -1,186 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>e6ee110a-27a6-4a19-b0cb-d24f48f71b53</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>myWebLog.Web</RootNamespace>
<AssemblyName>MyWebLog.Web</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Name>myWebLog.Web</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Debug\MyWebLog.Web.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\myWebLog.Web.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
</PropertyGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '11.0'">
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
</Otherwise>
</Choose>
<Import Project="$(FSharpTargetsPath)" />
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Keys.fs" />
<Compile Include="AppConfig.fs" />
<Compile Include="ViewModels.fs" />
<Compile Include="ModuleExtensions.fs" />
<Compile Include="AdminModule.fs" />
<Compile Include="CategoryModule.fs" />
<Compile Include="PageModule.fs" />
<Compile Include="PostModule.fs" />
<Compile Include="UserModule.fs" />
<Compile Include="App.fs" />
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Reference Include="Common.Logging">
<HintPath>..\packages\Common.Logging.3.3.1\lib\net40\Common.Logging.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Common.Logging.Core">
<HintPath>..\packages\Common.Logging.Core.3.3.1\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="CSharpFormat">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\CSharpFormat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dynamitey">
<HintPath>..\packages\Dynamitey.1.0.2.0\lib\net40\Dynamitey.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.CodeFormat">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.CodeFormat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Compiler.Service">
<HintPath>..\packages\FSharp.Compiler.Service.2.0.0.6\lib\net45\FSharp.Compiler.Service.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Formatting.Common">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.Formatting.Common.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Interop.Dynamic">
<HintPath>..\packages\FSharp.Interop.Dynamic.3.0.0.0\lib\portable-net45+sl50+win\FSharp.Interop.Dynamic.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Literate">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.Literate.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Markdown">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.Markdown.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.MetadataFormat">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.MetadataFormat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharpVSPowerTools.Core">
<HintPath>..\packages\FSharpVSPowerTools.Core.2.3.0\lib\net45\FSharpVSPowerTools.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="Nancy">
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nancy.Authentication.Forms">
<HintPath>..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nancy.Session.Persistable">
<HintPath>..\packages\Nancy.Session.Persistable.0.9.0\lib\net452\Nancy.Session.Persistable.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Nancy.Session.RethinkDb">
<HintPath>..\packages\Nancy.Session.RethinkDB.0.9.0\lib\net452\Nancy.Session.RethinkDb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NodaTime">
<HintPath>..\packages\NodaTime.1.3.2\lib\net35-Client\NodaTime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="RazorEngine">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\RazorEngine.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="RethinkDb.Driver">
<HintPath>..\packages\RethinkDb.Driver.2.3.9\lib\net45\RethinkDb.Driver.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Suave">
<HintPath>..\packages\Suave.1.1.3\lib\net40\Suave.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Web.Razor">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\System.Web.Razor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\myWebLog.Data\myWebLog.Data.fsproj">
<Name>myWebLog.Data</Name>
<Project>{1fba0b84-b09e-4b16-b9b6-5730dea27192}</Project>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\myWebLog.Resources\myWebLog.Resources.csproj">
<Name>myWebLog.Resources</Name>
<Project>{a12ea8da-88bc-4447-90cb-a0e2dcc37523}</Project>
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Common.Logging" version="3.3.1" targetFramework="net452" />
<package id="Common.Logging.Core" version="3.3.1" targetFramework="net452" />
<package id="Dynamitey" version="1.0.2.0" targetFramework="net452" />
<package id="FSharp.Compiler.Service" version="2.0.0.6" targetFramework="net452" />
<package id="FSharp.Core" version="4.0.0.1" targetFramework="net452" />
<package id="FSharp.Formatting" version="2.14.4" targetFramework="net452" />
<package id="FSharp.Interop.Dynamic" version="3.0.0.0" targetFramework="net452" />
<package id="FSharpVSPowerTools.Core" version="2.3.0" targetFramework="net452" />
<package id="Nancy" version="1.4.3" targetFramework="net452" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net452" />
<package id="Nancy.Session.Persistable" version="0.9.0" targetFramework="net452" />
<package id="Nancy.Session.RethinkDB" version="0.9.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="NodaTime" version="1.3.2" targetFramework="net452" />
<package id="RethinkDb.Driver" version="2.3.9" targetFramework="net452" />
<package id="Suave" version="1.1.3" targetFramework="net452" />
</packages>

View File

@ -1,39 +1,14 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "myWebLog", "myWebLog\myWebLog.csproj", "{B9F6DB52-65A1-4C2A-8C97-739E08A1D4FB}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "myWebLog.Web", "myWebLog.Web\myWebLog.Web.fsproj", "{E6EE110A-27A6-4A19-B0CB-D24F48F71B53}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "myWebLog.Data", "myWebLog.Data\myWebLog.Data.fsproj", "{1FBA0B84-B09E-4B16-B9B6-5730DEA27192}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "myWebLog.Resources", "myWebLog.Resources\myWebLog.Resources.csproj", "{A12EA8DA-88BC-4447-90CB-A0E2DCC37523}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{DF15419B-90C6-4F45-8EC1-7A63C5D3565C}"
ProjectSection(SolutionItems) = preProject
paket.dependencies = paket.dependencies
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B9F6DB52-65A1-4C2A-8C97-739E08A1D4FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9F6DB52-65A1-4C2A-8C97-739E08A1D4FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9F6DB52-65A1-4C2A-8C97-739E08A1D4FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9F6DB52-65A1-4C2A-8C97-739E08A1D4FB}.Release|Any CPU.Build.0 = Release|Any CPU
{E6EE110A-27A6-4A19-B0CB-D24F48F71B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6EE110A-27A6-4A19-B0CB-D24F48F71B53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6EE110A-27A6-4A19-B0CB-D24F48F71B53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6EE110A-27A6-4A19-B0CB-D24F48F71B53}.Release|Any CPU.Build.0 = Release|Any CPU
{1FBA0B84-B09E-4B16-B9B6-5730DEA27192}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FBA0B84-B09E-4B16-B9B6-5730DEA27192}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FBA0B84-B09E-4B16-B9B6-5730DEA27192}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FBA0B84-B09E-4B16-B9B6-5730DEA27192}.Release|Any CPU.Build.0 = Release|Any CPU
{A12EA8DA-88BC-4447-90CB-A0E2DCC37523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A12EA8DA-88BC-4447-90CB-A0E2DCC37523}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A12EA8DA-88BC-4447-90CB-A0E2DCC37523}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A12EA8DA-88BC-4447-90CB-A0E2DCC37523}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection

View File

@ -15,7 +15,20 @@
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.data>
<DbProviderFactories>
<add name="SqlClient Data Provider"
invariant="System.Data.SqlClient"
description=".NET Framework Data Provider for SQL Server"
type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</DbProviderFactories>
</system.data>
<connectionStrings>
<clear />
<add name="SessionStore"
providerName="System.Data.SqlClient"
connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\danie\Documents\Sandbox\myWebLog\src\myWebLog\session.mdf;Integrated Security=True;Connect Timeout=30" />
</connectionStrings>
</configuration>
<!-- publicKeyToken="32ab4ba45e0a69a1"
culture="neutral"

View File

@ -56,20 +56,27 @@
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\myWebLog.Data\myWebLog.Data.fsproj">
<Project>{1fba0b84-b09e-4b16-b9b6-5730dea27192}</Project>
<Name>myWebLog.Data</Name>
<ProjectReference Include="..\MyWebLog.App\MyWebLog.App.fsproj">
<Project>{9cea3a8b-e8aa-44e6-9f5f-2095ceed54eb}</Project>
<Name>MyWebLog.App</Name>
</ProjectReference>
<ProjectReference Include="..\myWebLog.Resources\myWebLog.Resources.csproj">
<ProjectReference Include="..\MyWebLog.Data.RethinkDB\MyWebLog.Data.RethinkDB.fsproj">
<Project>{d6c2be5e-883a-4f34-9905-b730543ca380}</Project>
<Name>myWebLog.Web</Name>
</ProjectReference>
<ProjectReference Include="..\MyWebLog.Entities\MyWebLog.Entities.fsproj">
<Project>{a87f3cf5-2189-442b-8acf-929f5153ac22}</Project>
<Name>MyWebLog.Entities</Name>
</ProjectReference>
<ProjectReference Include="..\MyWebLog.Logic\MyWebLog.Logic.fsproj">
<Project>{29f6eda3-4f43-4bb3-9c63-ae238a9b7f12}</Project>
<Name>MyWebLog.Entities</Name>
</ProjectReference>
<ProjectReference Include="..\MyWebLog.Resources\MyWebLog.Resources.csproj">
<Project>{a12ea8da-88bc-4447-90cb-a0e2dcc37523}</Project>
<Name>myWebLog.Resources</Name>
</ProjectReference>
<ProjectReference Include="..\myWebLog.Web\myWebLog.Web.fsproj">
<Project>{e6ee110a-27a6-4a19-b0cb-d24f48f71b53}</Project>
<Name>myWebLog.Web</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="content\scripts\tinymce-init.js" />
<Content Include="content\styles\admin.css" />

View File

@ -12,7 +12,7 @@
<article>
<h1>
<a href="/@Current.Post.Permalink"
title="@Translate.PermanentLinkTo &quot;@Current.Post.Title@quot;">@Current.Post.Title</a>
title="@Translate.PermanentLinkTo &quot;@Current.Post.Title&quot;">@Current.Post.Title</a>
</h1>
<p>
<i class="fa fa-calendar" title="@Translate.Date"></i> @Current.PublishedDate &nbsp;

13
src/paket.dependencies Normal file
View File

@ -0,0 +1,13 @@
framework: net40, net45, net452
source https://www.nuget.org/api/v2
nuget Common.Logging 3.3.0
nuget FAKE
nuget FSharp.Interop.Dynamic
nuget FSharp.Formatting
nuget Nancy
nuget Nancy.Authentication.Forms
nuget Nancy.Session.RethinkDb
nuget Newtonsoft.Json
nuget NodaTime
nuget RethinkDb.Driver
nuget Suave

34
src/paket.lock Normal file
View File

@ -0,0 +1,34 @@
FRAMEWORK: NET40, NET45, NET452
NUGET
remote: https://www.nuget.org/api/v2
Common.Logging (3.3)
Common.Logging.Core (>= 3.3)
Common.Logging.Core (3.3.1)
Dynamitey (1.0.2)
FAKE (4.36)
FSharp.Compiler.Service (2.0.0.6)
FSharp.Core (4.0.0.1)
FSharp.Formatting (2.14.4)
FSharp.Compiler.Service (2.0.0.6)
FSharpVSPowerTools.Core (>= 2.3 < 2.4)
FSharp.Interop.Dynamic (3.0)
Dynamitey (>= 1.0.2)
FSharp.Core (>= 3.1.2.1)
FSharpVSPowerTools.Core (2.3)
FSharp.Compiler.Service (>= 2.0.0.3)
Nancy (1.4.3)
Nancy.Authentication.Forms (1.4.1)
Nancy (>= 1.4.1)
Nancy.Session.Persistable (0.9)
Nancy (>= 1.4.3)
Newtonsoft.Json (>= 9.0.1)
Nancy.Session.RethinkDB (0.9)
Nancy.Session.Persistable (>= 0.9)
RethinkDb.Driver (>= 2.3.9)
Newtonsoft.Json (9.0.1)
NodaTime (1.3.2)
RethinkDb.Driver (2.3.10)
Common.Logging (>= 3.3) - framework: net45, net452
Newtonsoft.Json (>= 9.0.1) - framework: net45, net452
Suave (1.1.3)
FSharp.Core (>= 3.1.2.5)