Skip to content

Commit

Permalink
Merge branch 'release/0.106.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
sloria committed Apr 21, 2017
2 parents 49ad44f + ae68cc1 commit 3bdaa6e
Show file tree
Hide file tree
Showing 518 changed files with 1,998 additions and 36,196 deletions.
2 changes: 2 additions & 0 deletions .docker-compose.wb.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ OSFSTORAGE_PROVIDER_CONFIG_HMAC_SECRET=changeme

SERVER_CONFIG_ADDRESS=0.0.0.0
OSF_AUTH_CONFIG_API_URL=http://192.168.168.167:5000/api/v1/files/auth/

TASKS_CONFIG_BROKER_URL=amqp://guest:[email protected]:5672//
22 changes: 22 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@
Changelog
*********

0.106.0 (2017-04-21)
====================

- Registrations with 100+ nodes fail with content_length_mismatch error
- [Win 10] DropZone patch for Windows folder uploads (IE / Firefox)
- Submitting to OSF4M using deleted project name does not create new project
- Fangorn edit undefined
- API 502s when refreshing github metadata (was: TypeError: Can't compare datetime.datetime to NoneType)
- API 502s when requesting Github file metadata w/ version=2.2
- Search unhappy with some users
- Registries: "Withdrawn" label not appearing on newly-withdrawn registrations
- Retraction detail pages throw mithril mounting error
- Preprint Provider detail not 404-ing if not found
- Sitemap index datemod -> lastmod
- Cannot change GitHub branches on staging/staging3
- When project marked spam, update EZID DOI status to Unavailable
- Populate Fangorn text input on rename
- Allow UVA dataverse-specific Pop-up in Fangorn upon publishing dataset/verse
- Linked Registrations on Nodes in API
- OSF - Convert all Dropbox v1 API code/clients to v2 API before deadline
- Update dockerfile to support running registries locally

0.105.0 (2017-04-02)
====================

Expand Down
3 changes: 0 additions & 3 deletions MANIFEST.in

This file was deleted.

14 changes: 11 additions & 3 deletions README-docker-compose.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
- Ubuntu
- Add loopback alias
`sudo ifconfig lo:0 192.168.168.167 netmask 255.255.255.255 up`

- For persistance, add to /etc/network/interfaces...
Add lo:0 to auto line...
```auto lo lo:0```
Add stanza for lo:0...
```iface lo:0 inet static
address 192.168.168.167
netmask 255.255.255.255
Expand Down Expand Up @@ -93,6 +97,8 @@
wb:
volumes_from:
- container:wb-sync
...
```

- In `docker-sync.yml`:
Expand All @@ -106,6 +112,8 @@
sync_excludes_type: 'Name'
sync_excludes: ['.DS_Store', '*.pyc', '*.tmp', '.git', '.idea']
watch_excludes: ['.*\.DS_Store', '.*\.pyc', '.*\.tmp', '.*/\.git', '.*/\.idea']
...
```

Modifying these files will show up as changes in git. To avoid committing these files, run:
Expand Down Expand Up @@ -168,8 +176,8 @@ Ubuntu: Skip install of docker-sync, fswatch, and unison. instead...
- `$ docker-compose up -d mfr wb fakecas sharejs`
5. Run migrations and create preprint providers
- When starting with an empty database you will need to run migrations and populate preprint providers. See the [Running arbitrary commands](#running-arbitrary-commands) section below for instructions.
6. Start the OSF Web, API Server, and Preprints (Detached)
- `$ docker-compose up -d worker web api admin preprints`
6. Start the OSF Web, API Server, Preprints, and Registries (Detached)
- `$ docker-compose up -d worker web api admin preprints registries`
7. View the OSF at [http://localhost:5000](http://localhost:5000).


Expand All @@ -180,7 +188,7 @@ Ubuntu: Skip install of docker-sync, fswatch, and unison. instead...
```
$ docker-sync start
# Wait until you see "Nothing to do: replicas have not changed since last sync."
$ docker-compose up -d assets admin_assets mfr wb fakecas sharejs worker web api admin preprints
$ docker-compose up -d assets admin_assets mfr wb fakecas sharejs worker web api admin preprints registries
```

- To view the logs for a given container:
Expand Down
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,12 +456,6 @@ By default, Karma will run tests using a PhantomJS headless browser. You can run
$ inv karma -b Firefox
```

If you want to run cross browser tests with SauceLabs, use "sauce" parameter:

```bash
$ inv karma --sauce
```

#### Testing Addons

Addons tests are not run by default. To execute addons tests, run
Expand Down
21 changes: 21 additions & 0 deletions addons/base/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from framework.routing import process_rules
from framework.flask import app
from website import settings
from website.util import rubeus


def _is_image(filename):
Expand All @@ -28,6 +29,26 @@ def _is_image(filename):
)


def generic_root_folder(addon_short_name):
def _root_folder(node_settings, auth, **kwargs):
"""Return the Rubeus/HGrid-formatted response for the root folder only."""
# Quit if node settings does not have authentication
if not node_settings.has_auth or not node_settings.folder_id:
return None
node = node_settings.owner
root = rubeus.build_addon_root(
node_settings=node_settings,
name=node_settings.fetch_folder_name(),
permissions=auth,
nodeUrl=node.url,
nodeApiUrl=node.api_url,
private_key=kwargs.get('view_only', None),
)
return [root]
_root_folder.__name__ = '{0}_root_folder'.format(addon_short_name)
return _root_folder


class BaseAddonAppConfig(AppConfig):
name = 'addons.base'
label = 'addons_base'
Expand Down
22 changes: 1 addition & 21 deletions addons/base/generic_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from website.oauth.models import ExternalAccount

from website.util import permissions, rubeus
from website.util import permissions
from website.project.decorators import (
must_have_addon, must_be_addon_authorizer,
must_have_permission, must_not_be_registration,
Expand Down Expand Up @@ -67,26 +67,6 @@ def _folder_list(node_addon, **kwargs):
_folder_list.__name__ = '{0}_folder_list'.format(addon_short_name)
return _folder_list

def root_folder(addon_short_name):
def _root_folder(node_settings, auth, **kwargs):
"""Return the Rubeus/HGrid-formatted response for the root folder only."""
# Quit if node settings does not have authentication
if not node_settings.has_auth or not node_settings.folder_id:
return None
node = node_settings.owner
root = rubeus.build_addon_root(
node_settings=node_settings,
name=node_settings.fetch_folder_name(),
permissions=auth,
nodeUrl=node.url,
nodeApiUrl=node.api_url,
private_key=kwargs.get('view_only', None),
)
return [root]
_root_folder.__name__ = '{0}_root_folder'.format(addon_short_name)
return _root_folder


def get_config(addon_short_name, Serializer):
@must_be_logged_in
@must_have_addon(addon_short_name, 'node')
Expand Down
35 changes: 13 additions & 22 deletions addons/base/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,9 @@ def before_fork(self, node, user):
if hasattr(self, 'user_settings'):
if self.user_settings is None:
return (
u'Because you have not configured the authorization for this {addon} add-on, this '
u'{category} will not transfer your authentication to '
u'the forked {category}.'
u'Because you have not configured the {addon} add-on, your authentication will not be '
u'transferred to the forked {category}. You may authorize and configure the {addon} add-on '
u'in the new fork on the settings page.'
).format(
addon=self.config.full_name,
category=node.project_or_component,
Expand All @@ -489,7 +489,8 @@ def before_fork(self, node, user):
return (
u'Because the {addon} add-on has been authorized by a different '
u'user, forking it will not transfer authentication to the forked '
u'{category}.'
u'{category}. You may authorize and configure the {addon} add-on '
u'in the new fork on the settings page.'
).format(
addon=self.config.full_name,
category=node.project_or_component,
Expand All @@ -502,7 +503,7 @@ def after_fork(self, node, fork, user, save=True):
:param Node fork:
:param User user:
:param bool save:
:returns: Tuple of cloned settings and alert message
:returns: cloned settings
"""
clone = self.clone()
Expand All @@ -512,7 +513,7 @@ def after_fork(self, node, fork, user, save=True):
if save:
clone.save()

return clone, None
return clone

def before_register(self, node, user):
"""
Expand Down Expand Up @@ -653,6 +654,9 @@ def folder_path(self):
"BaseOAuthNodeSettings subclasses must expose a 'folder_path' property."
)

def fetch_folder_name(self):
return self.folder_name

@property
def nodelogger(self):
auth = None
Expand Down Expand Up @@ -798,9 +802,9 @@ def after_fork(self, node, fork, user, save=True):
"""After forking, copy user settings if the user is the one who authorized
the addon.
:return: A tuple of the form (cloned_settings, message)
:return: the cloned settings
"""
clone, _ = super(BaseOAuthNodeSettings, self).after_fork(
clone = super(BaseOAuthNodeSettings, self).after_fork(
node=node,
fork=fork,
user=user,
Expand All @@ -814,24 +818,11 @@ def after_fork(self, node, fork, user, save=True):
except (KeyError, AttributeError):
pass
clone.set_auth(self.external_account, user, metadata=metadata, log=False)
message = '{addon} authorization copied to forked {category}.'.format(
addon=self.config.full_name,
category=fork.project_or_component,
)
else:
clone.clear_settings()
message = (
u'{addon} authorization not copied to forked {category}. You may '
u'authorize this fork on the <u><a href="{url}">Settings</a></u> '
u'page.'
).format(
addon=self.config.full_name,
url=fork.web_url_for('node_setting'),
category=fork.project_or_component,
)
if save:
clone.save()
return clone, message
return clone

def before_register_message(self, node, user):
"""Return warning text to display if user auth will be copied to a
Expand Down
2 changes: 1 addition & 1 deletion addons/base/testing/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def test_user_is_owner_node_not_authorized_user_has_accounts(self):

def test_user_is_owner_node_authorized_user_is_not_owner(self):
self.node_settings.external_account = self.ExternalAccountFactory()
with mock.patch('website.addons.base.AddonOAuthUserSettingsBase.verify_oauth_access', return_value=True):
with mock.patch('addons.base.models.BaseOAuthUserSettings.verify_oauth_access', return_value=True):
self.user.external_accounts = []
assert_false(self.ser.user_is_owner)

Expand Down
9 changes: 2 additions & 7 deletions addons/base/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,25 +421,20 @@ def test_create_log(self):

def test_after_fork_by_authorized_user(self):
fork = ProjectFactory()
clone, message = self.node_settings.after_fork(
clone = self.node_settings.after_fork(
node=self.node, fork=fork, user=self.user_settings.owner
)
assert_equal(clone.user_settings, self.user_settings)

def test_after_fork_by_unauthorized_user(self):
fork = ProjectFactory()
user = UserFactory()
clone, message = self.node_settings.after_fork(
clone = self.node_settings.after_fork(
node=self.node, fork=fork, user=user,
save=True
)
assert_is(clone.user_settings, None)

def test_before_fork(self):
node = ProjectFactory()
message = self.node_settings.before_fork(node, self.user)
assert_true(message)

def test_before_remove_contributor_message(self):
message = self.node_settings.before_remove_contributor(
self.node, self.user)
Expand Down
1 change: 1 addition & 0 deletions addons/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ def get_auth(auth, **kwargs):
'callback_url': node.api_url_for(
('create_waterbutler_log' if not node.is_registration else 'registration_callbacks'),
_absolute=True,
_internal=True
),
}
}, settings.WATERBUTLER_JWT_SECRET, algorithm=settings.WATERBUTLER_JWT_ALGORITHM), WATERBUTLER_JWE_KEY)}
Expand Down
4 changes: 2 additions & 2 deletions addons/box/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from addons.base.apps import BaseAddonAppConfig
from addons.base.apps import BaseAddonAppConfig, generic_root_folder

from addons.box.views import box_root_folder
box_root_folder = generic_root_folder('box')

class BoxAddonAppConfig(BaseAddonAppConfig):

Expand Down
3 changes: 0 additions & 3 deletions addons/box/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,6 @@ def api(self):
def display_name(self):
return '{0}: {1}'.format(self.config.full_name, self.folder_id)

def fetch_folder_name(self):
return self.folder_name

def fetch_full_folder_path(self):
return self.folder_path

Expand Down
2 changes: 1 addition & 1 deletion addons/box/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def patch_client(target, mock_client=None):
Usage: ::
with patch_client('website.addons.box.views.BoxClient') as client:
with patch_client('addons.box.views.BoxClient') as client:
# test view that uses the box client.
"""
with mock.patch(target) as client_getter:
Expand Down
4 changes: 0 additions & 4 deletions addons/box/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,3 @@ def _set_folder(node_addon, folder, auth):
box_deauthorize_node = generic_views.deauthorize_node(
SHORT_NAME
)

box_root_folder = generic_views.root_folder(
SHORT_NAME
)
7 changes: 7 additions & 0 deletions addons/dataverse/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from framework.exceptions import HTTPError

from addons.dataverse import settings
from website.util.sanitize import strip_html

def _connect(host, token):
try:
Expand Down Expand Up @@ -112,3 +113,9 @@ def get_dataverse(connection, alias):
if connection is None:
return
return connection.get_dataverse(alias)


def get_custom_publish_text(connection):
if connection is None:
return ''
return strip_html(connection.get_custom_publish_text(), tags=['strong', 'li', 'ul'])
2 changes: 2 additions & 0 deletions addons/dataverse/static/dataverseFangornConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ var _dataverseItemButtons = {
var toPublish = both ? 'Dataverse and dataset' : 'dataset';
// Set the modal content to reflect the file's external host
var modalContent = [
// for now, only display custom publish text for UVA's dataverse
m('p.m-md', item.data.host == 'dataverse.lib.virginia.edu' ? m.trust(item.data.hostCustomPublishText) : ''),
m('p.m-md', both ? 'This dataset cannot be published until the ' + item.data.dataverse + ' Dataverse is published. ' : ''),
m('p.m-md', 'By publishing this ' + toPublish + ', all content will be made available through ' + host + ' using their internal privacy settings, regardless of your OSF project settings. '),
m('p.font-thick.m-md', both ? 'Do you want to publish this Dataverse AND this dataset?' : 'Are you sure you want to publish this dataset?')
Expand Down
8 changes: 6 additions & 2 deletions addons/dataverse/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,13 @@ def test_set_config_no_dataset(self, mock_connection):

class TestHgridViews(DataverseAddonTestCase, OsfTestCase, unittest.TestCase):

@mock.patch('addons.dataverse.views.client.get_custom_publish_text')
@mock.patch('addons.dataverse.views.client.connect_from_settings')
@mock.patch('addons.dataverse.views.client.get_files')
def test_dataverse_root_published(self, mock_files, mock_connection):
def test_dataverse_root_published(self, mock_files, mock_connection, mock_text):
mock_connection.return_value = create_mock_connection()
mock_files.return_value = ['mock_file']
mock_text.return_value = 'Do you want to publish?'

self.project.set_privacy('public')
self.project.save()
Expand Down Expand Up @@ -184,11 +186,13 @@ def test_dataverse_root_published(self, mock_files, mock_connection):
assert_true(res.json[0]['hasPublishedFiles'])
assert_equal(res.json[0]['version'], 'latest-published')

@mock.patch('addons.dataverse.views.client.get_custom_publish_text')
@mock.patch('addons.dataverse.views.client.connect_from_settings')
@mock.patch('addons.dataverse.views.client.get_files')
def test_dataverse_root_not_published(self, mock_files, mock_connection):
def test_dataverse_root_not_published(self, mock_files, mock_connection, mock_text):
mock_connection.return_value = create_mock_connection()
mock_files.return_value = []
mock_text.return_value = 'Do you want to publish?'

self.project.set_privacy('public')
self.project.save()
Expand Down
Loading

0 comments on commit 3bdaa6e

Please sign in to comment.