@@ -16,7 +16,7 @@ import { PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER,
1616import PERMISSIONS from '../../../config/permissions'
1717import { checkPermission } from '../../../helpers/permissions'
1818import ProjectInfo from '../../../components/ProjectInfo/ProjectInfo'
19- import {
19+ import {
2020 addProjectAttachment , updateProjectAttachment , uploadProjectAttachments , discardAttachments , changeAttachmentPermission ,
2121 removeProjectAttachment
2222} from '../../actions/projectAttachment'
@@ -38,6 +38,10 @@ class ProjectInfoContainer extends React.Component {
3838 this . onUploadAttachment = this . onUploadAttachment . bind ( this )
3939 this . removeAttachment = this . removeAttachment . bind ( this )
4040 this . onSubmitForReview = this . onSubmitForReview . bind ( this )
41+ this . extractLinksFromPosts = this . extractLinksFromPosts . bind ( this )
42+ this . extractMarkdownLink = this . extractMarkdownLink . bind ( this )
43+ this . extractHtmlLink = this . extractHtmlLink . bind ( this )
44+ this . extractRawLink = this . extractRawLink . bind ( this )
4145 }
4246
4347 shouldComponentUpdate ( nextProps , nextState ) { // eslint-disable-line no-unused-vars
@@ -156,13 +160,110 @@ class ProjectInfoContainer extends React.Component {
156160 updateProject ( project . id , { status : 'in_review' } )
157161 }
158162
163+ extractHtmlLink ( str ) {
164+ const links = [ ]
165+ const regex = / < a [ ^ > ] + h r e f = " ( .* ?) " [ ^ > ] * > ( [ \s \S ] * ?) < \/ a > / gm
166+ const urlRegex = / ^ (?: h t t p ( s ) ? : \/ \/ ) ? [ \w . - ] + (?: \. [ \w \. - ] + ) + [ \w \- \. _ ~ : / ? # [ \] @ ! \$ & ' \( \) \* \+ , ; = . ] + $ / gm // eslint-disable-line no-useless-escape
167+ const rawLinks = regex . exec ( str )
168+
169+ if ( Array . isArray ( rawLinks ) ) {
170+ let i = 0
171+ while ( i < rawLinks . length ) {
172+ const title = rawLinks [ i + 2 ]
173+ const address = rawLinks [ i + 1 ]
174+
175+ if ( urlRegex . test ( address ) ) {
176+ links . push ( {
177+ title,
178+ address
179+ } )
180+ }
181+
182+ i = i + 3
183+ }
184+ }
185+
186+ return links
187+ }
188+
189+ extractMarkdownLink ( str ) {
190+ const links = [ ]
191+ const regex = / (?: _ _ | [ * # ] ) | \[ ( .* ?) \] \( ( .* ?) \) / gm
192+ const urlRegex = / ^ (?: h t t p ( s ) ? : \/ \/ ) ? [ \w . - ] + (?: \. [ \w \. - ] + ) + [ \w \- \. _ ~ : / ? # [ \] @ ! \$ & ' \( \) \* \+ , ; = . ] + $ / gm // eslint-disable-line no-useless-escape
193+ const rawLinks = regex . exec ( str )
194+
195+ if ( Array . isArray ( rawLinks ) ) {
196+ let i = 0
197+ while ( i < rawLinks . length ) {
198+ const title = rawLinks [ i + 1 ]
199+ const address = rawLinks [ i + 2 ]
200+
201+ if ( urlRegex . test ( address ) ) {
202+ links . push ( {
203+ title,
204+ address
205+ } )
206+ }
207+
208+ i = i + 3
209+ }
210+ }
211+
212+ return links
213+ }
214+
215+ extractRawLink ( str ) {
216+ let links = [ ]
217+ const regex = / ( \s | ^ ) ( h t t p s ? : \/ \/ (?: w w w \. | (? ! w w w ) ) [ a - z A - Z 0 - 9 ] [ a - z A - Z 0 - 9 - ] + [ a - z A - Z 0 - 9 ] \. [ ^ \s ] { 2 , } | w w w \. [ a - z A - Z 0 - 9 ] [ a - z A - Z 0 - 9 - ] + [ a - z A - Z 0 - 9 ] \. [ ^ \s ] { 2 , } | h t t p s ? : \/ \/ (?: w w w \. | (? ! w w w ) ) [ a - z A - Z 0 - 9 ] + \. [ ^ \s ] { 2 , } | w w w \. [ a - z A - Z 0 - 9 ] + \. [ ^ \s ] { 2 , } [ \s ] ) ( \s | $ ) / igm // eslint-disable-line no-useless-escape
218+ const rawLinks = str . match ( regex )
219+
220+ if ( Array . isArray ( rawLinks ) ) {
221+ links = rawLinks
222+ . filter ( link => ! link . includes ( ']' ) )
223+ . map ( link => {
224+ const name = link . trim ( )
225+ const url = ! / ^ h t t p s ? : \/ \/ / i. test ( name ) ? 'http://' + name : name
226+
227+ return {
228+ title : name ,
229+ address : url
230+ }
231+ } )
232+ }
233+
234+ return links
235+ }
236+
237+ extractLinksFromPosts ( feeds ) {
238+ const links = [ ]
239+ feeds . forEach ( feed => {
240+ let childrenLinks = [ ]
241+ feed . posts . forEach ( post => {
242+ childrenLinks = childrenLinks . concat ( [
243+ ...this . extractHtmlLink ( post . rawContent ) ,
244+ ...this . extractMarkdownLink ( post . rawContent ) ,
245+ ...this . extractRawLink ( post . rawContent )
246+ ] )
247+ } )
248+
249+ if ( childrenLinks . length > 0 ) {
250+ links . push ( {
251+ title : feed . title ,
252+ children : childrenLinks
253+ } )
254+ }
255+ } )
256+
257+ return links
258+ }
259+
159260 render ( ) {
160261 const { duration } = this . state
161262 const { project, currentMemberRole, isSuperUser, phases, feeds,
162263 hideInfo, hideLinks, hideMembers, onChannelClick, activeChannelId, productsTimelines,
163264 isManageUser, phasesTopics, isProjectPlan, isProjectProcessing, projectTemplates,
164265 attachmentsAwaitingPermission, addProjectAttachment, discardAttachments, attachmentPermissions,
165- changeAttachmentPermission, projectMembers, loggedInUser, isSharingAttachment } = this . props
266+ changeAttachmentPermission, projectMembers, loggedInUser, isSharingAttachment, canAccessPrivatePosts } = this . props
166267 let directLinks = null
167268 // check if direct links need to be added
168269 const isMemberOrCopilot = _ . indexOf ( [ PROJECT_ROLE_COPILOT , PROJECT_ROLE_MANAGER ] , currentMemberRole ) > - 1
@@ -251,6 +352,20 @@ class ProjectInfoContainer extends React.Component {
251352 } )
252353 }
253354
355+ // extract links from posts
356+ const topicLinks = this . extractLinksFromPosts ( feeds )
357+ const publicTopicLinks = topicLinks . filter ( link => link . tag !== PROJECT_FEED_TYPE_MESSAGES )
358+ const privateTopicLinks = topicLinks . filter ( link => link . tag === PROJECT_FEED_TYPE_MESSAGES )
359+ const phaseLinks = this . extractLinksFromPosts ( phaseFeeds )
360+
361+ let links = [ ]
362+ links = links . concat ( project . bookmarks )
363+ links = links . concat ( publicTopicLinks )
364+ if ( canAccessPrivatePosts ) {
365+ links = links . concat ( privateTopicLinks )
366+ }
367+ links = links . concat ( phaseLinks )
368+
254369 return (
255370 < div >
256371 < div className = "sideAreaWrapper" >
@@ -300,7 +415,7 @@ class ProjectInfoContainer extends React.Component {
300415 }
301416 { ! hideLinks &&
302417 < LinksMenu
303- links = { project . bookmarks || [ ] }
418+ links = { links }
304419 canDelete = { canManageLinks }
305420 canEdit = { canManageLinks }
306421 canAdd = { canManageLinks }
0 commit comments