diff options
-rw-r--r-- | CHANGELOG.md | 11 | ||||
-rw-r--r-- | client/components/boards/boardColors.styl | 165 | ||||
-rw-r--r-- | client/components/lists/list.js | 24 | ||||
-rw-r--r-- | client/components/main/fonts.styl | 24 | ||||
-rw-r--r-- | client/components/main/layouts.styl | 2 | ||||
-rw-r--r-- | client/components/sidebar/sidebar.jade | 6 | ||||
-rw-r--r-- | client/components/sidebar/sidebar.js | 7 | ||||
-rw-r--r-- | client/lib/exportHTML.js | 206 | ||||
-rw-r--r-- | models/boards.js | 1 | ||||
-rw-r--r-- | package-lock.json | 34 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | public/fonts/poppins-bold.woff | bin | 0 -> 68016 bytes | |||
-rw-r--r-- | public/fonts/poppins-medium.woff | bin | 0 -> 68476 bytes | |||
-rw-r--r-- | public/fonts/poppins-regular.woff | bin | 0 -> 68608 bytes | |||
-rw-r--r-- | server/migrations.js | 1 |
15 files changed, 468 insertions, 14 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index cce2276e..69100810 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,16 @@ This release adds the following features: Thanks to willhseitz. - [Theme: Natural](https://github.com/wekan/wekan/pull/3098). You can select it from Board Settings / Change color / natural. - Thanks to compumatter and helioguardabaxo, + Thanks to compumatter and helioguardabaxo. +- [Theme: Modern](https://github.com/wekan/wekan/pull/3106). + Thanks to jeroenstoker com and helioguardabaxo. +- [Export board to HTML static page .zip archive](https://github.com/wekan/wekan/pull/3043). + Thanks to Lewiscowles1986. + +and fixes the following bugs: + +- [Change the swimlaneid of a card only if a new target swimlaneid is selected](https://github.com/wekan/wekan/pull/3108). + Thanks to marc1006. Thanks to above GitHub users for their contributions and translators for their translations. diff --git a/client/components/boards/boardColors.styl b/client/components/boards/boardColors.styl index 6b3994ff..0081143f 100644 --- a/client/components/boards/boardColors.styl +++ b/client/components/boards/boardColors.styl @@ -542,6 +542,169 @@ setBoardClear(color1,color2) .list-header background-color: #c9cfc3 border-bottom: 6px solid #c9cfc3 - + .swimlane .swimlane-header-wrap background-color: #c2c0ab + +/* + Alternate "Modern" Styling +*/ +.board-color-modern + setBoardColor(#2A80B8) + + /* General */ + body + background: #f5f5f5 + + &#header-quick-access + padding: 10px + font-size: 14px + background: #333 !important + + &#header-quick-access ul + overflow: visible + + &#header-quick-access ul li.current + border: 0 !important + font-weight: bold + + &#header-quick-access ul li.separator + display: none + + &#header-quick-access ul li:nth-child(3) + margin-right: 10px + + &#header-quick-access ul li a + padding: 5px 10px + border-radius: 2px + + &#header-quick-access ul li.current a + border-radius: 2px + background: rgba(255,255,255,.2) + + &#header #header-main-bar h1 + font-family: Poppins + font-weight: bold + &#header-quick-access #header-user-bar + position relative + + &#header-quick-access #header-user-bar .header-user-bar-name + margin: 5px 3px 0 0; + + section#notifications-drawer + top: 46px; + box-shadow: 0 4px 20px rgba(0,0,0,.1) + max-width: 100% + + section#notifications-drawer .header + top: 46px; + border-radius: 0 3px + height: 21px + background: #f7f7f7 + + /* Swimlane */ + .swimlane + background: #f5f5f5 + + .swimlane .swimlane-header-wrap .swimlane-header + font-family: Poppins + + /* All board views */ + .board-list .board-list-item + padding: 20px + + .board-list-item-name + font-family: Poppins + + /* Board */ + .list + background: transparent + border-left: 0 + margin: 10px 0 + padding: 0px + border-radius: 5px + min-width: 300px + + .list-body .open-minicard-composer:hover /*me*/ + background: none + box-shadow: none + + .list:first-child + margin-left: 5px + + .list.list-composer.js-list-composer + transition: all .3s ease + min-width: 80px + + .open-list-composer.js-open-inlined-form:hover + color: #222 + + .list-header + background: none + border-bottom-width: 0px + + .list-header .list-header-name + font-family: Poppins + color: #000 + font-weight: 500 + + /* Card changes */ + .minicard + background: #FFF + padding: 15px 15px 10px + box-shadow: 0 3px 8px rgba(0,0,0,.05) + + .minicard-plum:hover:not(.minicard-composer), .is-selected .minicard-plum, .draggable-hover-card .minicard-plum + background: none + + .minicard-title + line-height: 1.5em + + .minicard .minicard-cover + background-size: cover + margin: -15px -15px 10px + height: 100px + + .card-label-orange + color: #fff + + .card-date + font-size: 12px + padding: 3px 5px + + /* Pop over */ + .header-title + font-family: Poppins + font-size: 16px + color: #333 + + .pop-over + box-shadow: 0 4px 20px rgba(0,0,0,.1) + border: 0 + border-radius: 5px + + .pop-over .header + padding: 10px + border-bottom: 0 + border-radius: 5px 5px 0 0 + + .pop-over .content-container .content + padding: 5px 20px 20px + width: 260px + + .pop-over-list li > a + border-radius: 5px + + .pop-over-list li > a > i + margin-right: 5px + + .pop-over-list li>a .sub-name + margin-bottom: 8px + + /* Sidebar */ + .sidebar .sidebar-shadow + box-shadow: 0 0 60px rgba(0,0,0,.2) + + .sidebar .sidebar-content + padding: 30px + diff --git a/client/components/lists/list.js b/client/components/lists/list.js index 839304f8..5c315588 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -74,18 +74,16 @@ BlazeComponent.extendComponent({ const sortIndex = calculateIndex(prevCardDom, nextCardDom, nCards); const listId = Blaze.getData(ui.item.parents('.list').get(0))._id; const currentBoard = Boards.findOne(Session.get('currentBoard')); - let swimlaneId = ''; + const defaultSwimlaneId = currentBoard.getDefaultSwimline()._id; + let targetSwimlaneId = null; + + // only set a new swimelane ID if the swimlanes view is active if ( Utils.boardView() === 'board-view-swimlanes' || currentBoard.isTemplatesBoard() ) - swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id; - else if ( - Utils.boardView() === 'board-view-lists' || - Utils.boardView() === 'board-view-cal' || - !Utils.boardView - ) - swimlaneId = currentBoard.getDefaultSwimline()._id; + targetSwimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0)) + ._id; // Normally the jquery-ui sortable library moves the dragged DOM element // to its new position, which disrupts Blaze reactive updates mechanism @@ -98,9 +96,12 @@ BlazeComponent.extendComponent({ if (MultiSelection.isActive()) { Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => { + const newSwimlaneId = targetSwimlaneId + ? targetSwimlaneId + : card.swimlaneId || defaultSwimlaneId; card.move( currentBoard._id, - swimlaneId, + newSwimlaneId, listId, sortIndex.base + i * sortIndex.increment, ); @@ -108,7 +109,10 @@ BlazeComponent.extendComponent({ } else { const cardDomElement = ui.item.get(0); const card = Blaze.getData(cardDomElement); - card.move(currentBoard._id, swimlaneId, listId, sortIndex.base); + const newSwimlaneId = targetSwimlaneId + ? targetSwimlaneId + : card.swimlaneId || defaultSwimlaneId; + card.move(currentBoard._id, newSwimlaneId, listId, sortIndex.base); } boardComponent.setIsDragging(false); }, diff --git a/client/components/main/fonts.styl b/client/components/main/fonts.styl index fc8c8f00..5d6fb558 100644 --- a/client/components/main/fonts.styl +++ b/client/components/main/fonts.styl @@ -15,3 +15,27 @@ local('Roboto-Bold'), url('/fonts/roboto-bold.woff2') format('woff2'), url('/fonts/roboto-bold.woff') format('woff') + +@font-face + font-family: 'Poppins' + font-style: normal + font-weight: 400 + src: local('Poppins'), + local('Poppins-Regular'), + url('/fonts/poppins-regular.woff') format('woff') + +@font-face + font-family: 'Poppins' + font-style: normal + font-weight: 500 + src: local('Poppins Medium'), + local('Poppins-Medium'), + url('/fonts/poppins-medium.woff') format('woff') + +@font-face + font-family: 'Poppins' + font-style: normal + font-weight: 700 + src: local('Poppins Bold'), + local('Poppins-Bold'), + url('/fonts/poppins-bold.woff') format('woff') diff --git a/client/components/main/layouts.styl b/client/components/main/layouts.styl index 01ce2f16..85a5f1b2 100644 --- a/client/components/main/layouts.styl +++ b/client/components/main/layouts.styl @@ -32,7 +32,7 @@ a:hover,a:focus border-radius: unset html, body, input, select, textarea, button - font: 14px Roboto, "Helvetica Neue", Arial, Helvetica, sans-serif + font: 14px Roboto, Poppins, "Helvetica Neue", Arial, Helvetica, sans-serif line-height: 18px color: #4d4d4d diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 7d637142..04f2a8c2 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -363,7 +363,7 @@ template(name="boardMenuPopup") template(name="exportBoard") ul.pop-over-list li - a(href="{{exportUrl}}", download="{{exportJsonFilename}}") + a.download-json-link(href="{{exportUrl}}", download="{{exportJsonFilename}}") i.fa.fa-share-alt | {{_ 'export-board-json'}} li @@ -374,6 +374,10 @@ template(name="exportBoard") a(href="{{exportTsvUrl}}", download="{{exportTsvFilename}}") i.fa.fa-share-alt | {{_ 'export-board-tsv'}} + li + a.html-export-board + i.fa.fa-archive + | {{_ 'export-board-html'}} template(name="labelsWidget") .board-widget.board-widget-labels diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index 2c1cfd75..0e535041 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -463,6 +463,13 @@ BlazeComponent.extendComponent({ }, }).register('exportBoardPopup'); +Template.exportBoard.events({ + 'click .html-export-board': async event => { + event.preventDefault(); + await ExportHtml(Popup)(); + } +}); + Template.labelsWidget.events({ 'click .js-label': Popup.open('editLabel'), 'click .js-add-label': Popup.open('createLabel'), diff --git a/client/lib/exportHTML.js b/client/lib/exportHTML.js new file mode 100644 index 00000000..fe15b6aa --- /dev/null +++ b/client/lib/exportHTML.js @@ -0,0 +1,206 @@ +const JSZip = require('jszip'); + +window.ExportHtml = (Popup) => { + const saveAs = function(blob, filename) { + let dl = document.createElement('a'); + dl.href = window.URL.createObjectURL(blob); + dl.onclick = event => document.body.removeChild(event.target); + dl.style.display = 'none'; + dl.target = '_blank'; + dl.download = filename; + document.body.appendChild(dl); + dl.click(); + }; + + const asyncForEach = async function (array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array); + } + }; + + const getPageHtmlString = () => { + return `<!doctype html>${ + window.document.querySelector('html').outerHTML + }`; + }; + + const removeAnchors = htmlString => { + const replaceOpenAnchor = htmlString.replace(new RegExp('<a ', 'gim'), '<span '); + return replaceOpenAnchor.replace(new RegExp('<\/a', 'gim'), '</span'); + }; + + const ensureSidebarRemoved = () => { + document.querySelector('.board-sidebar.sidebar').remove(); + }; + + const addJsonExportToZip = async (zip, boardSlug) => { + const downloadJSONLink = document.querySelector('.download-json-link'); + const downloadJSONURL = downloadJSONLink.href; + const response = await fetch(downloadJSONURL); + const responseBody = await response.text(); + zip.file(`data/${boardSlug}.json`, responseBody); + }; + + const closeSidebar = () => { + document.querySelector('.board-header-btn.js-toggle-sidebar').click(); + }; + + const cleanBoardHtml = () => { + Array.from(document.querySelectorAll('script')).forEach(elem => + elem.remove(), + ); + Array.from( + document.querySelectorAll('link:not([rel="stylesheet"])'), + ).forEach(elem => elem.remove()); + document.querySelector('#header-quick-access').remove(); + Array.from( + document.querySelectorAll('#header-main-bar .board-header-btns'), + ).forEach(elem => elem.remove()); + Array.from(document.querySelectorAll('.list-composer')).forEach(elem => + elem.remove(), + ); + Array.from( + document.querySelectorAll( + '.list-composer,.js-card-composer, .js-add-card', + ), + ).forEach(elem => elem.remove()); + Array.from( + document.querySelectorAll('.js-perfect-scrollbar > div:nth-of-type(n+2)'), + ).forEach(elem => elem.remove()); + Array.from(document.querySelectorAll('.js-perfect-scrollbar')).forEach( + elem => { + elem.style = 'overflow-y: auto !important;'; + elem.classList.remove('js-perfect-scrollbar'); + }, + ); + Array.from(document.querySelectorAll('[href]:not(link)')).forEach(elem => + elem.attributes.removeNamedItem('href'), + ); + Array.from(document.querySelectorAll('[href]')).forEach(elem => { + // eslint-disable-next-line no-self-assign + elem.href = elem.href; + // eslint-disable-next-line no-self-assign + elem.src = elem.src; + }); + Array.from(document.querySelectorAll('.is-editable')).forEach(elem => { + elem.classList.remove('is-editable') + }) + + }; + + const getBoardSlug = () => { + return window.location.href.split('/').pop(); + }; + + const getStylesheetList = () => { + return Array.from( + document.querySelectorAll('link[href][rel="stylesheet"]'), + ); + }; + + const downloadStylesheets = async (stylesheets, zip) => { + await asyncForEach(stylesheets, async elem => { + const response = await fetch(elem.href); + const responseBody = await response.text(); + + const finalResponse = responseBody.replace( + new RegExp('packages\/[^\/]+\/upstream\/', 'gim'), '../' + ); + + const filename = elem.href + .split('/') + .pop() + .split('?') + .shift(); + const fileFullPath = `style/${filename}`; + zip.file(fileFullPath, finalResponse); + elem.href = `../${fileFullPath}`; + }); + }; + + const getSrcAttached = () => { + return Array.from(document.querySelectorAll('[src]')); + }; + + const downloadSrcAttached = async (elements, zip, boardSlug) => { + await asyncForEach(elements, async elem => { + const response = await fetch(elem.src); + const responseBody = await response.blob(); + const filename = elem.src + .split('/') + .pop() + .split('?') + .shift(); + const fileFullPath = `${boardSlug}/${elem.tagName.toLowerCase()}/${filename}`; + zip.file(fileFullPath, responseBody); + elem.src = `./${elem.tagName.toLowerCase()}/${filename}`; + }); + }; + + const removeCssUrlSurround = url => { + const working = url || ""; + return working + .split("url(") + .join("") + .split("\")") + .join("") + .split("\"") + .join("") + .split("')") + .join("") + .split("'") + .join("") + .split(")") + .join(""); + }; + + const getCardCovers = () => { + return Array.from(document.querySelectorAll('.minicard-cover')) + .filter(elem => elem.style['background-image']) + } + + const downloadCardCovers = async (elements, zip, boardSlug) => { + await asyncForEach(elements, async elem => { + const response = await fetch(removeCssUrlSurround(elem.style['background-image'])); + const responseBody = await response.blob(); + const filename = removeCssUrlSurround(elem.style['background-image']) + .split('/') + .pop() + .split('?') + .shift() + .split('#') + .shift(); + const fileFullPath = `${boardSlug}/covers/${filename}`; + zip.file(fileFullPath, responseBody); + elem.style = "background-image: url('" + `covers/${filename}` + "')"; + }); + }; + + const addBoardHTMLToZip = (boardSlug, zip) => { + ensureSidebarRemoved(); + const htmlOutputPath = `${boardSlug}/index.html`; + zip.file(htmlOutputPath, new Blob([ + removeAnchors(getPageHtmlString()) + ], { type: 'application/html' })); + }; + + return async () => { + const zip = new JSZip(); + const boardSlug = getBoardSlug(); + + await addJsonExportToZip(zip, boardSlug); + Popup.close(); + closeSidebar(); + cleanBoardHtml(); + + await downloadStylesheets(getStylesheetList(), zip); + await downloadSrcAttached(getSrcAttached(), zip, boardSlug); + await downloadCardCovers(getCardCovers(), zip, boardSlug); + + addBoardHTMLToZip(boardSlug, zip); + + const content = await zip.generateAsync({ type: 'blob' }); + saveAs(content, `${boardSlug}.zip`); + window.location.reload(); + } +}; diff --git a/models/boards.js b/models/boards.js index edfc7240..11d8fd89 100644 --- a/models/boards.js +++ b/models/boards.js @@ -257,6 +257,7 @@ Boards.attachSchema( 'corteza', 'clearblue', 'natural', + 'modern', ], // eslint-disable-next-line consistent-return autoValue() { diff --git a/package-lock.json b/package-lock.json index 9d156628..1e69d824 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2039,6 +2039,11 @@ "minimatch": "^3.0.4" } }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -2460,6 +2465,17 @@ "minimist": "^1.2.5" } }, + "jszip": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.4.0.tgz", + "integrity": "sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==", + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -2514,6 +2530,14 @@ "type-check": "~0.3.2" } }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, "lint-staged": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-7.3.0.tgz", @@ -3869,6 +3893,11 @@ "path-to-regexp": "~1.2.1" } }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "papaparse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.2.0.tgz", @@ -4330,6 +4359,11 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", diff --git a/package.json b/package.json index 1b4d3e8a..b049a12d 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "es6-promise": "^4.2.4", "flatted": "^2.0.1", "gridfs-stream": "^0.5.3", + "jszip": "^3.4.0", "ldapjs": "^1.0.2", "meteor-node-stubs": "^0.4.1", "mongodb": "^3.5.7", diff --git a/public/fonts/poppins-bold.woff b/public/fonts/poppins-bold.woff Binary files differnew file mode 100644 index 00000000..cc7e8447 --- /dev/null +++ b/public/fonts/poppins-bold.woff diff --git a/public/fonts/poppins-medium.woff b/public/fonts/poppins-medium.woff Binary files differnew file mode 100644 index 00000000..b8ccafeb --- /dev/null +++ b/public/fonts/poppins-medium.woff diff --git a/public/fonts/poppins-regular.woff b/public/fonts/poppins-regular.woff Binary files differnew file mode 100644 index 00000000..1ff39605 --- /dev/null +++ b/public/fonts/poppins-regular.woff diff --git a/server/migrations.js b/server/migrations.js index ccf875df..5655bd1d 100644 --- a/server/migrations.js +++ b/server/migrations.js @@ -121,6 +121,7 @@ Migrations.add('use-css-class-for-boards-colors', () => { '#568BA2': 'corteza', '#499BEA': 'clearblue', '#596557': 'natural', + '#2A80B8': 'modern', }; Boards.find().forEach(board => { const oldBoardColor = board.background.color; |