Integration of wiki

stable
Jesús Espino 2014-07-24 17:25:28 +02:00
parent 1fdd7acf5b
commit 8babff7cec
20 changed files with 493 additions and 92 deletions

View File

@ -45,6 +45,16 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide) ->
$routeProvider.when("/project/:pslug/tasks/:taskref/edit",
{templateUrl: "/partials/task-detail-edit.html"})
# Wiki
$routeProvider.when("/project/:pslug/wiki",
{redirectTo: (params) -> "/project/#{params.pslug}/wiki/home"})
$routeProvider.when("/project/:pslug/wiki/:slug",
{templateUrl: "/partials/wiki.html"})
$routeProvider.when("/project/:pslug/wiki/:slug/edit",
{templateUrl: "/partials/wiki-edit.html"})
# Issues
$routeProvider.when("/project/:pslug/issues", {templateUrl: "/partials/issues.html"})
$routeProvider.when("/project/:pslug/issues/:issueref",
@ -147,6 +157,7 @@ modules = [
"taigaIssues",
"taigaUserStories",
"taigaTasks",
"taigaWiki",
"taigaSearch",
"taigaAdmin",
"taigaNavMenu",

View File

@ -66,6 +66,9 @@ urls = {
"project-tasks-detail": "/project/:project/tasks/:ref",
"project-tasks-detail-edit": "/project/:project/tasks/:ref/edit",
"project-wiki": "/project/:project/wiki",
"project-wiki-page": "/project/:project/wiki/:slug",
"project-issues-detail": "/project/:project/issues/:ref",
"project-issues-detail-edit": "/project/:project/issues/:ref/edit",

View File

@ -43,6 +43,13 @@ BindOnceSrcDirective = ->
$el.attr("src", val)
return {link:link}
# Object href bind once helper.
BindOnceHrefDirective = ->
link = ($scope, $el, $attrs) ->
bindOnce $scope, $attrs.tgBoHref, (val) ->
$el.attr("href", val)
return {link:link}
# Object alt bind once helper.
BindOnceAltDirective = ->
link = ($scope, $el, $attrs) ->
@ -75,6 +82,7 @@ module = angular.module("taigaBase")
module.directive("tgBoHtml", BindOnceHtmlDirective)
module.directive("tgBoRef", BindOnceRefDirective)
module.directive("tgBoSrc", BindOnceSrcDirective)
module.directive("tgBoHref", BindOnceHrefDirective)
module.directive("tgBoAlt", BindOnceAltDirective)
module.directive("tgBoTitle", BindOnceTitleDirective)
module.directive("tgBindTitle", BindTitleDirective)

View File

@ -147,6 +147,7 @@ class RepositoryService extends taiga.Service
params.task = options.taskref if options.taskref?
params.issue = options.issueref if options.issueref?
params.milestone = options.mlref if options.mlref?
params.wikipage = options.wikipage if options.wikipage?
return @.queryOneRaw("resolver", null, params)

View File

@ -92,7 +92,7 @@ ProjectMenuDirective = ($log, $compile, $rootscope) ->
</a>
</li>
<li id="nav-wiki">
<a href="" title="Wiki">
<a href="" title="Wiki" tg-nav="project-wiki:project=project.slug">
<span class="icon icon-wiki"></span>
<span class="item">Wiki</span>
</a>

View File

@ -44,6 +44,7 @@ urls = {
"issues-restore": "/api/v1/issues/%s/restore"
"wiki": "/api/v1/wiki"
"wiki-restore": "/api/v1/wiki/%s/restore"
"wiki-links": "/api/v1/wiki-links"
"choices/userstory-statuses": "/api/v1/userstory-statuses"
"choices/userstory-statuses/bulk-update-order": "/api/v1/userstory-statuses/bulk_update_order"
"choices/points": "/api/v1/points"
@ -117,6 +118,7 @@ module.run([
"$tgSprintsResourcesProvider",
"$tgUserstoriesResourcesProvider",
"$tgTasksResourcesProvider",
"$tgWikiResourcesProvider",
"$tgIssuesResourcesProvider",
"$tgSearchResourcesProvider",
"$tgMdRenderResourcesProvider",

View File

@ -0,0 +1,39 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: modules/resources/wikis.coffee
###
taiga = @.taiga
resourceProvider = ($repo, $http, $urls) ->
service = {}
service.get = (wikiId) ->
return $repo.queryOne("wiki", wikiId)
service.listLinks = (projectId) ->
return $repo.queryMany("wiki-links", {project: projectId})
return (instance) ->
instance.wiki = service
module = angular.module("taigaResources")
module.factory("$tgWikiResourcesProvider", ["$tgRepo", "$tgHttp", "$tgUrls", resourceProvider])

View File

@ -0,0 +1,22 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: modules/wiki.coffee
###
module = angular.module("taigaWiki", [])

View File

@ -0,0 +1,223 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: modules/wiki/detail.coffee
###
taiga = @.taiga
mixOf = @.taiga.mixOf
groupBy = @.taiga.groupBy
bindOnce = @.taiga.bindOnce
module = angular.module("taigaWiki")
#############################################################################
## Wiki Detail Controller
#############################################################################
class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgConfirm",
"$tgResources",
"$routeParams",
"$q",
"$location",
"$filter",
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @filter) ->
@scope.projectSlug = @params.pslug
@scope.wikiSlug = @params.slug
@scope.sectionName = "Wiki"
promise = @.loadInitialData()
promise.then null, ->
console.log "FAIL" #TODO
loadProject: ->
return @rs.projects.get(@scope.projectId).then (project) =>
@scope.project = project
@scope.membersById = groupBy(project.memberships, (x) -> x.user)
return project
loadWiki: ->
return @rs.wiki.get(@scope.wikiId).then (wiki) =>
@scope.wiki = wiki
loadWikiLinks: ->
return @rs.wiki.listLinks(@scope.projectId).then (wikiLinks) =>
@scope.wikiLinks = wikiLinks
loadInitialData: ->
params = {
pslug: @params.pslug
wikipage: @params.slug
}
promise = @repo.resolve(params).then (data) =>
@scope.wikiId = data.wikipage
@scope.projectId = data.project
return data
promise.then null, =>
@location.path("/project/#{@params.pslug}/wiki/#{@params.slug}/edit")
return promise.then(=> @.loadProject())
.then(=> @.loadUsersAndRoles())
.then(=> @.loadWikiLinks())
.then(=> @.loadWiki())
edit: ->
@location.path("/project/#{@scope.projectSlug}/wiki/#{@scope.wikiSlug}/edit")
delete: ->
onSuccess = =>
@confirm.notify("success")
@location.path("/project/#{@scope.projectSlug}/wiki")
onError = =>
@confirm.notify("error")
@repo.remove(@scope.wiki).then onSuccess, onError
module.controller("WikiDetailController", WikiDetailController)
#############################################################################
## Wiki Edit Controller
#############################################################################
class WikiEditController extends WikiDetailController
loadInitialData: ->
deferred = @q.defer()
params = {
pslug: @params.pslug
}
promise = @repo.resolve(params)
promise.then (data) =>
@scope.wikiId = data.wikipage
@scope.projectId = data.project
return data
promise.then(=> @.loadProject())
.then(=> @.loadUsersAndRoles())
.then(=> @.loadWikiLinks())
params = {
pslug: @params.pslug
wikipage: @params.slug
}
promise2 = @repo.resolve(params)
promise2.then (data) =>
@scope.wikiId = data.wikipage
return data
promise2.then => @.loadWiki()
promise2.then null, =>
@scope.wiki = {
content: ""
}
return @q.all(promise, promise2)
save: ->
onSuccess = =>
@confirm.notify("success")
@location.path("/project/#{@scope.projectSlug}/wiki/#{@scope.wiki.slug}")
onError = =>
@confirm.notify("error")
@location.path("/project/#{@scope.projectSlug}/wiki/#{@scope.wiki.slug}")
if @scope.wiki.id
@repo.save(@scope.wiki).then onSuccess, onError
else
@scope.wiki.project = @scope.projectId
@scope.wiki.slug = @scope.wikiSlug
@repo.create("wiki", @scope.wiki).then onSuccess, onError
module.controller("WikiEditController", WikiEditController)
#############################################################################
## Wiki Main Directive
#############################################################################
WikiDirective = ($tgrepo, $log, $location, $confirm) ->
link = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
return {link:link}
module.directive("tgWikiDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", WikiDirective])
#############################################################################
## Wiki Edit Main Directive
#############################################################################
WikiEditDirective = ($tgrepo, $log, $location, $confirm) ->
link = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
return {link:link}
module.directive("tgWikiEdit", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", WikiEditDirective])
#############################################################################
## Wiki User Info Directive
#############################################################################
WikiUserInfoDirective = ($log) ->
template = _.template("""
<figure class="avatar">
<img src="<%= imgurl %>" alt="<%- name %>">
</figure>
<span class="description">last modification</span>
<span class="username"><%- name %></span>
""")
link = ($scope, $el, $attrs) ->
if not $attrs.ngModel?
return $log.error "WikiUserDirective: no ng-model attr is defined"
render = (wiki) ->
if not $scope.usersById?
$log.error "WikiUserDirective requires userById set in scope."
else
user = $scope.usersById[wiki.last_modifier]
if user is undefined
ctx = {name: "Unassigned", imgurl: "/images/unnamed.png"}
else
ctx = {name: user.full_name_display, imgurl: user.photo}
html = template(ctx)
$el.html(html)
bindOnce($scope, $attrs.ngModel, render)
return {
link: link
restrict: "AE"
}
module.directive("tgWikiUserInfo", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", WikiUserInfoDirective])

View File

@ -0,0 +1,118 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: modules/wiki/detail.coffee
###
taiga = @.taiga
mixOf = @.taiga.mixOf
groupBy = @.taiga.groupBy
bindOnce = @.taiga.bindOnce
slugify = @.taiga.slugify
module = angular.module("taigaWiki")
#############################################################################
## Wiki Main Directive
#############################################################################
WikiNavDirective = ($tgrepo, $log, $location, $confirm) ->
template = _.template("""
<header>
<h1>Links</h1>
</header>
<nav>
<ul>
<% _.each(wikiLinks, function(link, index) { %>
<li class="wiki-link" data-id="<%- index %>">
<a title="<%- link.title %>">
<span class="link-title"><%- link.title %></span>
<span class="icon icon-delete"></span>
</a>
<input type="text" placeholder="name" class="hidden" value="<%- link.title %>" />
</li>
<% }) %>
<li class="new hidden">
<input type="text" placeholder="name"/>
</li>
</ul>
</nav>
<a href="" title="Add link" class="add-button button button-gray">Add link</a>
""")
link = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
if not $attrs.ngModel?
return $log.error "WikiNavDirective: no ng-model attr is defined"
render = (wikiLinks) ->
html = template({wikiLinks: wikiLinks, projectSlug: $scope.projectSlug})
$el.off()
$el.html(html)
$el.on "click", ".wiki-link .link-title", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
linkId = target.parents('.wiki-link').data('id')
linkSlug = $scope.wikiLinks[linkId].href
$scope.$apply ->
$location.path("/project/#{$scope.projectSlug}/wiki/#{linkSlug}")
$el.on "click", ".add-button", (event) ->
event.preventDefault()
$el.find(".new").removeClass("hidden")
$el.find(".new input").focus()
$el.find(".add-button").hide()
$el.on "click", ".wiki-link .icon-delete", (event) ->
event.preventDefault()
event.stopPropagation()
target = angular.element(event.currentTarget)
linkId = target.parents('.wiki-link').data('id')
$tgrepo.remove($scope.wikiLinks[linkId]).then ->
$ctrl.loadWikiLinks().then ->
render($scope.wikiLinks)
$el.on "keyup", ".new input", (event) ->
event.preventDefault()
if event.keyCode == 13
target = angular.element(event.currentTarget)
newLink = target.val()
$el.find(".new").addClass("hidden")
$el.find(".new input").val('')
$tgrepo.create("wiki-links", {project: $scope.projectId, title: newLink, href: slugify(newLink)}).then ->
$ctrl.loadWikiLinks().then ->
render($scope.wikiLinks)
$el.find(".add-button").show()
else if event.keyCode == 27
target = angular.element(event.currentTarget)
$el.find(".new").addClass("hidden")
$el.find(".new input").val('')
$el.find(".add-button").show()
bindOnce($scope, $attrs.ngModel, render)
return {link:link}
module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", WikiNavDirective])

View File

@ -44,6 +44,10 @@ trim = (data, char) ->
return _.str.trim(data, char)
slugify = (data) ->
return _.str.slugify(data)
toggleText = (element, texts) ->
nextTextPosition = element.data('nextTextPosition')
nextTextPosition = 0 if not nextTextPosition? or nextTextPosition >= texts.length
@ -99,6 +103,7 @@ taiga = @.taiga
taiga.bindOnce = bindOnce
taiga.mixOf = mixOf
taiga.trim = trim
taiga.slugify = slugify
taiga.toggleText = toggleText
taiga.groupBy = groupBy
taiga.timeout = timeout

View File

@ -1,28 +0,0 @@
section.wiki-nav
header
h1 Links
nav
ul
li
a(href="", title="link-name")
span Link1
span.icon.icon-arrow-right
li
a(href="", title="link-name")
span Link1
span.icon.icon-arrow-right
li
a(href="", title="link-name")
span Link1
span.icon.icon-arrow-right
li
a(href="", title="link-name")
span Link1
span.icon.icon-arrow-right
li
a(href="", title="link-name")
span Link1
span.icon.icon-arrow-right
a.button.button-green(href="", title="Edit page") Edit page
a.button.button-gray(href="", title="Add page") Add page
a.button.button-red(href="", title="Delete page") Delete page

View File

@ -1,21 +1,9 @@
div.summary.wiki-summary
ul
li
span.number 20
span.description document <br />created
li
span.number 20
span.description document <br />attached
li
span.number 125
span.number(tg-bo-html="wiki.editions")
span.description times <br />edited
li
span.number 12/05/2014
span.number(tg-bo-html="wiki.modified_date|date:'dd/MM/yyyy HH:mm'")
span.description last <br />edit
li.username-edition
figure.avatar
img(src="http://thecodeplayer.com/u/uifaces/34.jpg", alt="username")
span.description last modification
span.username Anler Hernández
//- Do we really need a button for this?
a.button.button-green(href="", title="See history") See history
li.username-edition(tg-wiki-user-info, ng-model='wiki')

View File

@ -0,0 +1,23 @@
extends dummy-layout
block head
title Taiga Project management web application with scrum in mind!
block content
div.wrapper(tg-wiki-edit, ng-controller="WikiEditController as ctrl",
ng-init="section='wiki'")
sidebar.menu-secondary.extrabar
section.wiki-nav(tg-wiki-nav, ng-model="wikiLinks")
section.main.backlog
//Include views/components/mainTitle
header
h1
span(tg-bo-html="project.name")
span.green Wiki
span.green(tg-bo-html='wikiSlug')
a.button.button-green.save-wiki(href="", title="Save", ng-click="ctrl.save()") Save
section.wysiwyg
textarea(placeholder="Write a your wiki page", ng-model="wiki.content", tg-markitup)
//include views/modules/attachments

View File

@ -1,60 +1,25 @@
extends layout
extends dummy-layout
block head
title Taiga Project management web application with scrum in mind!
block content
div.wrapper
div.wrapper(tg-wiki-detail, ng-controller="WikiDetailController as ctrl",
ng-init="section='wiki'")
sidebar.menu-secondary.extrabar
include views/modules/wiki-nav
section.wiki-nav(tg-wiki-nav, ng-model="wikiLinks")
section.main.backlog
//Include views/components/mainTitle
header
h1
span Taiga
span(tg-bo-html="project.name")
span.green Wiki
span.wiki-title(tg-bo-html='wiki.slug')
a.button.button-red.edit-wiki(href="", title="Delete", ng-click="ctrl.delete()") Delete
a.button.button-green.edit-wiki(href="", title="Edit", ng-click="ctrl.edit()") Edit
include views/modules/wiki-summary
section.wiki-content.wysiwyg
h1 Page title
p.
Lorem fistrum pecador benemeritaar ese hombree diodeno ese que llega me cago en tus muelas.
Benemeritaar torpedo diodenoo la caidita mamaar sexuarl condemor te voy a borrar el cerito al ataquerl papaar papaar.
Pupita por la gloria de mi madre torpedo quietooor condemor apetecan.
Mamaar por la gloria de mi madre se calle ustée la caidita de la pradera ese que llega caballo blanco caballo negroorl ese hombree jarl.
No te digo trigo por no llamarte Rodrigor pecador te va a hasé pupitaa hasta luego Lucas está la cosa muy malar benemeritaar.
h2 Section Title
p.
No puedor mamaar a wan de la pradera diodenoo jarl diodenoo ese hombree.
Benemeritaar al ataquerl apetecan amatomaa ese que llega a peich al ataquerl torpedo a wan qué dise usteer mamaar.
Qué dise usteer condemor pupita papaar papaar quietooor pupita.
Papaar papaar va usté muy cargadoo está la cosa muy malar pecador jarl se calle ustée.
h3 Subsection title
p.
Qué dise usteer ese pedazo de no puedor te voy a borrar el cerito condemor se calle ustée no puedor a gramenawer a wan.
A wan no te digo trigo por no llamarte Rodrigor al ataquerl diodenoo la caidita está la cosa muy malar mamaar va usté muy cargadoo no te digo
trigo por no llamarte Rodrigor diodeno.
Se calle ustée caballo blanco caballo negroorl benemeritaar jarl amatomaa ahorarr pupita está la cosa muy malar.
ul
li text in paragraphs
li Qué dise usteer ese pedazo de no puedor te voy a borrar el cerito condemor se calle ustée no puedor a gramenawer a wan.
li Se calle ustée caballo blanco caballo negroorl benemeritaar jarl amatomaa ahorarr pupita está la cosa muy malar.
li text in paragraphs
li text in paragraphs
ol
li text in paragraphs
li Qué dise usteer ese pedazo de no puedor te voy a borrar el cerito condemor se calle ustée no puedor a gramenawer a wan.
li Se calle ustée caballo blanco caballo negroorl benemeritaar jarl amatomaa ahorarr pupita está la cosa muy malar.
li text in paragraphs
li text in paragraphs
p Se calle ustée caballo blanco caballo negroorl benemeritaar jarl amatomaa ahorarr pupita está la cosa muy malar.
a(href="") Benemeritaar torpedo diodenoo la caidita mamaar
blockquote
Pupita por la gloria de mi madre torpedo quietooor condemor apetecan.
pre.
.button {
color: $white;
display: block;
margin-bottom: .5rem;
text-align: center;
}
include views/modules/attachments
section.wiki-content.wysiwyg(tg-bind-html="wiki.html")
//include views/modules/attachments

View File

@ -0,0 +1,10 @@
.wysiwyg {
margin-bottom: 2rem;
textarea {
background: white;
max-height: none;
}
}
.save-wiki {
float: right;
}

View File

@ -1,3 +1,9 @@
.wiki-content {
margin-bottom: 2rem;
}
.edit-wiki {
float: right;
}
.delete-wiki {
float: right;
}

View File

@ -123,6 +123,7 @@ $prefix-for-spec: true;
@import 'layout/kanban';
@import 'layout/issues';
@import 'layout/wiki';
@import 'layout/wiki-edit';
//#################################################
// Shame

View File

@ -13,6 +13,9 @@
a {
display: block;
padding: 1rem 0 1rem 1rem;
span {
cursor: pointer;
}
}
.icon {
@include transition (opacity 1s linear);

View File

@ -46,6 +46,7 @@ paths = {
"app/coffee/modules/issues/*.coffee",
"app/coffee/modules/userstories/*.coffee",
"app/coffee/modules/tasks/*.coffee",
"app/coffee/modules/wiki/*.coffee",
"app/coffee/modules/admin/*.coffee",
"app/coffee/modules/locales/*.coffee",
"app/coffee/modules/base/*.coffee",