diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index e5d828bf..c212486b 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -188,6 +188,13 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven # Wiki $routeProvider.when("/project/:pslug/wiki", {redirectTo: (params) -> "/project/#{params.pslug}/wiki/home"}, ) + $routeProvider.when("/project/:pslug/wiki-list", + { + templateUrl: "wiki/wiki-list.html", + loader: true, + section: "wiki" + } + ) $routeProvider.when("/project/:pslug/wiki/:slug", { templateUrl: "wiki/wiki.html", diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index e593f3c6..75b0e23e 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -80,6 +80,7 @@ urls = { "project-issues-detail": "/project/:project/issue/:ref" "project-wiki": "/project/:project/wiki" + "project-wiki-list": "/project/:project/wiki-list" "project-wiki-page": "/project/:project/wiki/:slug" # Team diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 6f769312..8514be37 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -165,6 +165,41 @@ CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls)-> module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate", "$tgNavUrls", CreatedByDisplayDirective]) + +UserDisplayDirective = ($template, $compile, $translate, $navUrls)-> + # Display the user information (full name and photo). + # + # Example: + # div.creator(tg-user-display, tg-user-id="{{ user.id }}") + # + # Requirements: + # - model object must have the attributes 'created_date' and + # 'owner'(ng-model) + # - scope.usersById object is required. + + link = ($scope, $el, $attrs) -> + id = $attrs.tgUserId + console.log($scope.usersById[id]) + $scope.user = $scope.usersById[id] or { + full_name_display: $translate.instant("COMMON.EXTERNAL_USER") + photo: "/" + window._version + "/images/user-noimage.png" + } + + $scope.url = if $scope.owner?.is_active then $navUrls.resolve("user-profile", {username: $scope.owner.username}) else "" + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + scope: true, + templateUrl: "common/components/user-display.html" + } + +module.directive("tgUserDisplay", ["$tgTemplate", "$compile", "$translate", "$tgNavUrls", + UserDisplayDirective]) + ############################################################################# ## Watchers directive ############################################################################# diff --git a/app/coffee/modules/resources/wiki.coffee b/app/coffee/modules/resources/wiki.coffee index c333d9cb..8c7beee4 100644 --- a/app/coffee/modules/resources/wiki.coffee +++ b/app/coffee/modules/resources/wiki.coffee @@ -34,6 +34,9 @@ resourceProvider = ($repo, $http, $urls) -> service.getBySlug = (projectId, slug) -> return $repo.queryOne("wiki", "by_slug?project=#{projectId}&slug=#{slug}") + service.list = (projectId) -> + return $repo.queryMany("wiki", {project: projectId}) + service.listLinks = (projectId) -> return $repo.queryMany("wiki-links", {project: projectId}) diff --git a/app/coffee/modules/wiki/pages-list.coffee b/app/coffee/modules/wiki/pages-list.coffee new file mode 100644 index 00000000..5aaddb8c --- /dev/null +++ b/app/coffee/modules/wiki/pages-list.coffee @@ -0,0 +1,100 @@ +### +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino Garcia +# Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian +# +# 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 . +# +# File: modules/wiki/pages-list.coffee +### + +taiga = @.taiga + +mixOf = @.taiga.mixOf + +module = angular.module("taigaWiki") + +############################################################################# +## Wiki Pages List Controller +############################################################################# + +class WikiPagesListController extends mixOf(taiga.Controller, taiga.PageMixin) + @.$inject = [ + "$scope", + "$rootScope", + "$tgRepo", + "$tgModel", + "$tgConfirm", + "$tgResources", + "$routeParams", + "$q", + "$tgNavUrls", + "tgErrorHandlingService" + ] + + constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q, + @navUrls, @errorHandlingService) -> + @scope.projectSlug = @params.pslug + @scope.wikiSlug = @params.slug + @scope.wikiTitle = @scope.wikiSlug + @scope.sectionName = "Wiki" + @scope.linksVisible = false + + promise = @.loadInitialData() + + # On Error + promise.then null, @.onInitialDataError.bind(@) + + loadProject: -> + return @rs.projects.getBySlug(@params.pslug).then (project) => + if not project.is_wiki_activated + @errorHandlingService.permissionDenied() + + @scope.projectId = project.id + @scope.project = project + @scope.$emit('project:loaded', project) + return project + + loadWikiPages: -> + promise = @rs.wiki.list(@scope.projectId).then (wikipages) => + @scope.wikipages = wikipages + + loadWikiLinks: -> + return @rs.wiki.listLinks(@scope.projectId).then (wikiLinks) => + @scope.wikiLinks = wikiLinks + + for link in @scope.wikiLinks + link.url = @navUrls.resolve("project-wiki-page", { + project: @scope.projectSlug + slug: link.href + }) + + selectedWikiLink = _.find(wikiLinks, {href: @scope.wikiSlug}) + @scope.wikiTitle = selectedWikiLink.title if selectedWikiLink? + + loadInitialData: -> + promise = @.loadProject() + return promise.then (project) => + @.fillUsersAndRoles(project.members, project.roles) + @q.all([@.loadWikiLinks(), @.loadWikiPages()]).then @.checkLinksPerms.bind(this) + + checkLinksPerms: -> + if @scope.project.my_permissions.indexOf("add_wiki_link") != -1 || + (@scope.project.my_permissions.indexOf("view_wiki_links") != -1 && @scope.wikiLinks.length) + @scope.linksVisible = true + +module.controller("WikiPagesListController", WikiPagesListController) diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index 0263a4f1..687c484a 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -1460,12 +1460,22 @@ "DELETE_LINK_TITLE": "Delete Wiki link", "NAVIGATION": { "SECTION_NAME": "Links", - "ACTION_ADD_LINK": "Add link" + "ACTION_ADD_LINK": "Add link", + "ALL_PAGES": "All pages" }, "SUMMARY": { "TIMES_EDITED": "times
edited", "LAST_EDIT": "last
edit", "LAST_MODIFICATION": "last modification" + }, + "SECTION_PAGES_LIST": "All pages", + "PAGES_LIST_COLUMNS": { + "TITLE": "Title", + "EDITIONS": "Editions", + "CREATED": "Created", + "MODIFIED": "Modified", + "CREATOR": "Creator", + "LAST_MODIFIER": "Last modifier" } }, "HINTS": { diff --git a/app/partials/common/components/user-display.jade b/app/partials/common/components/user-display.jade new file mode 100644 index 00000000..e3263d3b --- /dev/null +++ b/app/partials/common/components/user-display.jade @@ -0,0 +1,13 @@ +.user-avatar + a( + href="{{url}}" + title="{{user.full_name_display}}" + ) + img( + src="{{user.photo}}" + alt="{{user.full_name_display}}" + ) +a.user-full-name( + href="{{url}}" + title="{{user.full_name_display}}" +) {{user.full_name_display}} diff --git a/app/partials/wiki/wiki-list.jade b/app/partials/wiki/wiki-list.jade new file mode 100644 index 00000000..cc5e71c0 --- /dev/null +++ b/app/partials/wiki/wiki-list.jade @@ -0,0 +1,44 @@ +doctype html + +div.wrapper(ng-controller="WikiPagesListController as ctrl", + ng-init="section='wiki'") + tg-project-menu + sidebar.menu-secondary.extrabar(ng-if="linksVisible") + section.wiki-nav(tg-wiki-nav, ng-model="wikiLinks") + section.main.wiki + header + h1 + span(tg-bo-bind="project.name") + span.green(translate="PROJECT.SECTION.WIKI") + span.date(translate="WIKI.SECTION_PAGES_LIST") + + section.wiki-pages-table.basic-table + .row.title + .title-field( + translate="WIKI.PAGES_LIST_COLUMNS.TITLE" + ) + .editions-field( + translate="WIKI.PAGES_LIST_COLUMNS.EDITIONS" + ) + .creator-field( + translate="WIKI.PAGES_LIST_COLUMNS.CREATOR" + ) + .created-field( + translate="WIKI.PAGES_LIST_COLUMNS.CREATED" + ) + .last-modifier-field( + translate="WIKI.PAGES_LIST_COLUMNS.LAST_MODIFIER" + ) + .modified-field( + translate="WIKI.PAGES_LIST_COLUMNS.MODIFIED" + ) + + div.row.table-main(ng-repeat="wikipage in wikipages track by wikipage.slug") + div.title-field + a(href="", tg-nav="project-wiki-page:project=project.slug,slug=wikipage.slug") + | {{wikipage.slug}} + div.editions-field {{wikipage.editions}} + div.creator-field(tg-user-display, tg-user-id="{{wikipage.owner}}") + div.created-field(tg-bo-bind="wikipage.created_date|momentFormat:'DD MMM YYYY HH:mm'") + div.last-modifier-field(tg-user-display, tg-user-id="{{wikipage.last_modifier}}") + div.modified-field(tg-bo-bind="wikipage.modified_date|momentFormat:'DD MMM YYYY HH:mm'") diff --git a/app/partials/wiki/wiki-nav.jade b/app/partials/wiki/wiki-nav.jade index 258afc42..7137e16f 100644 --- a/app/partials/wiki/wiki-nav.jade +++ b/app/partials/wiki/wiki-nav.jade @@ -1,5 +1,6 @@ header h1(translate="WIKI.NAVIGATION.SECTION_NAME") + a(href="", tg-nav="project-wiki-list:project=project.slug", translate="WIKI.NAVIGATION.ALL_PAGES") nav ul.sortable diff --git a/app/styles/layout/wiki.scss b/app/styles/layout/wiki.scss index 6c661125..36b3b20f 100644 --- a/app/styles/layout/wiki.scss +++ b/app/styles/layout/wiki.scss @@ -91,3 +91,93 @@ top: .4rem; } } + +.wiki-pages-table { + display: flex; + margin-bottom: 2rem; + &.empty { + display: none; + } + .row { + &:hover { + background: lighten($primary, 65%); + transition: background .2s ease-in; + } + .icon { + display: inline; + } + } + .title { + @include font-size(medium); + @include font-type(bold); + border-bottom: 1px solid $gray-light; + &:hover { + background: transparent; + } + div { + cursor: pointer; + } + .votes { + color: $gray; + } + } + .table-main { + @include font-size(small); + border-bottom: 1px solid darken($whitish, 4%); + } + .avatar { + align-items: center; + display: flex; + img { + width: 35px; + } + figcaption { + flex-basis: 60%; + flex-grow: 1; + margin-left: .5rem; + } + } + .title-field { + overflow: hidden; + padding-right: 1rem; + width: 100%; + a { + @include ellipsis(100%); + display: block; + } + span { + vertical-align: middle; + &:first-child { + margin-right: .5rem; + } + } + } + .editions-field, + .created-field, + .modified-field, + .creator-field , + .last-modifier-field { + flex-basis: 140px; + flex-grow: 1; + flex-shrink: 0; + padding: 0 1rem; + position: relative; + text-align: left; + } + .creator-field , + .last-modifier-field { + display: flex; + flex-basis: 180px; + flex-direction: row; + .user-avatar { + flex-grow: 0; + img { + height: 48px; + } + } + .user-full-name { + flex-grow: 1; + padding: .5rem; + } + } +}