Skip to content

Commit

Permalink
sharing an entire project
Browse files Browse the repository at this point in the history
  • Loading branch information
williamstein authored and nicholasruhland committed Sep 1, 2015
1 parent 0e83790 commit 056a0ef
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 79 deletions.
61 changes: 27 additions & 34 deletions salvus/page/project_files.cjsx
Expand Up @@ -176,33 +176,24 @@ FileRow = rclass
@render_name_link(styles, name, ext)


render_public_file_info_popover : (currently_shared) ->
if currently_shared
<Popover title='This file is being shared publicly'>
<span style={wordWrap:'break-word'}>
Description: {@props.public_data.description}
</span>
</Popover>
else
<Popover title='This file used to be shared publicly'>
<span style={wordWrap:'break-word'}>
If you want to share this file again, select the checkbox and then click
the '<Icon name='share-square-o' /> Share...' button.
</span>
</Popover>
render_public_file_info_popover : ->
<Popover title='This file is being shared publicly'>
<span style={wordWrap:'break-word'}>
Description: {@props.public_data.description}
</span>
</Popover>

render_public_file_info : ->
if @props.public_data?
if @props.public_data? and @props.is_public
<span><span>&nbsp;</span>
<OverlayTrigger
trigger = 'click'
rootClose
overlay = {@render_public_file_info_popover(@props.is_public)} >
overlay = {@render_public_file_info_popover()} >
<Button
bsStyle = {if @props.is_public then 'info'}
bsStyle = 'info'
bsSize = 'xsmall'
onClick = {(e)->e.stopPropagation()}
style = {if not @props.is_public then (color:'#666',textDecoration:'line-through')}
>
<Icon name='bullhorn' /> <span className='hidden-xs'>Public</span>
</Button>
Expand Down Expand Up @@ -264,28 +255,22 @@ DirectoryRow = rclass
@props.actions.set_file_search('')
@props.actions.set_url_to_path(path)

render_public_directory_info_popover : (currently_shared) ->
if currently_shared
<Popover title='This folder is being shared publicly' style={wordWrap:'break-word'}>
Description: {@props.public_data.description}
</Popover>
else
<Popover title='This folder used to be shared publicly' style={wordWrap:'break-word'}>
If you want to share this folder again, click the checkbox and then click 'Share'
</Popover>
render_public_directory_info_popover : ->
<Popover title='This folder is being shared publicly' style={wordWrap:'break-word'}>
Description: {@props.public_data.description}
</Popover>

render_public_directory_info : ->
if @props.public_data?
if @props.public_data? and @props.is_public
<span><span>&nbsp;</span>
<OverlayTrigger
trigger = 'click'
rootClose
overlay = {@render_public_directory_info_popover(@props.is_public)} >
overlay = {@render_public_directory_info_popover()} >
<Button
bsStyle = {if @props.is_public then 'info'}
bsStyle = 'info'
bsSize = 'xsmall'
onClick = {(e)->e.stopPropagation()}
style = {if not @props.is_public then (color:'#666', textDecoration:'line-through')}
>
<Icon name='bullhorn' /> <span className='hidden-xs'>Public</span>
</Button>
Expand Down Expand Up @@ -378,7 +363,7 @@ FileListing = rclass

propTypes :
listing : rtypes.array.isRequired
file_map : rtypes.object
file_map : rtypes.object.isRequired
checked_files : rtypes.object
current_path : rtypes.string
page_number : rtypes.number
Expand Down Expand Up @@ -1020,7 +1005,13 @@ ProjectFilesActionBox = rclass
</div>

different_project_button : ->
<Button bsSize='xsmall' onClick={=>@setState(show_different_project : true)}>a different project</Button>
<Button
bsSize = 'xsmall'
onClick = {=>@setState(show_different_project : true)}
style = {padding:'0px 5px'}
>
a different project
</Button>

copy_click : ->
destination_directory = @state.copy_destination_directory
Expand Down Expand Up @@ -1234,7 +1225,9 @@ ProjectFilesActionBox = rclass
action = @props.file_action
action_button = file_action_buttons[action]
if not action_button?
<div>Undefined action</div>
return <div>Undefined action</div>
if not @props.file_map?
return <Loading />
else
<Well>
<Row>
Expand Down
2 changes: 1 addition & 1 deletion salvus/page/project_log.cjsx
Expand Up @@ -396,7 +396,7 @@ ProjectLog = rclass
<Button onClick={@previous_page} disabled={@props.page<=0} >
<Icon name='angle-double-left' /> Newer
</Button>
<Button disabled>{"#{cur_page + 1}/#{num_pages + 1}"}</Button>
<Button disabled>{"#{cur_page + 1}/#{num_pages}"}</Button>
<Button onClick={@next_page} disabled={@props.page>=num_pages-1} >
Older <Icon name='angle-double-right' />
</Button>
Expand Down
142 changes: 100 additions & 42 deletions salvus/page/project_settings.cjsx
Expand Up @@ -386,49 +386,100 @@ UsagePanel = rclass
</span>
</ProjectSettingsPanel>

ShareCopyPanel = rclass
displayName : 'ProjectSettings-ShareCopyPanel'
SharePanel = rclass
displayName : 'ProjectSettings-SharePanel'

propTypes :
project : rtypes.object.isRequired
flux : rtypes.object.isRequired
project : rtypes.object.isRequired
public_paths : rtypes.object.isRequired
flux : rtypes.object.isRequired
desc : rtypes.string.isRequired

getInitialState : ->
state : 'view' # view --> edit --> saving --> view
share_desc : @

render_share : ->
<Input
ref = 'share_description'
type = 'text'
placeholder = 'No description'
disabled = {@state.state == 'saving'}
onChange = {=>@setState(description_text:@refs.share_description.getValue())} />
state : 'view' # view --> edit --> view
desc : @props.desc

componentWillReceiveProps : (nextProps) ->
if @state.desc isnt nextProps.desc
@setState
desc : nextProps.desc
state : 'view'

cancel : ->
@setState(state : 'view')

save : ->
actions = @props.flux.getProjectActions(@props.project.get('project_id'))
actions.set_public_path('', @refs.share_project.getValue())
@setState(state : 'view')

render_share_cancel_buttons : ->
<ButtonToolbar style={paddingBottom:'5px'}>
<Button key='share' bsStyle='primary' onClick={@save}>
<Icon name='share-square-o' /> Share
</Button>
<Button key='cancel' onClick={@cancel}>Cancel</Button>
</ButtonToolbar>

render_update_desc_button: ->
<ButtonToolbar style={paddingBottom:'5px'}>
<Button key='share' bsStyle='primary' onClick={@save} disabled={@state.desc == @props.desc} >
<Icon name='share-square-o' /> Change description
</Button>
</ButtonToolbar>

render_share : (shared) ->
if @state.state == 'edit' or shared
<form onSubmit={(e)=>e.preventDefault(); @save()}>
<Input
ref = 'share_project'
type = 'text'
value = {@state.desc}
onChange = {=>@setState(desc : @refs.share_project.getValue())}
placeholder = 'Give a description...' />
{@render_share_cancel_buttons() if @state.state == 'edit'}
{@render_update_desc_button() if shared}
</form>

toggle_share : (shared) ->
actions = @props.flux.getProjectActions(@props.project.get('project_id'))
if shared
actions.disable_public_path('')
else
@setState(state : 'edit')

render_share_unshare_button : (shared) ->
<Button
bsStyle = {if shared then 'warning' else 'primary'}
onClick = {=>@toggle_share(shared)}
style = {float: 'right', marginBottom:'10px'}
>
<Icon name={if shared then 'shield' else 'share-square-o'} /> {if shared then 'Unshare' else 'Share'} Project...
</Button>

render : ->
if not @props.public_paths?
return <Loading />
project_id = @props.project.get('project_id')
shared = @props.flux.getStore('projects').get_public_paths(project_id)

<ProjectSettingsPanel title='Share or copy project' icon='share'>
project_store = @props.flux.getProjectStore(project_id)
id = project_store.get_public_path_id('')
shared = @props.public_paths.get(id)? and not @props.public_paths.get(id).get('disabled')
if shared
share_message = "This project is publicly shared, so anyone can see it."
else
share_message = "Share this project publicly. You can also share individual files or folders from the file listing."
<ProjectSettingsPanel title='Project sharing' icon='share'>
<Row>
<Col sm=8>
Share this project publicly. You can also share individual files or folders from the file listing.
{share_message}
</Col>
<Col sm=4>
<Button bsStyle='primary' onClick={@toggle_share} style={float: 'right'}>
<Icon name='share-square-o' /> {if shared then 'Share' else 'Unshare'} Project...
</Button>
{@render_share()}
{@render_share_unshare_button(shared) if @state.state == 'view'}
</Col>
</Row>
<hr />
<Row>
<Col sm=8>
Copy this entire project to a different project.
</Col>
<Col sm=4>
<Button bsStyle='primary' onClick={@copy_project} style={float: 'right'}>
<Icon name='copy' /> Copy to Project
</Button>
<Col sm=12>
{@render_share(shared)}
</Col>
</Row>
</ProjectSettingsPanel>
Expand Down Expand Up @@ -846,14 +897,16 @@ ProjectSettings = rclass
displayName : 'ProjectSettings-ProjectSettings'

propTypes :
project : rtypes.object.isRequired
user_map : rtypes.object.isRequired
flux : rtypes.object.isRequired

shouldComponentUpdate : (nextProps) ->
return @props.project != nextProps.project or @props.user_map != nextProps.user_map
project : rtypes.object.isRequired
user_map : rtypes.object.isRequired
flux : rtypes.object.isRequired
public_paths : rtypes.object.isRequired

render : ->
# get the description of the share, in case the project is being shared
store = @props.flux.getProjectStore(@props.project.get('project_id'))
share_desc = @props.public_paths.get(store.get_public_path_id('')).get('description') ? ''

<div>
{if @props.project.get('deleted') then <DeletedProjectWarning />}
<h1><Icon name='wrench' /> Settings and configuration</h1>
Expand All @@ -866,6 +919,8 @@ ProjectSettings = rclass
<Col sm=6>
<ProjectControlPanel key='control' project={@props.project} flux={@props.flux} />
<SageWorksheetPanel key='worksheet' project={@props.project} flux={@props.flux} />
<SharePanel key='share' project={@props.project}
flux={@props.flux} public_paths={@props.public_paths} desc={share_desc} />
<HideDeletePanel key='hidedelete' project={@props.project} flux={@props.flux} />
</Col>
</Row>
Expand All @@ -875,10 +930,11 @@ ProjectController = rclass
displayName : 'ProjectSettings-ProjectController'

propTypes :
project_map : rtypes.object
user_map : rtypes.object
project_id : rtypes.string.isRequired
flux : rtypes.object
project_map : rtypes.object
user_map : rtypes.object
project_id : rtypes.string.isRequired
public_paths : rtypes.object
flux : rtypes.object

getInitialState : ->
admin_project : undefined # used in case visitor to project is admin
Expand Down Expand Up @@ -908,7 +964,7 @@ ProjectController = rclass


render: ->
if not @props.flux? or not @props.project_map? or not @props.user_map?
if not @props.flux? or not @props.project_map? or not @props.user_map? or not @props.public_paths?
return <Loading />
user_map = @props.user_map
project = @props.project_map?.get(@props.project_id) ? @state.admin_project
Expand All @@ -924,14 +980,16 @@ ProjectController = rclass
else
<div>
{@render_admin_message() if @state.admin_project?}
<ProjectSettings flux={@props.flux} project_id={@props.project_id} project={project} user_map={@props.user_map} />
<ProjectSettings flux={@props.flux} project_id={@props.project_id} project={project} user_map={@props.user_map} public_paths={@props.public_paths} />
</div>

render = (project_id) ->
project_store = flux.getProjectStore(project_id)
connect_to =
project_map : 'projects'
user_map : 'users'
stripe_customer : 'account' # the QuotaConsole component depends on this in that it calls something in the account store!
public_paths : project_store.name
<Flux flux={flux} connect_to={connect_to} >
<ProjectController project_id={project_id} />
</Flux>
Expand Down
10 changes: 8 additions & 2 deletions salvus/page/project_store.coffee
Expand Up @@ -788,7 +788,8 @@ class ProjectActions extends Actions
@process_results(err, output, max_results, max_output, cmd)

class ProjectStore extends Store
_init : =>
_init : (project_id) =>
@project_id = project_id
ActionIds = @flux.getActionIds(@name)
@register(ActionIds.setTo, @setTo)
@_account_store = @flux.getStore('account')
Expand Down Expand Up @@ -919,6 +920,11 @@ class ProjectStore extends Store
if @state.public_paths?
return @_public_paths_cache ?= immutable.fromJS((misc.copy_without(x,['id','project_id']) for _,x of @state.public_paths.toJS()))

get_public_path_id: (path) =>
# (this exists because rethinkdb doesn't have compound primary keys)
{SCHEMA, client_db} = require('schema')
return SCHEMA.public_paths.user_query.set.fields.id({project_id:@project_id, path:path}, client_db)

_compute_public_files: (x) =>
listing = x.listing
pub = x.public
Expand Down Expand Up @@ -949,7 +955,7 @@ exports.getStore = getStore = (project_id, flux) ->
actions = flux.createActions(name, ProjectActions)
actions._init(project_id)
store = flux.createStore(name, ProjectStore)
store._init()
store._init(project_id)

queries = misc.deep_copy(QUERIES)

Expand Down

0 comments on commit 056a0ef

Please sign in to comment.