diff --git a/dispatch/api/exceptions.py b/dispatch/api/exceptions.py index 23ba2066d..9af4b5603 100644 --- a/dispatch/api/exceptions.py +++ b/dispatch/api/exceptions.py @@ -16,3 +16,11 @@ class InvalidGalleryAttachments(APIException): class BadCredentials(APIException): status_code = status.HTTP_400_BAD_REQUEST default_detail = 'Invalid user credentials' + +class PollClosed(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = 'Poll closed' + +class InvalidPoll(APIException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = 'Invalid poll' diff --git a/dispatch/api/serializers.py b/dispatch/api/serializers.py index 15b501984..7f1c1aac8 100644 --- a/dispatch/api/serializers.py +++ b/dispatch/api/serializers.py @@ -3,10 +3,8 @@ from rest_framework.validators import UniqueValidator from dispatch.modules.content.models import ( - Article, Image, ImageAttachment, ImageGallery, - Issue, File, Page, Author, Section, Tag, Topic, - Video, VideoAttachment) - + Article, Image, ImageAttachment, ImageGallery, Issue, + File, Page, Author, Section, Tag, Topic, Video, VideoAttachment, Poll, PollAnswer, PollVote) from dispatch.modules.auth.models import Person, User from dispatch.api.mixins import DispatchModelSerializer, DispatchPublishableSerializer @@ -746,3 +744,102 @@ def update(self, instance, validated_data): instance.save(validated_data) return instance + +class PollVoteSerializer(DispatchModelSerializer): + """Serializes the PollVote model""" + + answer_id = serializers.IntegerField(write_only=True) + + class Meta: + + model = PollVote + fields = ( + 'id', + 'answer_id', + ) + +class PollAnswerSerializer(DispatchModelSerializer): + """Serializes the PollAnswer model""" + + poll_id = serializers.IntegerField(write_only=True) + vote_count = serializers.SerializerMethodField() + + class Meta: + model = PollAnswer + fields = ( + 'id', + 'name', + 'vote_count', + 'poll_id' + ) + + def get_vote_count(self, obj): + vote_count = 0 + poll = Poll.objects.get(id=obj.poll_id) + + if self.is_authenticated() or poll.show_results: + vote_count = obj.get_vote_count() + + return vote_count + +class PollSerializer(DispatchModelSerializer): + """Serializes the Poll model.""" + + answers = serializers.SerializerMethodField() + answers_json = JSONField( + required=False, + write_only=True + ) + total_votes = serializers.SerializerMethodField() + question = serializers.CharField(required=True) + name = serializers.CharField(required=True) + + class Meta: + model = Poll + fields = ( + 'id', + 'is_open', + 'show_results', + 'name', + 'question', + 'answers', + 'answers_json', + 'total_votes' + ) + + def get_total_votes(self,obj): + total_votes = 0 + + if self.is_authenticated() or obj.show_results: + total_votes = obj.get_total_votes() + + return total_votes + + def get_answers(self, obj): + answers = PollAnswer.objects.filter(poll_id=obj.id) + serializer = PollAnswerSerializer(answers, many=True, context=self.context) + return serializer.data + + def create(self, validated_data): + # Create new ImageGallery instance + instance = Poll() + + # Then save as usual + return self.update(instance, validated_data, True) + + def update(self, instance, validated_data, is_new=False): + # Update all the basic fields + instance.question = validated_data.get('question', instance.question) + instance.name = validated_data.get('name', instance.name) + instance.is_open = validated_data.get('is_open', instance.is_open) + instance.show_results = validated_data.get('show_results', instance.show_results) + # Save instance before processing/saving content in order to + # save associations to correct ID + instance.save() + + answers = validated_data.get('answers_json') + + if isinstance(answers, list): + instance.save_answers(answers, is_new) + + return instance diff --git a/dispatch/api/urls.py b/dispatch/api/urls.py index b633e5383..442ff9569 100644 --- a/dispatch/api/urls.py +++ b/dispatch/api/urls.py @@ -24,6 +24,7 @@ router.register(r'zones', views.ZoneViewSet, base_name='api-zones') router.register(r'token', views.TokenViewSet, base_name='api-token') router.register(r'videos', views.VideoViewSet, base_name='api-videos') +router.register(r'polls', views.PollViewSet, base_name='api-polls') dashboard_recent_articles = views.DashboardViewSet.as_view({'get': 'list_recent_articles'}) dashboard_user_actions = views.DashboardViewSet.as_view({'get': 'list_actions'}) diff --git a/dispatch/api/views.py b/dispatch/api/views.py index f47f7f3db..7a2f35193 100644 --- a/dispatch/api/views.py +++ b/dispatch/api/views.py @@ -1,5 +1,6 @@ from django.db.models import Q, ProtectedError, Prefetch from django.contrib.auth import authenticate +from django.db import IntegrityError from rest_framework import viewsets, mixins, filters, status from rest_framework.response import Response @@ -14,14 +15,15 @@ from dispatch.models import ( Article, File, Image, ImageAttachment, ImageGallery, Issue, - Page, Author, Person, Section, Tag, Topic, User, Video) + Page, Author, Person, Section, Tag, Topic, User, Video, Poll, PollAnswer, PollVote) from dispatch.api.mixins import DispatchModelViewSet, DispatchPublishableMixin from dispatch.api.serializers import ( ArticleSerializer, PageSerializer, SectionSerializer, ImageSerializer, FileSerializer, IssueSerializer, ImageGallerySerializer, TagSerializer, TopicSerializer, PersonSerializer, UserSerializer, - IntegrationSerializer, ZoneSerializer, WidgetSerializer, TemplateSerializer, VideoSerializer) -from dispatch.api.exceptions import ProtectedResourceError, BadCredentials + IntegrationSerializer, ZoneSerializer, WidgetSerializer, TemplateSerializer, VideoSerializer, PollSerializer, + PollVoteSerializer ) +from dispatch.api.exceptions import ProtectedResourceError, BadCredentials, PollClosed, InvalidPoll from dispatch.theme import ThemeManager from dispatch.theme.exceptions import ZoneNotFound, TemplateNotFound @@ -163,7 +165,7 @@ def get_queryset(self): if q is not None: # If a search term (q) is present, filter queryset by term against `name` queryset = queryset.filter(name__icontains=q) - + return queryset class TopicViewSet(DispatchModelViewSet): @@ -248,6 +250,43 @@ def get_queryset(self): queryset = queryset.filter(title__icontains=q) return queryset +class PollViewSet(DispatchModelViewSet): + """Viewset for the Poll model views.""" + model = Poll + serializer_class = PollSerializer + + def get_queryset(self): + queryset = Poll.objects.all() + q = self.request.query_params.get('q', None) + if q is not None: + queryset = queryset.filter(Q(name__icontains=q) | Q(question__icontains=q) ) + return queryset + + @detail_route(permission_classes=[AllowAny], methods=['post'],) + def vote(self, request, pk=None): + poll = get_object_or_404(Poll.objects.all(), pk=pk) + + if not poll.is_open: + raise PollClosed() + + answer = get_object_or_404(PollAnswer.objects.all(), pk=request.data['answer_id']) + + if answer.poll != poll: + raise InvalidPoll() + + # Change vote + if 'vote_id' in request.data: + vote_id = request.data['vote_id'] + vote = PollVote.objects.filter(answer__poll=poll, id=vote_id).update(answer=answer) + return Response({'id': vote_id}) + + serializer = PollVoteSerializer(data=request.data) + + serializer.is_valid(raise_exception=True) + serializer.save() + + return Response(serializer.data) + class TemplateViewSet(viewsets.GenericViewSet): """Viewset for Template views""" permission_classes = (IsAuthenticated,) diff --git a/dispatch/migrations/0012_polls.py b/dispatch/migrations/0012_polls.py new file mode 100644 index 000000000..d6e249912 --- /dev/null +++ b/dispatch/migrations/0012_polls.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2018-06-04 19:15 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('dispatch', '0011_article_featured_video'), + ] + + operations = [ + migrations.CreateModel( + name='Poll', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('question', models.CharField(max_length=255)), + ('is_open', models.BooleanField(default=True)), + ('show_results', models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name='PollAnswer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('poll', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='dispatch.Poll')), + ], + ), + migrations.CreateModel( + name='PollVote', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='votes', to='dispatch.PollAnswer')), + ], + ), + ] diff --git a/dispatch/modules/content/embeds.py b/dispatch/modules/content/embeds.py index 70df64a93..72ff401fc 100644 --- a/dispatch/modules/content/embeds.py +++ b/dispatch/modules/content/embeds.py @@ -83,6 +83,31 @@ class AdvertisementEmbed(AbstractTemplateEmbed): class PullQuoteEmbed(AbstractTemplateEmbed): TEMPLATE = 'embeds/quote.html' +class WidgetEmbed(AbstractEmbed): + @classmethod + def render(self, data): + + from dispatch.theme import ThemeManager + from dispatch.theme.exceptions import ZoneNotFound, WidgetNotFound + + try: + widget_id = data['widget_id'] + widget = ThemeManager.Widgets.get(widget_id) + except: + return '' + + return widget.render(data=data['data']) + + # except ZoneNotFound: + # return '' + + # try: + # return zone.widget.render(add_context=kwargs) + # except (WidgetNotFound, AttributeError): + # pass + + # return '' + class ImageEmbed(AbstractTemplateEmbed): TEMPLATE = 'embeds/image.html' @@ -135,6 +160,7 @@ def prepare_data(self, data): 'size': len(images) } +embeds.register('widget', WidgetEmbed) embeds.register('quote', PullQuoteEmbed) embeds.register('code', CodeEmbed) embeds.register('advertisement', AdvertisementEmbed) diff --git a/dispatch/modules/content/models.py b/dispatch/modules/content/models.py index 020332adc..ff687a7d9 100644 --- a/dispatch/modules/content/models.py +++ b/dispatch/modules/content/models.py @@ -7,10 +7,12 @@ from PIL import Image as Img from django.db import IntegrityError +from django.db import transaction + from django.db.models import ( Model, DateTimeField, CharField, TextField, PositiveIntegerField, ImageField, FileField, BooleanField, UUIDField, ForeignKey, - ManyToManyField, SlugField, SET_NULL) + ManyToManyField, SlugField, SET_NULL, CASCADE) from django.conf import settings from django.core.validators import MaxValueValidator from django.utils import timezone @@ -222,7 +224,7 @@ def save_featured_image(self, data): if data is None: if attachment: attachment.delete() - + self.featured_image = None return @@ -553,3 +555,43 @@ class Issue(Model): volume = PositiveIntegerField(null=True) issue = PositiveIntegerField(null=True) date = DateTimeField() + +class Poll(Model): + name = CharField(max_length=255) + question = CharField(max_length=255) + is_open = BooleanField(default=True) + show_results = BooleanField(default=True) + + @transaction.atomic + def save_answers(self, answers, is_new): + if not is_new: + self.delete_old_answers(answers) + for answer in answers: + try: + answer_id = answer.get('id') + answer_obj = PollAnswer.objects.get(poll=self, id=answer_id) + answer_obj.name = answer['name'] + except PollAnswer.DoesNotExist: + answer_obj = PollAnswer(poll=self, name=answer['name']) + answer_obj.save() + + def delete_old_answers(self, answers): + PollAnswer.objects.filter(poll=self) \ + .exclude(id__in=[answer.get('id', 0) for answer in answers]) \ + .delete() + + def get_total_votes(self): + return PollVote.objects.filter(answer__poll=self).count() + +class PollAnswer(Model): + poll = ForeignKey(Poll, related_name='answers', on_delete=CASCADE) + name = CharField(max_length=255) + + def get_vote_count(self): + """Return the number of votes for this answer""" + return PollVote.objects.filter(answer=self).count() + +class PollVote(Model): + id = UUIDField(default=uuid.uuid4, primary_key=True) + answer = ForeignKey(PollAnswer, related_name='votes', on_delete=CASCADE) + timestamp = DateTimeField(auto_now_add=True) diff --git a/dispatch/static/manager/.eslintrc.js b/dispatch/static/manager/.eslintrc.js index d47e6ac51..9f7bce6a8 100644 --- a/dispatch/static/manager/.eslintrc.js +++ b/dispatch/static/manager/.eslintrc.js @@ -19,9 +19,18 @@ module.exports = { "semi": ["error", "never"], "no-unused-vars": ["warn", {"args": "after-used"}], "no-console": ["warn"], + "keyword-spacing": 2, "react/prop-types": 0, + "react/no-find-dom-node": 0, + "react/self-closing-comp": 2, "react/jsx-indent-props": ["error", 2], - "react/no-find-dom-node": 0 + "react/jsx-first-prop-new-line": ["error", "multiline-multiprop"], + "react/jsx-closing-bracket-location": ["error", "after-props"], + "react/jsx-curly-spacing": 2, + "react/no-string-refs": 0, + "react/jsx-props-no-multi-spaces": 2, + "react/jsx-space-before-closing": 2, + "react/no-deprecated": 0, }, "plugins": [ "react" diff --git a/dispatch/static/manager/package.json b/dispatch/static/manager/package.json index 34de41ea1..9f85d33fa 100644 --- a/dispatch/static/manager/package.json +++ b/dispatch/static/manager/package.json @@ -5,14 +5,26 @@ "author": "Peter Siemens ", "homepage": "https://www.ubyssey.ca", "contributors": [ - "Ben Cook " + "Rowan Baker-French", + "Jonathan Chapple", + "Ben Cook ", + "Brandon Forys", + "Ben Holmes", + "Axel Jacobsen", + "Jamie Lee", + "Pippin Lee", + "Benny Lo", + "Arash Outadi", + "Jordan Schalm", + "Atsushi Yamamoto", + "Ron Yong" ], "dependencies": { "@blueprintjs/core": "1.35.7", "@blueprintjs/datetime": "^1.16.0", "babel": "^6.23.0", "babel-core": "^6.21.0", - "babel-loader": "^6.2.10", + "babel-loader": "^7.1.4", "babel-preset-es2015": "^6.13.2", "babel-preset-react": "^6.11.1", "class-autobind": "^0.1.4", @@ -21,37 +33,38 @@ "es6-promise": "^3.2.1", "eslint": "^3.17.1", "eslint-loader": "^1.6.3", - "eslint-plugin-react": "^6.10.0", + "eslint-plugin-react": "^7.9.1", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^0.10.0", - "history": "^2.1.2", + "history": "^3.0.0", "immutable": "^3.8.1", "isomorphic-fetch": "^2.2.1", "jquery": "^3.1.1", "js-cookie": "^2.1.2", "moment": "^2.17.1", - "node-sass": "^4.5.0", + "node-sass": "^4.9.0", "normalizr": "^2.2.1", "qwery": "^4.0.0", "ramda": "^0.22.1", - "react": "^15.4.2", - "react-ace": "^5.0.1", + "react": "^15.3.2", + "react-ace": "^6.1.1", "react-addons-css-transition-group": "^15.4.1", - "react-dnd": "^2.4.0", - "react-dnd-html5-backend": "^2.4.1", + "react-day-picker": "^7.1.9", + "react-dnd": "^2.5.1", + "react-dnd-html5-backend": "^2.5.1", "react-document-title": "^2.0.2", - "react-dom": "^15.3.1", - "react-dropzone": "^3.12.3", - "react-measure": "^1.4.7", + "react-dom": "^15.3.2", + "react-dropzone": "^4.2.11", + "react-measure": "^2.0.2", "react-redux": "^4.4.5", - "react-redux-loading-bar": "^2.7.2", - "react-router": "^2.7.0", - "react-router-redux": "^4.0.5", + "react-redux-loading-bar": "^4.0.5", + "react-router": "^3.2.1", + "react-router-redux": "^4.0.8", "redux": "^3.5.2", "redux-logger": "^3.0.6", "redux-promise-middleware": "^4.0.0", "redux-thunk": "^2.1.0", - "sass-loader": "^6.0.2", + "sass-loader": "^7.0.3", "style-loader": "^0.13.1", "url": "^0.11.0", "url-loader": "^0.5.7", diff --git a/dispatch/static/manager/src/js/actions/PollsActions.js b/dispatch/static/manager/src/js/actions/PollsActions.js new file mode 100644 index 000000000..d78f94dbf --- /dev/null +++ b/dispatch/static/manager/src/js/actions/PollsActions.js @@ -0,0 +1,30 @@ +import { push } from 'react-router-redux' + +import * as types from '../constants/ActionTypes' +import { pollSchema } from '../constants/Schemas' + +import DispatchAPI from '../api/dispatch' + +import { ResourceActions } from '../util/redux' + +class PollsActions extends ResourceActions { + + search(query) { + let queryObj = {} + + if (query) { + queryObj.q = query + } + + return dispatch => { + dispatch(push({ pathname: '/polls/', query: queryObj })) + } + } + +} + +export default new PollsActions ( + types.POLLS, + DispatchAPI.polls, + pollSchema +) diff --git a/dispatch/static/manager/src/js/api/dispatch.js b/dispatch/static/manager/src/js/api/dispatch.js index deafdc00e..daf9f8748 100644 --- a/dispatch/static/manager/src/js/api/dispatch.js +++ b/dispatch/static/manager/src/js/api/dispatch.js @@ -15,7 +15,7 @@ function prepareMultipartPayload(payload) { if (payload[key] && payload[key].constructor === File) { formData.append(key, payload[key]) } else if (typeof payload[key] !== 'undefined') { - if(payload[key] === null) { + if (payload[key] === null) { formData.append(key, '') } else { @@ -78,7 +78,7 @@ function getRequest(route, id=null, query={}, token=null) { headers: buildHeaders(token) } ) - .then(parseJSON) + .then(parseJSON) } function getPageRequest(uri, token=null) { @@ -89,7 +89,7 @@ function getPageRequest(uri, token=null) { headers: buildHeaders(token) } ) - .then(parseJSON) + .then(parseJSON) } function postRequest(route, id=null, payload={}, token=null) { @@ -101,7 +101,7 @@ function postRequest(route, id=null, payload={}, token=null) { body: JSON.stringify(payload) } ) - .then(parseJSON) + .then(parseJSON) } function postMultipartRequest(route, id=null, payload={}, token=null) { @@ -113,7 +113,7 @@ function postMultipartRequest(route, id=null, payload={}, token=null) { body: prepareMultipartPayload(payload) } ) - .then(parseJSON) + .then(parseJSON) } function patchMultipartRequest(route, id=null, payload={}, token=null) { @@ -125,7 +125,7 @@ function patchMultipartRequest(route, id=null, payload={}, token=null) { body: prepareMultipartPayload(payload) } ) - .then(parseJSON) + .then(parseJSON) } function deleteRequest(route, id=null, payload={}, token=null) { @@ -137,7 +137,7 @@ function deleteRequest(route, id=null, payload={}, token=null) { body: JSON.stringify(payload) } ) - .then(handleError) + .then(handleError) } function patchRequest(route, id=null, payload={}, token=null) { @@ -149,7 +149,7 @@ function patchRequest(route, id=null, payload={}, token=null) { body: JSON.stringify(payload) } ) - .then(parseJSON) + .then(parseJSON) } const DispatchAPI = { @@ -434,6 +434,24 @@ const DispatchAPI = { save: (token, userId, data) => { return patchRequest('users', userId, data, token) } + }, + 'polls': { + list: (token, query) => { + return getRequest('polls', null, query, token) + }, + get: (token, pollId) => { + return getRequest('polls', pollId, null, token) + }, + save: (token, pollId, data) => { + return patchRequest('polls', pollId, data, token) + }, + create: (token, data) => { + return postRequest('polls', null, data, token) + }, + delete: (token, pollId) => { + return deleteRequest('polls', pollId, null, token) + }, + } } diff --git a/dispatch/static/manager/src/js/components/ArticleEditor/ArticleContentEditor.js b/dispatch/static/manager/src/js/components/ArticleEditor/ArticleContentEditor.js index 705741e68..af8385739 100644 --- a/dispatch/static/manager/src/js/components/ArticleEditor/ArticleContentEditor.js +++ b/dispatch/static/manager/src/js/components/ArticleEditor/ArticleContentEditor.js @@ -9,6 +9,7 @@ import { PullQuoteEmbed, GalleryEmbed, CodeEmbed, + WidgetEmbed } from '../../vendor/dispatch-editor' const embeds = [ @@ -17,12 +18,13 @@ const embeds = [ CodeEmbed, PullQuoteEmbed, GalleryEmbed, + WidgetEmbed ] export default class ArticleContentEditor extends React.Component { render() { return ( -
+
- Basic fields - Featured image - Featured video - Delivery - Template - SEO + Basic fields + Featured image + {/* Featured video */} + Delivery + Template + SEO @@ -43,12 +43,13 @@ export default function ArticleSidebar(props) { entities={props.entities} /> - + {/* uncomment when featured videos are ready */} + {/* - + */} props.publishArticle()} disabled={props.isNew}> - Publish + Publish ) @@ -21,7 +21,7 @@ export default function ArticleToolbar(props) { intent={Intent.PRIMARY} onClick={() => props.unpublishArticle()} disabled={props.isNew}> - Unpublish + Unpublish ) @@ -32,13 +32,13 @@ export default function ArticleToolbar(props) { props.saveArticle()}> - Update + Update {props.article.is_published ? unpublish : publish} props.previewArticle()}> - Preview + Preview props.update('slug', e.target.value) } /> + onChange={e => props.update('slug', e.target.value)} /> props.update('section', section) } /> + update={section => props.update('section', section)} /> props.update('authors', authors) } /> + update={authors => props.update('authors', authors)} /> props.update('tags', tags) } /> + update={tags => props.update('tags', tags)} /> props.update('topic', topic) } /> + update={topic => props.update('topic', topic)} /> props.update('snippet', e.target.value) } /> + onChange={e => props.update('snippet', e.target.value)} />
diff --git a/dispatch/static/manager/src/js/components/Editor/tabs/DeliveryTab.js b/dispatch/static/manager/src/js/components/Editor/tabs/DeliveryTab.js index d35a0a1b9..5034ff09a 100644 --- a/dispatch/static/manager/src/js/components/Editor/tabs/DeliveryTab.js +++ b/dispatch/static/manager/src/js/components/Editor/tabs/DeliveryTab.js @@ -57,14 +57,14 @@ export default function DeliveryTab(props) { props.update('importance', e.target.value) } /> + onChange={e => props.update('importance', e.target.value)} /> props.update('reading_time', e.target.value) } /> + onChange={e => props.update('reading_time', e.target.value)} /> @@ -72,7 +72,7 @@ export default function DeliveryTab(props) { className='pt-large' disabled={!isInstantArticlesEnabled} checked={R.path(['fb-instant-articles', 'enabled'], props.integrations)} - onChange={ e => updateInstantArticle(props.update, props.integrations, e.target.checked) } /> + onChange={e => updateInstantArticle(props.update, props.integrations, e.target.checked)} /> {warningMessage} diff --git a/dispatch/static/manager/src/js/components/Editor/tabs/FeaturedImageTab.js b/dispatch/static/manager/src/js/components/Editor/tabs/FeaturedImageTab.js index b35f9b946..941849c22 100644 --- a/dispatch/static/manager/src/js/components/Editor/tabs/FeaturedImageTab.js +++ b/dispatch/static/manager/src/js/components/Editor/tabs/FeaturedImageTab.js @@ -6,7 +6,7 @@ import { FormInput, TextAreaInput, ImageInput } from '../../inputs' export default function FeaturedImageTab(props) { function updateImage(imageId) { - if(imageId){ + if (imageId){ return props.update( 'featured_image', R.merge(props.featured_image, { image: imageId }) diff --git a/dispatch/static/manager/src/js/components/Editor/tabs/SEOTab.js b/dispatch/static/manager/src/js/components/Editor/tabs/SEOTab.js index 0c4e18ce4..1882d0451 100644 --- a/dispatch/static/manager/src/js/components/Editor/tabs/SEOTab.js +++ b/dispatch/static/manager/src/js/components/Editor/tabs/SEOTab.js @@ -59,7 +59,7 @@ export default function SEOTab(props) { placeholder='Focus Keywords' value={props.seo_keyword} fill={true} - onChange={ e => props.update('seo_keyword', e.target.value) } /> + onChange={e => props.update('seo_keyword', e.target.value)} />
props.update('seo_description', e.target.value) } /> + onChange={e => props.update('seo_description', e.target.value)} />
this.props.update('template', template) } /> + update={template => this.props.update('template', template)} />
{fields}
diff --git a/dispatch/static/manager/src/js/components/Editor/toolbar/VersionsDropdown.js b/dispatch/static/manager/src/js/components/Editor/toolbar/VersionsDropdown.js index 29adc6f10..9ec542af7 100644 --- a/dispatch/static/manager/src/js/components/Editor/toolbar/VersionsDropdown.js +++ b/dispatch/static/manager/src/js/components/Editor/toolbar/VersionsDropdown.js @@ -22,9 +22,8 @@ export default class VersionsDropdown extends React.Component { this.props.getVersion(version) this.refs.dropdown.close() } - - renderDropdown() { + renderDropdown() { let versions = this.getVersions().map( (version) => { @@ -37,7 +36,7 @@ export default class VersionsDropdown extends React.Component { className={`o-dropdown-list__item${selectedClassName}`} key={version} onClick={() => this.selectVersion(version)}> - + {`Version ${version}${published}`} ) @@ -60,7 +59,7 @@ export default class VersionsDropdown extends React.Component { content={this.renderDropdown()}> this.refs.dropdown.open()}> {`Version ${this.props.current_version}`} - + ) diff --git a/dispatch/static/manager/src/js/components/EventEditor/EventCard.js b/dispatch/static/manager/src/js/components/EventEditor/EventCard.js index 75ff75d78..6051c9b32 100644 --- a/dispatch/static/manager/src/js/components/EventEditor/EventCard.js +++ b/dispatch/static/manager/src/js/components/EventEditor/EventCard.js @@ -24,8 +24,7 @@ export default function EventCard(props) { const image = (
-
+ style={{backgroundImage: `url('${props.event.image}')`}} /> ) return ( diff --git a/dispatch/static/manager/src/js/components/EventEditor/EventForm.js b/dispatch/static/manager/src/js/components/EventEditor/EventForm.js index 00a52de4f..18cc7e8ca 100644 --- a/dispatch/static/manager/src/js/components/EventEditor/EventForm.js +++ b/dispatch/static/manager/src/js/components/EventEditor/EventForm.js @@ -5,14 +5,14 @@ import { AnchorButton } from '@blueprintjs/core' import { FormInput, TextInput, TextAreaInput, DateTimeInput, SelectInput } from '../inputs' const CATEGORY_CHOICES = [ - ['sports', 'Sports'], - ['music', 'Music'], - ['academic', 'Academic'], - ['party', 'Party'], - ['business', 'Business'], - ['ceremony', 'Ceremony'], - ['workshop', 'Workshop'], - ['other', 'Other'] + ['sports', 'Sports'], + ['music', 'Music'], + ['academic', 'Academic'], + ['party', 'Party'], + ['business', 'Business'], + ['ceremony', 'Ceremony'], + ['workshop', 'Workshop'], + ['other', 'Other'] ] export default class EventForm extends React.Component { @@ -40,7 +40,7 @@ export default class EventForm extends React.Component { placeholder='Name' value={this.props.listItem.title || ''} fill={true} - onChange={ e => this.props.update('title', e.target.value) } /> + onChange={e => this.props.update('title', e.target.value)} /> this.props.update('description', e.target.value) } /> + onChange={e => this.props.update('description', e.target.value)} /> this.props.update('host', e.target.value) } /> + onChange={e => this.props.update('host', e.target.value)} /> } + src={this.state.displayImg || this.props.listItem.image} />
@@ -96,7 +96,7 @@ export default class EventForm extends React.Component { error={this.props.errors.start_time}> this.props.update('start_time', dt)} /> + onChange={dt => this.props.update('start_time', dt)} /> this.props.update('end_time', dt)} /> + onChange={dt => this.props.update('end_time', dt)} /> this.props.update('location', e.target.value) } /> + onChange={e => this.props.update('location', e.target.value)} /> this.props.update('address', e.target.value) } /> + onChange={e => this.props.update('address', e.target.value)} /> this.props.update('category', e.target.value)} /> + onChange={e => this.props.update('category', e.target.value)} /> this.props.update('event_url', e.target.value) } /> + onChange={e => this.props.update('event_url', e.target.value)} /> this.props.update('ticket_url', e.target.value) } /> + onChange={e => this.props.update('ticket_url', e.target.value)} /> this.props.update('submitter_email', e.target.value) } /> + onChange={e => this.props.update('submitter_email', e.target.value)} /> this.props.update('submitter_phone', e.target.value) } /> + onChange={e => this.props.update('submitter_phone', e.target.value)} /> ) diff --git a/dispatch/static/manager/src/js/components/EventEditor/EventPendingTag.js b/dispatch/static/manager/src/js/components/EventEditor/EventPendingTag.js index 6d49ede97..8be64ce88 100644 --- a/dispatch/static/manager/src/js/components/EventEditor/EventPendingTag.js +++ b/dispatch/static/manager/src/js/components/EventEditor/EventPendingTag.js @@ -8,7 +8,7 @@ require('../../../styles/components/event_audit.scss') class EventPendingTagComponent extends React.Component { - componentWillMount() { + componentDidMount() { this.props.countPending(this.props.token, { pending: 1, limit: 0 }) } diff --git a/dispatch/static/manager/src/js/components/EventEditor/index.js b/dispatch/static/manager/src/js/components/EventEditor/index.js index 0a16244e4..d637655b2 100644 --- a/dispatch/static/manager/src/js/components/EventEditor/index.js +++ b/dispatch/static/manager/src/js/components/EventEditor/index.js @@ -79,19 +79,21 @@ const mapDispatchToProps = (dispatch) => { function EventEditorComponent(props) { const publishButton = ( - props.publishEvent(props.token, props.listItem.id)} + props.publishEvent(props.token, props.listItem.id)} intent={Intent.PRIMARY} disabled={props.isNew} > - + Publish ) const unpublishButton = ( - props.unpublishEvent(props.token, props.listItem.id)} + props.unpublishEvent(props.token, props.listItem.id)} intent={Intent.PRIMARY} disabled={props.isNew} > - + Unpublish ) @@ -109,7 +111,7 @@ function EventEditorComponent(props) { const isPublished = (obj) => { return obj.entities.local && obj.listItem.id - ? obj.entities.local[obj.listItem.id].is_published : false + ? obj.entities.local[obj.listItem.id].is_published : false } const EventEditor = connect( diff --git a/dispatch/static/manager/src/js/components/GalleryEditor/AttachmentForm.js b/dispatch/static/manager/src/js/components/GalleryEditor/AttachmentForm.js index 22244792f..2dacd5e10 100644 --- a/dispatch/static/manager/src/js/components/GalleryEditor/AttachmentForm.js +++ b/dispatch/static/manager/src/js/components/GalleryEditor/AttachmentForm.js @@ -14,7 +14,7 @@ export default function AttachmentForm(props) { placeholder='Caption' value={props.caption || ''} fill={true} - onChange={ e => props.update('caption', e.target.value) } /> + onChange={e => props.update('caption', e.target.value)} /> props.update('credit', e.target.value) } /> + onChange={e => props.update('credit', e.target.value)} /> diff --git a/dispatch/static/manager/src/js/components/GalleryEditor/GalleryForm.js b/dispatch/static/manager/src/js/components/GalleryEditor/GalleryForm.js index bda77f0c8..637daff76 100644 --- a/dispatch/static/manager/src/js/components/GalleryEditor/GalleryForm.js +++ b/dispatch/static/manager/src/js/components/GalleryEditor/GalleryForm.js @@ -125,9 +125,9 @@ class GalleryFormComponent extends React.Component { let newImages if (images && images.length) { newImages = R.filter(id => R.findIndex(R.either( - R.propSatisfies(image => image && image.id == id, 'image'), - R.propEq('image_id', id) - ), images) === -1, ids) + R.propSatisfies(image => image && image.id == id, 'image'), + R.propEq('image_id', id) + ), images) === -1, ids) .map(makeNewImage) } else { newImages = R.map(makeNewImage, ids) @@ -170,7 +170,6 @@ class GalleryFormComponent extends React.Component { } render() { - let i = 1 const inGalleryImages = this.props.listItem.images ? this.props.listItem.images.map((img, idx) => { @@ -204,20 +203,18 @@ class GalleryFormComponent extends React.Component { content={form} isModal={true} onClose={() => { this.props.selectImage(0) }} - // put the rightmost thumbs' popover on the left position={(i % this.getNumPerRow() == 0) - ? Position.LEFT : Position.RIGHT} - > -
-
- {i++} -
+ ? Position.LEFT : Position.RIGHT}> +
+
+ {i++}
+
this.props.update('title', e.target.value) } /> + onChange={e => this.props.update('title', e.target.value)} />

Gallery

this.setState({ zoneDims })}> - - {inGalleryImages} - - + {({ measureRef }) => +
+ + {inGalleryImages} + + +
+ }
diff --git a/dispatch/static/manager/src/js/components/Header.js b/dispatch/static/manager/src/js/components/Header.js index 2a6551ade..08dd5a0a8 100644 --- a/dispatch/static/manager/src/js/components/Header.js +++ b/dispatch/static/manager/src/js/components/Header.js @@ -11,9 +11,10 @@ export default function Header(props) { diff --git a/dispatch/static/manager/src/js/components/Header/HeaderButtons.js b/dispatch/static/manager/src/js/components/Header/HeaderButtons.js new file mode 100644 index 000000000..baab65359 --- /dev/null +++ b/dispatch/static/manager/src/js/components/Header/HeaderButtons.js @@ -0,0 +1,49 @@ +import React, { Component } from 'react' +import { Link } from 'react-router' + +require('../../../styles/components/header.scss') +require('../../../styles/utilities/_pseudo_bootstrap.scss') + +const links = { + Content: [['Articles', ''], 'Pages'], + Widgets: ['Polls', 'Zones', 'Events'], + Media: ['Galleris', 'Files', 'Images', 'Videos'], + Misc: ['Issues', 'People'] +} + +const renderLinks = (type, key, items) => { + console.log(key) + console.log(items) + console.log('render links') + return( +
+
+ + {String(key)} +
+
+ { + items.map((item, index) => { + {item} + }) + } +
+
+ ) +} +const HeaderButtons = (props) => { + const dropdownType = props.isDesktop ? 'nav-dropdown-container' : 'nav-dropdown-container-mobile' + return ( +
+
+ { + Object.keys(links).map((key, index)=> { + return( renderLinks(dropdownType, key, links[key])) + }) + } +
+
+ ) +} + +export default HeaderButtons \ No newline at end of file diff --git a/dispatch/static/manager/src/js/components/ImageEditor/ImageForm.js b/dispatch/static/manager/src/js/components/ImageEditor/ImageForm.js index 92698c3ae..fc164e7a0 100644 --- a/dispatch/static/manager/src/js/components/ImageEditor/ImageForm.js +++ b/dispatch/static/manager/src/js/components/ImageEditor/ImageForm.js @@ -37,7 +37,7 @@ export default class ImageForm extends React.Component { placeholder='Image title' value={this.props.listItem.title || ''} fill={true} - onChange={ e => this.props.update('title', e.target.value) } /> + onChange={e => this.props.update('title', e.target.value)} /> this.props.update('tags', tags) } /> - + update={tags => this.props.update('tags', tags)} /> +
diff --git a/dispatch/static/manager/src/js/components/ImageStore.js b/dispatch/static/manager/src/js/components/ImageStore.js index 421f4ee8d..6058a5e83 100644 --- a/dispatch/static/manager/src/js/components/ImageStore.js +++ b/dispatch/static/manager/src/js/components/ImageStore.js @@ -1,69 +1,69 @@ -var _ = require('lodash'); +var _ = require('lodash') var ImageStore = function(){ - return { - images: [], - dump: function(images){ - this.images = images; - }, - append: function(images){ - this.images = this.images.concat(images); - }, - addTemp: function(name, thumb){ - var tempImage = { - tempName: name, - thumb: thumb, - } - this.images.unshift(tempImage); - }, - updateProgress: function(name, progress){ - var i = _.findIndex(this.images, {tempName: name}) - this.images[i].progress = progress; - }, - updateImage: function(id, callback){ - dispatch.find('image', id, function(data){ - var i = _.findIndex(this.images, {id: id}); - this.images[i] = data; - callback(); - }.bind(this)) - }, - updateImageWithData: function(data){ - var i = _.findIndex(this.images, {id: data.id}); - this.images[i] = data; - }, - updateAttachment: function(attachment_id, data){ - var i = _.findIndex(this.images, {attachment_id: attachment_id}); - this.images[i] = data; - }, - replaceTemp: function(name, image){ - var i = _.findIndex(this.images, {tempName: name}); - this.images[i] = image; - }, - getImage: function(id){ - var i = _.findIndex(this.images, {id: id}); - return this.images[i]; - }, - getImages: function(ids){ - var images = []; - _.forEach(ids, function(id, index){ - images.push(this.getImage(id)); - }.bind(this)); - return images; - }, - removeImage: function(id){ - _.remove(this.images, function(n) { - return n.id == id; - }); - }, - removeAttachment: function(attachment_id) { - _.remove(this.images, function(n) { - return n.attachment_id == attachment_id; - }); - }, - all: function(){ - return this.images; - } + return { + images: [], + dump: function(images){ + this.images = images + }, + append: function(images){ + this.images = this.images.concat(images) + }, + addTemp: function(name, thumb){ + var tempImage = { + tempName: name, + thumb: thumb, + } + this.images.unshift(tempImage) + }, + updateProgress: function(name, progress){ + var i = _.findIndex(this.images, {tempName: name}) + this.images[i].progress = progress + }, + updateImage: function(id, callback){ + dispatch.find('image', id, function(data){ + var i = _.findIndex(this.images, {id: id}) + this.images[i] = data + callback() + }.bind(this)) + }, + updateImageWithData: function(data){ + var i = _.findIndex(this.images, {id: data.id}) + this.images[i] = data + }, + updateAttachment: function(attachment_id, data){ + var i = _.findIndex(this.images, {attachment_id: attachment_id}) + this.images[i] = data + }, + replaceTemp: function(name, image){ + var i = _.findIndex(this.images, {tempName: name}) + this.images[i] = image + }, + getImage: function(id){ + var i = _.findIndex(this.images, {id: id}) + return this.images[i] + }, + getImages: function(ids){ + var images = [] + _.forEach(ids, function(id, index){ + images.push(this.getImage(id)) + }.bind(this)) + return images + }, + removeImage: function(id){ + _.remove(this.images, function(n) { + return n.id == id + }) + }, + removeAttachment: function(attachment_id) { + _.remove(this.images, function(n) { + return n.attachment_id == attachment_id + }) + }, + all: function(){ + return this.images } + } } -module.exports = ImageStore; +module.exports = ImageStore diff --git a/dispatch/static/manager/src/js/components/ItemEditor/ListItemToolbar.js b/dispatch/static/manager/src/js/components/ItemEditor/ListItemToolbar.js index 8fad89262..311af1da2 100644 --- a/dispatch/static/manager/src/js/components/ItemEditor/ListItemToolbar.js +++ b/dispatch/static/manager/src/js/components/ItemEditor/ListItemToolbar.js @@ -20,7 +20,7 @@ export default function ListItemToolbar(props) { intent={Intent.DANGER} disabled={props.isNew} onConfirm={() => props.deleteListItem()}> - Delete + Delete ) @@ -29,7 +29,7 @@ export default function ListItemToolbar(props) { props.goBack()}> - Back + Back {props.isNew ? newTitle : editTitle} @@ -37,7 +37,7 @@ export default function ListItemToolbar(props) { props.saveListItem()}> - {props.isNew ? 'Save' : 'Update'} + {props.isNew ? 'Save' : 'Update'} {props.extraButton} {props.deleteListItem ? deleteButton : null} diff --git a/dispatch/static/manager/src/js/components/ItemEditor/index.js b/dispatch/static/manager/src/js/components/ItemEditor/index.js index 26d7ef43d..ec87aa5e6 100644 --- a/dispatch/static/manager/src/js/components/ItemEditor/index.js +++ b/dispatch/static/manager/src/js/components/ItemEditor/index.js @@ -11,7 +11,7 @@ const NEW_LISTITEM_ID = 'new' class ItemEditor extends React.Component { - componentWillMount() { + componentDidMount() { if (this.props.isNew) { // Create empty listItem this.props.setListItem({ id: NEW_LISTITEM_ID }) @@ -19,9 +19,7 @@ class ItemEditor extends React.Component { // Fetch listItem this.props.getListItem(this.props.token, this.props.itemId) } - } - componentDidMount() { if (this.props.route) { confirmNavigation( this.props.router, diff --git a/dispatch/static/manager/src/js/components/ItemList/ItemListHeader.js b/dispatch/static/manager/src/js/components/ItemList/ItemListHeader.js index e53278e59..08d384019 100644 --- a/dispatch/static/manager/src/js/components/ItemList/ItemListHeader.js +++ b/dispatch/static/manager/src/js/components/ItemList/ItemListHeader.js @@ -21,7 +21,8 @@ export default function ItemListHeader(props) { const toolbarLeft = (
- props.actions.toggleAllItems(props.items.ids)} />
diff --git a/dispatch/static/manager/src/js/components/ModalContainer.js b/dispatch/static/manager/src/js/components/ModalContainer.js index 7a09a6432..caa1238a7 100644 --- a/dispatch/static/manager/src/js/components/ModalContainer.js +++ b/dispatch/static/manager/src/js/components/ModalContainer.js @@ -5,7 +5,7 @@ require('../../styles/components/modal_container.scss') export default function ModalContainer(props) { return (
-
+
{props.children}
) diff --git a/dispatch/static/manager/src/js/components/PageEditor/PageContentEditor.js b/dispatch/static/manager/src/js/components/PageEditor/PageContentEditor.js index fd66859ab..3f9d5656b 100644 --- a/dispatch/static/manager/src/js/components/PageEditor/PageContentEditor.js +++ b/dispatch/static/manager/src/js/components/PageEditor/PageContentEditor.js @@ -18,7 +18,7 @@ const embeds = [ export default class PageContentEditor extends React.Component { render() { return ( -
+
- Basic fields - Featured image - Template - SEO + Basic fields + Featured image + Template + SEO diff --git a/dispatch/static/manager/src/js/components/PageEditor/PageToolbar.js b/dispatch/static/manager/src/js/components/PageEditor/PageToolbar.js index 28a74d9f4..361046342 100644 --- a/dispatch/static/manager/src/js/components/PageEditor/PageToolbar.js +++ b/dispatch/static/manager/src/js/components/PageEditor/PageToolbar.js @@ -30,7 +30,7 @@ export default function PageToolbar(props) { props.previewPage()}> - Preview + Preview props.update('slug', e.target.value) } /> + onChange={e => props.update('slug', e.target.value)} /> props.update('snippet', e.target.value) } /> + onChange={e => props.update('snippet', e.target.value)} />
diff --git a/dispatch/static/manager/src/js/components/PersonEditor/PersonForm.js b/dispatch/static/manager/src/js/components/PersonEditor/PersonForm.js index c2c900931..cbd2e6b46 100644 --- a/dispatch/static/manager/src/js/components/PersonEditor/PersonForm.js +++ b/dispatch/static/manager/src/js/components/PersonEditor/PersonForm.js @@ -41,7 +41,7 @@ export default class PersonForm extends React.Component { placeholder='Full Name' value={this.props.listItem.full_name || ''} fill={true} - onChange={ e => this.props.update('full_name', e.target.value) } /> + onChange={e => this.props.update('full_name', e.target.value)} /> this.props.update('slug', e.target.value) } /> + onChange={e => this.props.update('slug', e.target.value)} /> this.props.update('facebook_url', e.target.value) } /> + onChange={e => this.props.update('facebook_url', e.target.value)} /> this.props.update('twitter_url', e.target.value) } /> + onChange={e => this.props.update('twitter_url', e.target.value)} /> } + src={this.state.displayImg || this.props.listItem.image_url} />
@@ -112,7 +112,7 @@ export default class PersonForm extends React.Component { placeholder='Description' value={this.props.listItem.description || ''} fill={true} - onChange={ e => this.props.update('description', e.target.value) } /> + onChange={e => this.props.update('description', e.target.value)} /> ) diff --git a/dispatch/static/manager/src/js/components/PlaceholderBar.js b/dispatch/static/manager/src/js/components/PlaceholderBar.js index 38b64ce8f..2e32ac556 100644 --- a/dispatch/static/manager/src/js/components/PlaceholderBar.js +++ b/dispatch/static/manager/src/js/components/PlaceholderBar.js @@ -4,6 +4,6 @@ require('../../styles/components/placeholder_bar.scss') export default function PlaceholderBar() { return ( -
+
) } diff --git a/dispatch/static/manager/src/js/components/PollEditor/Poll.js b/dispatch/static/manager/src/js/components/PollEditor/Poll.js new file mode 100644 index 000000000..150768d2e --- /dev/null +++ b/dispatch/static/manager/src/js/components/PollEditor/Poll.js @@ -0,0 +1,101 @@ +import React, { Component } from 'react' +import { Button, Intent } from '@blueprintjs/core' + +require('../../../styles/components/poll.scss') + +const COLOR_OPACITY = .8 + +class Poll extends Component { + constructor(props){ + super(props) + this.state = { + showResults: true, + } + } + + getPollResult(voteCount) { + const total = this.props.answers.reduce((acc, answer) => acc + answer.vote_count, 0) + + if (this.state.showResults && total) { + return String((100*voteCount/total).toFixed(0)) + '%' + } + + return 0 + } + + toggleResults() { + this.setState(prevstate => ({ + showResults: !prevstate.showResults + })) + } + + render() { + const { answers, question } = this.props + const notShowResult= this.state.showResults ? 0 : COLOR_OPACITY + const showResult = this.state.showResults ? COLOR_OPACITY : 0 + return ( +
+
+
+

{question}

+
+ {answers.map((answer, index) => { + return ( +
+
+ ) + } +} + +Poll.defaultProps = { + question: 'Default question', + answers: [ + { + name: 'Answer 1', + vote_count: 2, + votes: [] + }, + { + name: 'Answer 2', + vote_count: 1, + votes: [] + }, + ] +} + +export default Poll diff --git a/dispatch/static/manager/src/js/components/PollEditor/PollForm.js b/dispatch/static/manager/src/js/components/PollEditor/PollForm.js new file mode 100644 index 000000000..29822e9e7 --- /dev/null +++ b/dispatch/static/manager/src/js/components/PollEditor/PollForm.js @@ -0,0 +1,164 @@ +import React from 'react' +import { Button, Intent } from '@blueprintjs/core' +import R from 'ramda' + +import { FormInput, TextInput } from '../inputs' +import Poll from './Poll' +import SelectInput from '../inputs/selects/SelectInput' + +require('../../../styles/components/poll_form.scss') + +const DEFAULT_ANSWERS = [ + { + 'name': '', + 'vote_count': 0 + }, + { + 'name': '', + 'vote_count': 0 + } +] + +export default class PollForm extends React.Component { + + addAnswer() { + this.props.update('answers', this.getAnswers().concat(DEFAULT_ANSWERS[0])) + } + + removeAnswer(index) { + this.props.update('answers', R.remove(index, 1, this.getAnswers())) + } + + handleUpdateAnswer(e, index) { + this.props.update('answers', + R.adjust(R.assoc('name', e.target.value), index, this.getAnswers()) + ) + } + + getAnswers() { + return this.props.listItem.answers || DEFAULT_ANSWERS + } + + renderAnswers() { + const answers = this.getAnswers().map( + (answer, index) => { + return ( + + this.handleUpdateAnswer(e, index)} /> + this.removeAnswer(index)}> + Remove answer + + + ) + } + ) + return ( +
+ {answers} +
+ ) + } + + renderAddAnswerButton() { + return ( + + ) + } + + renderPollOpenSelect() { + const OPTIONS = [ + [true, 'Poll Open'], + [false, 'Poll Closed'] + ] + + return ( + +
+ this.props.update('is_open', e.target.value)} /> +
+
+ ) + } + + renderOptions() { + const OPTIONS = [ + [true, 'Show results'], + [false, 'Hide results'] + ] + + return ( +
+ +
+ this.props.update('show_results', e.target.value)} /> +
+
+ {(this.props.listItem.id === 'new') ? null : this.renderPollOpenSelect()} +
+ ) + } + + render() { + return ( +
+
+
e.preventDefault()}> + + this.props.update('name', e.target.value)} /> + + + this.props.update('question', e.target.value)} /> + + {this.renderAnswers()} + {this.renderAddAnswerButton()} + {this.renderOptions()} +
+
+
+ +
+
+ ) + } +} diff --git a/dispatch/static/manager/src/js/components/PollEditor/index.js b/dispatch/static/manager/src/js/components/PollEditor/index.js new file mode 100644 index 000000000..c7eb1d9ea --- /dev/null +++ b/dispatch/static/manager/src/js/components/PollEditor/index.js @@ -0,0 +1,64 @@ +import React from 'react' +import { connect } from 'react-redux' + +import pollsActions from '../../actions/PollsActions' +import PollForm from './PollForm' + +import ItemEditor from '../ItemEditor' + +const TYPE = 'Poll' +const AFTER_DELETE = 'polls' + +function prepareJSONData(data) { + data.answers_json = data.answers + return data +} + +function PollEditorComponent(props) { + return ( + + ) +} + +const mapStateToProps = (state) => { + return { + listItem: state.app.polls.single, + entities: { + remote: state.app.entities.polls, + local: state.app.entities.local.polls, + }, + token: state.app.auth.token + } +} + +const mapDispatchToProps = (dispatch) => { + return { + getListItem: (token, pollId) => { + dispatch(pollsActions.get(token, pollId)) + }, + setListItem: (poll) => { + dispatch(pollsActions.set(poll)) + }, + saveListItem: (token, pollId, data) => { + dispatch(pollsActions.save(token, pollId, prepareJSONData(data))) + }, + createListItem: (token, data) => { + dispatch(pollsActions.create(token, prepareJSONData(data), AFTER_DELETE)) + }, + deleteListItem: (token, pollId, next) => { + dispatch(pollsActions.delete(token, pollId, next)) + } + } +} + +const PollEditor = connect( + mapStateToProps, + mapDispatchToProps +)(PollEditorComponent) + +export default PollEditor diff --git a/dispatch/static/manager/src/js/components/SectionEditor/SectionForm.js b/dispatch/static/manager/src/js/components/SectionEditor/SectionForm.js index 984867c1c..b07d474de 100644 --- a/dispatch/static/manager/src/js/components/SectionEditor/SectionForm.js +++ b/dispatch/static/manager/src/js/components/SectionEditor/SectionForm.js @@ -15,7 +15,7 @@ export default function SectionForm(props) { placeholder='Name' value={props.listItem.name || ''} fill={true} - onChange={ e => props.update('name', e.target.value) } /> + onChange={e => props.update('name', e.target.value)} /> props.update('slug', e.target.value) } /> + onChange={e => props.update('slug', e.target.value)} /> diff --git a/dispatch/static/manager/src/js/components/TagEditor/TagForm.js b/dispatch/static/manager/src/js/components/TagEditor/TagForm.js index 1f2b4637b..b417742d5 100644 --- a/dispatch/static/manager/src/js/components/TagEditor/TagForm.js +++ b/dispatch/static/manager/src/js/components/TagEditor/TagForm.js @@ -14,7 +14,7 @@ export default function TagForm(props) { placeholder='Name' value={props.listItem.name || ''} fill={true} - onChange={ e => props.update('name', e.target.value) } /> + onChange={e => props.update('name', e.target.value)} /> ) diff --git a/dispatch/static/manager/src/js/components/TopicEditor/TopicForm.js b/dispatch/static/manager/src/js/components/TopicEditor/TopicForm.js index 29550b2f1..19067fc83 100644 --- a/dispatch/static/manager/src/js/components/TopicEditor/TopicForm.js +++ b/dispatch/static/manager/src/js/components/TopicEditor/TopicForm.js @@ -14,7 +14,7 @@ export default function TopicForm(props) { placeholder='Name' value={props.listItem.name || ''} fill={true} - onChange={ e => props.update('name', e.target.value) } /> + onChange={e => props.update('name', e.target.value)} /> ) diff --git a/dispatch/static/manager/src/js/components/VideoEditor/VideoForm.js b/dispatch/static/manager/src/js/components/VideoEditor/VideoForm.js index 5024c6a6d..1493fd7fa 100644 --- a/dispatch/static/manager/src/js/components/VideoEditor/VideoForm.js +++ b/dispatch/static/manager/src/js/components/VideoEditor/VideoForm.js @@ -15,7 +15,7 @@ export default function VideoForm(props) { placeholder='Title' value={props.listItem.title || ''} fill={true} - onChange={ e => props.update('title', e.target.value) } /> + onChange={e => props.update('title', e.target.value)} /> props.update('url', e.target.value) } /> + onChange={e => props.update('url', e.target.value)} /> diff --git a/dispatch/static/manager/src/js/components/WidgetFields.js b/dispatch/static/manager/src/js/components/WidgetFields.js new file mode 100644 index 000000000..7e5cb7c59 --- /dev/null +++ b/dispatch/static/manager/src/js/components/WidgetFields.js @@ -0,0 +1,43 @@ +import React from 'react' +import { connect } from 'react-redux' + +import WidgetField from './ZoneEditor/WidgetField' + +class WidgetFieldsComponent extends React.Component { + render() { + const widget = this.props.entities.widgets[this.props.widgetId] + const fields = widget ? widget.fields.map((field) => ( + this.props.updateField(field.name, data)} /> + )) : null + + return ( +
+ {fields} +
+ ) + } +} + +const mapStateToProps = (state) => { + + return { + entities: { + widgets: state.app.entities.widgets + } + } +} + +const mapDispatchToProps = () => { + return {} +} + +const WidgetFields = connect( + mapStateToProps, + mapDispatchToProps +)(WidgetFieldsComponent) + +export default WidgetFields diff --git a/dispatch/static/manager/src/js/components/ZoneEditor/WidgetField.js b/dispatch/static/manager/src/js/components/ZoneEditor/WidgetField.js new file mode 100644 index 000000000..fea45a969 --- /dev/null +++ b/dispatch/static/manager/src/js/components/ZoneEditor/WidgetField.js @@ -0,0 +1,34 @@ +import React from 'react' + +import * as fields from '../fields' + +import { FormInput } from '../inputs' + +export default function WidgetField(props) { + const Field = fields[props.field.type] + let fieldError = '' + let childErrors = null + if (props.field.type == 'widget' && props.error) { + try { + childErrors = JSON.parse(props.error) + fieldError = childErrors.self + } catch (e) { + fieldError = props.error + } + } else { + fieldError = props.error + } + + return ( + + + + ) + +} diff --git a/dispatch/static/manager/src/js/components/ZoneEditor/index.js b/dispatch/static/manager/src/js/components/ZoneEditor/index.js index bd6cd96b9..fd9d360c2 100644 --- a/dispatch/static/manager/src/js/components/ZoneEditor/index.js +++ b/dispatch/static/manager/src/js/components/ZoneEditor/index.js @@ -14,7 +14,7 @@ import FieldGroup from '../fields/FieldGroup' class ZoneEditorComponent extends React.Component { - componentWillMount() { + componentDidMount() { this.props.getZone(this.props.token, this.props.zoneId) } diff --git a/dispatch/static/manager/src/js/components/fields/IntegerField.js b/dispatch/static/manager/src/js/components/fields/IntegerField.js index 191578052..6af753823 100644 --- a/dispatch/static/manager/src/js/components/fields/IntegerField.js +++ b/dispatch/static/manager/src/js/components/fields/IntegerField.js @@ -13,7 +13,7 @@ export default class IntegerField extends React.Component { } onChange(val) { - let { selectionStart, selectionEnd } = this.refs.input.refs.input + let { selectionStart, selectionEnd } = this.refs.textInput.refs.input const initialLength = val.length val = val.replace(/[^\d-]+/g, '') @@ -36,7 +36,7 @@ export default class IntegerField extends React.Component { componentDidUpdate() { //put the caret back in the correct place - this.refs.input.refs.input.setSelectionRange( + this.refs.textInput.refs.input.setSelectionRange( this.state.selectionStart, this.state.selectionEnd) } @@ -44,7 +44,7 @@ export default class IntegerField extends React.Component { render() { return ( props.onChange(selected)} /> + ) +} \ No newline at end of file diff --git a/dispatch/static/manager/src/js/components/fields/WidgetField.js b/dispatch/static/manager/src/js/components/fields/WidgetField.js index 4d549e3fc..6300e7ed8 100644 --- a/dispatch/static/manager/src/js/components/fields/WidgetField.js +++ b/dispatch/static/manager/src/js/components/fields/WidgetField.js @@ -62,7 +62,7 @@ export default class WidgetFieldComponent extends React.Component { selected={this.getWidgetId()} update={widgetId => this.handleWidgetChange(widgetId)} />
- {fields} + {fields}
) } diff --git a/dispatch/static/manager/src/js/components/fields/index.js b/dispatch/static/manager/src/js/components/fields/index.js index afa1b3b32..150e0e74e 100644 --- a/dispatch/static/manager/src/js/components/fields/index.js +++ b/dispatch/static/manager/src/js/components/fields/index.js @@ -8,6 +8,7 @@ import IntegerField from './IntegerField' import BoolField from './BoolField' import SelectField from './SelectField' import WidgetField from './WidgetField' +import PollField from './PollField' export { CharField as char, @@ -19,5 +20,6 @@ export { IntegerField as integer, BoolField as bool, SelectField as select, - WidgetField as widget + WidgetField as widget, + PollField as poll, } diff --git a/dispatch/static/manager/src/js/components/inputs/DateTimeInput.js b/dispatch/static/manager/src/js/components/inputs/DateTimeInput.js index be1cc097e..8428b23bd 100644 --- a/dispatch/static/manager/src/js/components/inputs/DateTimeInput.js +++ b/dispatch/static/manager/src/js/components/inputs/DateTimeInput.js @@ -12,7 +12,7 @@ function ensureDate(date) { let ret = date if (!(date instanceof Date)) { const time_ms = Date.parse(date) - if(isNaN(time_ms)) { + if (isNaN(time_ms)) { ret = null } else { ret = new Date(time_ms) diff --git a/dispatch/static/manager/src/js/components/inputs/ImageInput.js b/dispatch/static/manager/src/js/components/inputs/ImageInput.js index 8d24645d8..aeba9d135 100644 --- a/dispatch/static/manager/src/js/components/inputs/ImageInput.js +++ b/dispatch/static/manager/src/js/components/inputs/ImageInput.js @@ -17,7 +17,7 @@ function Image(props) {
- +
) @@ -90,11 +90,11 @@ class ImageInputComponent extends React.Component { onClick={() => this.removeImage(image.id)} /> )} /> this.chooseImage() }>{this.props.many ? 'Add image' : (this.props.selected ? 'Change image' : 'Select image')} - + onClick={() => this.chooseImage()}>{this.props.many ? 'Add image' : (this.props.selected ? 'Change image' : 'Select image')} + {(this.props.selected && this.props.removable) && this.removeImage() }>{'Remove image'} - } + onClick={() => this.removeImage()}>{'Remove image'} + }
) } diff --git a/dispatch/static/manager/src/js/components/inputs/filters/AuthorFilterInput.js b/dispatch/static/manager/src/js/components/inputs/filters/AuthorFilterInput.js index d5364a5e1..c080cbfcd 100644 --- a/dispatch/static/manager/src/js/components/inputs/filters/AuthorFilterInput.js +++ b/dispatch/static/manager/src/js/components/inputs/filters/AuthorFilterInput.js @@ -33,8 +33,7 @@ class AuthorFilterInputComponent extends React.Component { attribute='full_name' label='Author' icon='person' - editMessage='Filter by author' - /> + editMessage='Filter by author' /> ) } } diff --git a/dispatch/static/manager/src/js/components/inputs/filters/TagsFilterInput.js b/dispatch/static/manager/src/js/components/inputs/filters/TagsFilterInput.js index 96e4ff20f..cf1943bdc 100644 --- a/dispatch/static/manager/src/js/components/inputs/filters/TagsFilterInput.js +++ b/dispatch/static/manager/src/js/components/inputs/filters/TagsFilterInput.js @@ -29,8 +29,7 @@ class TagsFilterInputComponent extends React.Component { attribute='name' label='Tag' icon='tag' - editMessage='Filter by tag' - /> + editMessage='Filter by tag' /> ) } } diff --git a/dispatch/static/manager/src/js/components/inputs/selects/ItemSelectInput.js b/dispatch/static/manager/src/js/components/inputs/selects/ItemSelectInput.js index a6aacb511..322b395e5 100644 --- a/dispatch/static/manager/src/js/components/inputs/selects/ItemSelectInput.js +++ b/dispatch/static/manager/src/js/components/inputs/selects/ItemSelectInput.js @@ -13,9 +13,9 @@ function Item(props) {
  • - + {props.text} - +
  • ) } else { @@ -23,9 +23,9 @@ function Item(props) {
  • - + {props.text} - +
  • ) } @@ -92,8 +92,8 @@ class ItemSelectInput extends React.Component { } getSelected() { - if(this.props.many){ - if(this.props.selected){ + if (this.props.many){ + if (this.props.selected){ return typeof this.props.selected !== 'object' ? [this.props.selected] : this.props.selected } else { return [] @@ -124,8 +124,8 @@ class ItemSelectInput extends React.Component { isSelected={true} text={item[this.props.attribute]} onClick={() => this.removeValue(item.id)} /> - )) - + )) + const results = this.props.results .filter(id => this.isNotSelected(id)) .map(id => this.props.entities[id]) @@ -153,7 +153,7 @@ class ItemSelectInput extends React.Component { value={this.state.query} fill={true} placeholder='Search' /> - {createButton} + {createButton}
      @@ -218,7 +218,7 @@ class ItemSelectInput extends React.Component { className='pt-button c-item-list__header__filters__filter' onClick={() => this.refs.dropdown.open()}> {this.props.editMessage} - +
    diff --git a/dispatch/static/manager/src/js/components/inputs/selects/PollSelectInput.js b/dispatch/static/manager/src/js/components/inputs/selects/PollSelectInput.js new file mode 100644 index 000000000..cc2a2ffc7 --- /dev/null +++ b/dispatch/static/manager/src/js/components/inputs/selects/PollSelectInput.js @@ -0,0 +1,61 @@ +import React from 'react' +import { connect } from 'react-redux' + +import ItemSelectInput from './ItemSelectInput' + +import pollsActions from '../../../actions/PollsActions' + +class PollSelectInputComponent extends React.Component { + + listPolls(query) { + let queryObj = {} + + if (query) { + queryObj['q'] = query + } + + this.props.listPolls(this.props.token, queryObj) + } + + render() { + const label = this.props.many ? 'polls' : 'poll' + + return ( + this.props.onChange(selected)} + fetchResults={(query) => this.listPolls(query)} + attribute='name' + editMessage={this.props.selected ? `Edit ${label}` : `Add ${label}`} /> + ) + } + +} + +const mapStateToProps = (state) => { + return { + polls: state.app.polls.list, + entities: { + polls: state.app.entities.polls + }, + token: state.app.auth.token + } +} + +const mapDispatchToProps = (dispatch) => { + return { + listPolls: (token, query) => { + dispatch(pollsActions.list(token, query)) + } + } +} + +const PollSelectInput = connect( + mapStateToProps, + mapDispatchToProps +)(PollSelectInputComponent) + +export default PollSelectInput diff --git a/dispatch/static/manager/src/js/components/integrations/FBInstantArticles.js b/dispatch/static/manager/src/js/components/integrations/FBInstantArticles.js deleted file mode 100644 index e3c321480..000000000 --- a/dispatch/static/manager/src/js/components/integrations/FBInstantArticles.js +++ /dev/null @@ -1,136 +0,0 @@ -import React from 'react' - -import { AnchorButton, Intent } from '@blueprintjs/core' - -import { FormInput, TextInput, SelectInput } from '../inputs' - -function fbLoginURI(clientId, redirectURI) { - return `https://www.facebook.com/v2.8/dialog/oauth?client_id=${clientId}&redirect_uri=${window.location.origin}/admin${redirectURI}?callback=1&scope=pages_manage_instant_articles,pages_show_list` -} - -export function InstantArticlesClientEditor(props) { - /* Renders a panel with fields to edit the Facebook client information */ - - const cancelButton = ( - - Cancel - - ) - - return ( -
    - - props.onChange({ client_id: e.target.value }) } /> - - - - props.onChange({ client_secret: e.target.value }) } /> - - - - - Save settings - - {props.editMode ? cancelButton : null} - -
    - ) -} - -export function InstantArticlesPageEditor(props) { - /* Renders a panel that either shows a Facebook login button or a dropdown list of pages */ - - const pages = - - const login = - - return ( -
    - - - - - Change app information - - - - {props.pages ? pages : login} - -
    - ) -} - -export function InstantArticlesEnabledEditor(props) { - /* Renders a panel that shows the currently enabled Facebook Page, with option to remove */ - - return ( - -
    - - Remove integration - -
    - ) -} - -function InstanceArticlesLogin(props) { - /* Renders a button that opens a Facebook login dialog */ - - return ( - -
    - Authenticate with Facebook -
    - ) -} - -function InstantArticlesPageDropdown(props) { - /* Renders a dropdown list to select a Facebook page */ - - const pageMap = props.pages.reduce((pageMap, page) => { - pageMap[page.id] = page - return pageMap - }, {}) - - const options = props.pages.map(page => [page.id, page.name]) - - return ( - - - props.onChange(pageMap[e.target.value])} /> - - Enable Instant Articles - - - - ) -} diff --git a/dispatch/static/manager/src/js/components/modals/ImageManager/ImagePanel.js b/dispatch/static/manager/src/js/components/modals/ImageManager/ImagePanel.js index 4b63c08b2..d67609098 100644 --- a/dispatch/static/manager/src/js/components/modals/ImageManager/ImagePanel.js +++ b/dispatch/static/manager/src/js/components/modals/ImageManager/ImagePanel.js @@ -39,7 +39,7 @@ export default function ImagePanel(props) { props.update('tags', tags) } /> + update={tags => props.update('tags', tags)} />
    diff --git a/dispatch/static/manager/src/js/components/modals/ImageManager/ImageThumb.js b/dispatch/static/manager/src/js/components/modals/ImageManager/ImageThumb.js index b2dff5be0..9b9941107 100644 --- a/dispatch/static/manager/src/js/components/modals/ImageManager/ImageThumb.js +++ b/dispatch/static/manager/src/js/components/modals/ImageManager/ImageThumb.js @@ -14,12 +14,11 @@ export default function ImageThumb(props) { return (
    + style={props.width ? { width: props.width} : {}}>
    props.selectImage(props.image.id)} - style={style}>
    + style={style} />
    ) } diff --git a/dispatch/static/manager/src/js/components/modals/ImageManager/index.js b/dispatch/static/manager/src/js/components/modals/ImageManager/index.js index 15aadd7e5..d1e6be318 100644 --- a/dispatch/static/manager/src/js/components/modals/ImageManager/index.js +++ b/dispatch/static/manager/src/js/components/modals/ImageManager/index.js @@ -44,7 +44,7 @@ class ImageManagerComponent extends React.Component { } loadMore() { - if(this.props.images.count > this.state.limit){ + if (this.props.images.count > this.state.limit){ this.setState(prevState => ({ limit: prevState.limit + 10 }), this.props.listImages(this.props.token, {limit: this.state.limit, ordering: '-created_at'})) @@ -150,13 +150,13 @@ class ImageManagerComponent extends React.Component { ref={(node) => { this.images = node }}>{images}
    {!this.props.many ? -
    - {image ? imagePanel : null} -
    : null} +
    + {image ? imagePanel : null} +
    : null}
    -
    +
    this.insertImage()}>Insert diff --git a/dispatch/static/manager/src/js/constants/ActionTypes.js b/dispatch/static/manager/src/js/constants/ActionTypes.js index 06c845146..859233997 100644 --- a/dispatch/static/manager/src/js/constants/ActionTypes.js +++ b/dispatch/static/manager/src/js/constants/ActionTypes.js @@ -15,6 +15,7 @@ export const GALLERIES = resourceActionTypes('GALLERIES') export const EVENTS = resourceActionTypes('EVENTS', ['COUNT_PENDING']) export const USERS = resourceActionTypes('USERS') export const VIDEOS = resourceActionTypes('VIDEOS') +export const POLLS = resourceActionTypes('POLLS') // Authentication actions export const AUTH = actionTypes('AUTH', [ diff --git a/dispatch/static/manager/src/js/constants/Schemas.js b/dispatch/static/manager/src/js/constants/Schemas.js index cee714ca7..761b48f1e 100644 --- a/dispatch/static/manager/src/js/constants/Schemas.js +++ b/dispatch/static/manager/src/js/constants/Schemas.js @@ -16,6 +16,7 @@ export const widgetSchema = new Schema('widgets') export const eventSchema = new Schema('events') export const userSchema = new Schema('users') export const videoSchema = new Schema('videos') +export const pollSchema = new Schema('polls') articleSchema.define({ section: sectionSchema, diff --git a/dispatch/static/manager/src/js/containers/AppContainer.js b/dispatch/static/manager/src/js/containers/AppContainer.js index 714f3aac6..aa1fb83ff 100644 --- a/dispatch/static/manager/src/js/containers/AppContainer.js +++ b/dispatch/static/manager/src/js/containers/AppContainer.js @@ -5,7 +5,7 @@ import * as userActions from '../actions/UserActions' class App extends React.Component { - componentWillMount() { + componentDidMount() { this.checkAuthenticated() } diff --git a/dispatch/static/manager/src/js/containers/BasicContainer.js b/dispatch/static/manager/src/js/containers/BasicContainer.js index 1836c8fbc..0c3058cc6 100644 --- a/dispatch/static/manager/src/js/containers/BasicContainer.js +++ b/dispatch/static/manager/src/js/containers/BasicContainer.js @@ -1,5 +1,5 @@ import React from 'react' export default function BasicContainer(props) { - return (
    {props.children}
    ) + return
    {props.children}
    } diff --git a/dispatch/static/manager/src/js/containers/MainContainer.js b/dispatch/static/manager/src/js/containers/MainContainer.js index 71de1a5b8..0614c8774 100644 --- a/dispatch/static/manager/src/js/containers/MainContainer.js +++ b/dispatch/static/manager/src/js/containers/MainContainer.js @@ -13,9 +13,9 @@ require('../../styles/components/toaster.scss') class Main extends React.Component { - componentWillMount() { - //this.props.countPending(this.props.token, { pending: 1, limit: 0 }) - } + // componentWillMount() { + // this.props.countPending(this.props.token, { pending: 1, limit: 0 }) + // } componentDidMount() { this.props.setupToaster(this.refs.toaster) @@ -32,7 +32,10 @@ class Main extends React.Component { render() { return (
    - +
    {this.props.children} {this.props.modal.component ? this.renderModal() : null} diff --git a/dispatch/static/manager/src/js/index.js b/dispatch/static/manager/src/js/index.js index 005fa3b8b..263d804ac 100644 --- a/dispatch/static/manager/src/js/index.js +++ b/dispatch/static/manager/src/js/index.js @@ -45,10 +45,6 @@ render(( - - - - @@ -97,6 +93,12 @@ render(( + + + + + + diff --git a/dispatch/static/manager/src/js/pages/Articles/ArticleIndexPage.js b/dispatch/static/manager/src/js/pages/Articles/ArticleIndexPage.js index bf9e62e5d..6f15428cd 100644 --- a/dispatch/static/manager/src/js/pages/Articles/ArticleIndexPage.js +++ b/dispatch/static/manager/src/js/pages/Articles/ArticleIndexPage.js @@ -53,13 +53,11 @@ function ArticlePageComponent(props) { props.searchArticles(props.location.query.author, section, props.location.query.q)} - />, + update={(section) => props.searchArticles(props.location.query.author, section, props.location.query.q)} />, props.searchArticles(author, props.location.query.section, props.location.query.q)} - /> + update={(author) => props.searchArticles(author, props.location.query.section, props.location.query.q)} /> ] return ( diff --git a/dispatch/static/manager/src/js/pages/DashboardPage.js b/dispatch/static/manager/src/js/pages/DashboardPage.js index a4d8d2147..b500ce0b6 100644 --- a/dispatch/static/manager/src/js/pages/DashboardPage.js +++ b/dispatch/static/manager/src/js/pages/DashboardPage.js @@ -29,7 +29,7 @@ class DashboardPageComponent extends React.Component {
    {elem.meta.author} {elem.meta.count == 1 ? ` ${elem.meta.action} ` : ` made ${elem.meta.count} ${elem.meta.action} to ` } - + {` ${moment(elem.timestamp).from(moment())}`}
    @@ -44,7 +44,7 @@ class DashboardPageComponent extends React.Component { ) - ) + ) } render() { @@ -61,12 +61,12 @@ class DashboardPageComponent extends React.Component {
    • - New Article + New Article
    • - New Page + New Page
    diff --git a/dispatch/static/manager/src/js/pages/Events/EventAuditPage.js b/dispatch/static/manager/src/js/pages/Events/EventAuditPage.js index e2a24bb60..b6e367273 100644 --- a/dispatch/static/manager/src/js/pages/Events/EventAuditPage.js +++ b/dispatch/static/manager/src/js/pages/Events/EventAuditPage.js @@ -50,8 +50,8 @@ class EventAuditPage extends React.Component { getDisplayedCount(props) { props = props || this.props return props.events.ids - .map(id => props.entities.events[id]) - .filter(event => event && event.is_submission).length + .map(id => props.entities.events[id]) + .filter(event => event && event.is_submission).length } checkPage() { @@ -108,18 +108,18 @@ class EventAuditPage extends React.Component { this.props.history.goBack()}> - Back + onClick={() => this.props.router.goBack()}> + Back {this.props.events.count} event{this.props.events.count == 1 ? '' : 's'} pending approval - {this.props.events.count ? pagination : null} + {this.props.events.count ? pagination : null}
    - {events} + {events}
    diff --git a/dispatch/static/manager/src/js/pages/Events/EventPage.js b/dispatch/static/manager/src/js/pages/Events/EventPage.js index df5f4cfb4..1d63e7154 100644 --- a/dispatch/static/manager/src/js/pages/Events/EventPage.js +++ b/dispatch/static/manager/src/js/pages/Events/EventPage.js @@ -6,6 +6,6 @@ export default function EventPage(props) { return ( + goBack={props.router.goBack} /> ) } diff --git a/dispatch/static/manager/src/js/pages/Events/NewEventPage.js b/dispatch/static/manager/src/js/pages/Events/NewEventPage.js index 47db5889d..adfe30967 100644 --- a/dispatch/static/manager/src/js/pages/Events/NewEventPage.js +++ b/dispatch/static/manager/src/js/pages/Events/NewEventPage.js @@ -6,6 +6,6 @@ export default function NewEventPage(props) { return ( + goBack={props.router.goBack} /> ) } diff --git a/dispatch/static/manager/src/js/pages/FilesPage.js b/dispatch/static/manager/src/js/pages/FilesPage.js index 61992a4b1..b3247e92c 100644 --- a/dispatch/static/manager/src/js/pages/FilesPage.js +++ b/dispatch/static/manager/src/js/pages/FilesPage.js @@ -109,7 +109,10 @@ class FilesPageComponent extends React.Component { headers={['Filename', 'Created', 'Updated']} columns={[ - item => ({item.name}), + item => ({item.name}), item => humanizeDatetime(item.created_at), item => humanizeDatetime(item.updated_at), ]} @@ -122,8 +125,7 @@ class FilesPageComponent extends React.Component { toggleAllItems: this.props.toggleAllFiles, deleteItems: (fileIds) => this.handleDeleteFiles(fileIds), searchItems: (query) => this.handleSearchFiles(query) - }} - /> + }} />
    this.onDropzoneClick()}>

    Drag files into window or click here to upload

    diff --git a/dispatch/static/manager/src/js/pages/Galleries/GalleryPage.js b/dispatch/static/manager/src/js/pages/Galleries/GalleryPage.js index fc2faba21..207d541fd 100644 --- a/dispatch/static/manager/src/js/pages/Galleries/GalleryPage.js +++ b/dispatch/static/manager/src/js/pages/Galleries/GalleryPage.js @@ -6,7 +6,7 @@ export default function TagPage(props) { return ( ) } diff --git a/dispatch/static/manager/src/js/pages/Galleries/NewGalleryPage.js b/dispatch/static/manager/src/js/pages/Galleries/NewGalleryPage.js index c387ae062..c3f71fba2 100644 --- a/dispatch/static/manager/src/js/pages/Galleries/NewGalleryPage.js +++ b/dispatch/static/manager/src/js/pages/Galleries/NewGalleryPage.js @@ -6,7 +6,7 @@ export default function NewTagPage(props) { return ( ) } diff --git a/dispatch/static/manager/src/js/pages/Images/EditImagePage.js b/dispatch/static/manager/src/js/pages/Images/EditImagePage.js index ae0394f32..bb1915318 100644 --- a/dispatch/static/manager/src/js/pages/Images/EditImagePage.js +++ b/dispatch/static/manager/src/js/pages/Images/EditImagePage.js @@ -7,7 +7,7 @@ export default function EditImagePage(props) { return ( ) } diff --git a/dispatch/static/manager/src/js/pages/Images/ImagesIndexPage.js b/dispatch/static/manager/src/js/pages/Images/ImagesIndexPage.js index e1d81efe0..eb6b8468a 100644 --- a/dispatch/static/manager/src/js/pages/Images/ImagesIndexPage.js +++ b/dispatch/static/manager/src/js/pages/Images/ImagesIndexPage.js @@ -45,7 +45,7 @@ class ImagesPageComponent extends React.Component { offset: (this.getCurrentPage() - 1) * DEFAULT_LIMIT } - + if (this.props.location.query.author) { query.author = this.props.location.query.author } @@ -100,10 +100,8 @@ class ImagesPageComponent extends React.Component { } renderThumb(url) { - return( -
    - -
    + return ( +
    ) } @@ -131,18 +129,16 @@ class ImagesPageComponent extends React.Component { item => humanizeDatetime(item.created_at, true), item => humanizeDatetime(item.updated_at, true) ]) - + const filters = [ this.props.searchImages(author, this.props.location.query.tags, this.props.location.query.q)} - />, + update={(author) => this.props.searchImages(author, this.props.location.query.tags, this.props.location.query.q)} />, this.props.searchImages(this.props.location.query.author, tags, this.props.location.query.q)} - /> + update={(tags) => this.props.searchImages(this.props.location.query.author, tags, this.props.location.query.q)} /> ] return ( @@ -180,8 +176,7 @@ class ImagesPageComponent extends React.Component { toggleAllItems: this.props.toggleAllImages, deleteItems: (imageIds) => this.handleDeleteImages(imageIds), searchItems: (query) => this.handleSearchImages(query) - }} - /> + }} />
    this.onDropzoneClick()}>

    Drag images into window or click here to upload

    diff --git a/dispatch/static/manager/src/js/pages/Integrations/FBInstantArticlesIntegrationPage.js b/dispatch/static/manager/src/js/pages/Integrations/FBInstantArticlesIntegrationPage.js deleted file mode 100644 index 87fbe602e..000000000 --- a/dispatch/static/manager/src/js/pages/Integrations/FBInstantArticlesIntegrationPage.js +++ /dev/null @@ -1,191 +0,0 @@ -import React from 'react' -import R from 'ramda' -import { connect } from 'react-redux' -import { push } from 'react-router-redux' -import DocumentTitle from 'react-document-title' - -import Panel from '../../components/Panel' - -import { - InstantArticlesClientEditor, - InstantArticlesPageEditor, - InstantArticlesEnabledEditor -} from '../../components/integrations/FBInstantArticles' - -import * as integrationActions from '../../actions/IntegrationActions' - -const INTEGRATION_ID = 'fb-instant-articles' - -class FBInstantArticlesIntegrationPageComponent extends React.Component { - - constructor(props) { - super(props) - - this.cachedSettings = null - - this.state = { - editMode: false - } - } - - componentWillMount() { - - this.props.fetchIntegration(this.props.token, INTEGRATION_ID) - - if (this.props.location.query.callback) { - this.props.integrationCallback( - this.props.token, - INTEGRATION_ID, - this.props.location.query - ) - } - - } - - componentWillReceiveProps(nextProps) { - - if (R.path(['integration', 'settings', 'client_configured'], nextProps)) { - this.setState({ editMode: false }) - } - - } - - enterEditMode() { - // Temporarily save old settings so we can revert - this.cachedSettings = this.props.integration.settings - - this.updateSettings({ client_configured: false }) - this.setState({ editMode: true }) - } - - exitEditMode() { - // Revert old settings - this.updateSettings(this.cachedSettings) - - this.setState({ editMode: false }) - } - - updateSettings(settings) { - return this.props.updateIntegration( - INTEGRATION_ID, - { settings: R.merge(this.props.integration.settings, settings) } - ) - } - - updateFacebookPage(page) { - return this.updateSettings({ - page_id: page.id, - page_name: page.name, - page_access_token: page.access_token - }) - } - - componentDidUpdate() { - - if (!this.props.integration.settings.page_id && R.path(['callback', 'pages', 'data'], this.props.integration)) { - const page = this.props.integration.callback.pages.data[0] - this.updateFacebookPage(page) - this.props.resetURI(this.props.location.pathname) - } - - } - - renderFacebookInstantArticles() { - - const settings = this.props.integration.settings - const callback = this.props.integration.callback || {} - - if (settings.page_configured) { - return ( - this.props.deleteIntegration(this.props.token, INTEGRATION_ID)} /> - ) - } - - if (settings.client_configured && !this.state.editMode) { - return ( - this.updateFacebookPage(data)} - onSave={() => this.props.saveIntegration(this.props.token, INTEGRATION_ID, this.props.integration)} - enterEditMode={() => this.enterEditMode()} /> - ) - } - - return ( - this.updateSettings(data)} - onSave={() => this.props.saveIntegration(this.props.token, INTEGRATION_ID, this.props.integration)} - editMode={this.state.editMode} - exitEditMode={() => this.exitEditMode()}/> - ) - } - - render() { - - if (this.props.integration) { - return ( - - - {this.renderFacebookInstantArticles()} - - - ) - } - - return ( -
    Loading
    - ) - - } - -} - -const mapStateToProps = (state) => { - - let integration = state.app.integrations.integrations[INTEGRATION_ID] || null - - if (integration) { - integration.settings = integration.settings || {} - } - - return { - token: state.app.auth.token, - integration: integration - } -} - -const mapDispatchToProps = (dispatch) => { - return { - integrationCallback: (token, integrationId, query) => { - dispatch(integrationActions.integrationCallback(token, integrationId, query)) - }, - fetchIntegration: (token, integrationId) => { - dispatch(integrationActions.fetchIntegration(token, integrationId)) - }, - saveIntegration: (token, integrationId, data) => { - dispatch(integrationActions.saveIntegration(token, integrationId, data)) - }, - deleteIntegration: (token, integrationId) => { - dispatch(integrationActions.deleteIntegration(token, integrationId)) - }, - updateIntegration: (integrationId, data) => { - dispatch(integrationActions.updateIntegration(integrationId, data)) - }, - resetURI: (pathname) => { - dispatch(push(pathname)) - } - } -} - -const FBInstantArticlesIntegrationPage = connect( - mapStateToProps, - mapDispatchToProps -)(FBInstantArticlesIntegrationPageComponent) - -export default FBInstantArticlesIntegrationPage diff --git a/dispatch/static/manager/src/js/pages/Integrations/IntegrationsIndexPage.js b/dispatch/static/manager/src/js/pages/Integrations/IntegrationsIndexPage.js deleted file mode 100644 index 8ac83cdf7..000000000 --- a/dispatch/static/manager/src/js/pages/Integrations/IntegrationsIndexPage.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import DocumentTitle from 'react-document-title' - -import SidebarNav from '../../components/SidebarNav' - -const NAV_ITEMS = [ - { path: '/integrations/fb-instant-articles/', label: 'Facebook Instant Articles' } -] - -export default function IntegrationsIndexPage(props) { - return ( - -
    - -
    {props.children}
    -
    -
    - ) -} diff --git a/dispatch/static/manager/src/js/pages/Integrations/index.js b/dispatch/static/manager/src/js/pages/Integrations/index.js deleted file mode 100644 index 47f7247b9..000000000 --- a/dispatch/static/manager/src/js/pages/Integrations/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import IntegrationsIndexPage from './IntegrationsIndexPage' -import FBInstantArticlesIntegrationPage from './FBInstantArticlesIntegrationPage' - -export { - IntegrationsIndexPage as Index, - FBInstantArticlesIntegrationPage as FBInstantArticles -} diff --git a/dispatch/static/manager/src/js/pages/Issues/IssuePage.js b/dispatch/static/manager/src/js/pages/Issues/IssuePage.js index 1362fc881..1fd8f6d52 100644 --- a/dispatch/static/manager/src/js/pages/Issues/IssuePage.js +++ b/dispatch/static/manager/src/js/pages/Issues/IssuePage.js @@ -6,7 +6,7 @@ export default function IssuePage(props) { return ( ) } diff --git a/dispatch/static/manager/src/js/pages/Issues/NewIssuePage.js b/dispatch/static/manager/src/js/pages/Issues/NewIssuePage.js index 9bf504a1f..e26424605 100644 --- a/dispatch/static/manager/src/js/pages/Issues/NewIssuePage.js +++ b/dispatch/static/manager/src/js/pages/Issues/NewIssuePage.js @@ -6,7 +6,7 @@ export default function NewIssuePage(props) { return ( ) } diff --git a/dispatch/static/manager/src/js/pages/ItemIndexPage.js b/dispatch/static/manager/src/js/pages/ItemIndexPage.js index f13793bf8..f671f193d 100644 --- a/dispatch/static/manager/src/js/pages/ItemIndexPage.js +++ b/dispatch/static/manager/src/js/pages/ItemIndexPage.js @@ -121,7 +121,7 @@ export default class ListItemsPageComponent extends React.Component { emptyMessage={`You haven\'t created any ${this.props.typePlural} yet.`} createHandler={() => ( - Create {this.typeString} + Create {this.typeString} ) } diff --git a/dispatch/static/manager/src/js/pages/LoginPage.js b/dispatch/static/manager/src/js/pages/LoginPage.js index 7ba2eadff..c2af928ef 100644 --- a/dispatch/static/manager/src/js/pages/LoginPage.js +++ b/dispatch/static/manager/src/js/pages/LoginPage.js @@ -47,14 +47,14 @@ class LoginPageComponent extends React.Component { type="email" placeholder='Email' value={this.state.email} - onChange={this.onChangeEmail} />
    + onChange={this.onChangeEmail} />

    + onChange={this.onChangePassword} />
    ) } } diff --git a/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/CodeEmbed.js b/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/CodeEmbed.js index a93500071..8e43cc1f9 100644 --- a/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/CodeEmbed.js +++ b/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/CodeEmbed.js @@ -18,7 +18,7 @@ const MODES = [ function CodeEmbedComponent(props) { - return( + return (
    props.updateField('content', content)} - editorProps={{$blockScrolling: true}} - /> + editorProps={{$blockScrolling: true}} />
    props.updateField('mode', e.target.value)}/> + onChange={(e) => props.updateField('mode', e.target.value)} />
    diff --git a/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/WidgetEmbed.js b/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/WidgetEmbed.js index a54398b8e..fef2a0113 100644 --- a/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/WidgetEmbed.js +++ b/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/WidgetEmbed.js @@ -15,22 +15,22 @@ function WidgetEmbedComponent(props) { return (
    - - { - props.updateField('widget_id', widgetId) - props.stopEditing() - }} /> - + + { + props.updateField('widget_id', widgetId) + props.stopEditing() + }} /> + - - - + + +
    ) diff --git a/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/index.js b/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/index.js index 5b3b1cb41..d4db1b36a 100644 --- a/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/index.js +++ b/dispatch/static/manager/src/js/vendor/dispatch-editor/embeds/index.js @@ -3,6 +3,7 @@ import VideoEmbed from './VideoEmbed' import CodeEmbed from './CodeEmbed' import PullQuoteEmbed from './PullQuoteEmbed' import GalleryEmbed from './GalleryEmbed' +import WidgetEmbed from './WidgetEmbed' export { ImageEmbed, @@ -10,4 +11,5 @@ export { PullQuoteEmbed, CodeEmbed, GalleryEmbed, + WidgetEmbed } diff --git a/dispatch/static/manager/src/js/vendor/dispatch-editor/helpers/convertJSON.js b/dispatch/static/manager/src/js/vendor/dispatch-editor/helpers/convertJSON.js index 17ecf766e..ff647ddca 100644 --- a/dispatch/static/manager/src/js/vendor/dispatch-editor/helpers/convertJSON.js +++ b/dispatch/static/manager/src/js/vendor/dispatch-editor/helpers/convertJSON.js @@ -74,7 +74,7 @@ function blockToJSON(jsonBlocks, block) { const type = block.getType() const lastType = jsonBlocks.last() ? jsonBlocks.last().type : null - switch(type) { + switch (type) { case DRAFT_TYPES.EMBED: return embedToJSON(jsonBlocks, block, lastType) case DRAFT_TYPES.PARAGRAPH: diff --git a/dispatch/static/manager/src/styles/components/poll.scss b/dispatch/static/manager/src/styles/components/poll.scss new file mode 100644 index 000000000..a6940ea5a --- /dev/null +++ b/dispatch/static/manager/src/styles/components/poll.scss @@ -0,0 +1,202 @@ +@import '../utilities/colors'; + +.poll-preview{ + em { + font-style: italic; + } +} +.poll-container { + //structure + padding-left: 1.5rem; + padding-right: 1.5rem; + margin: 15px 0; + max-width: 500px; + + // Border + border: 2px solid black; + + h1 { + display: block; + font-size: 2em; + -webkit-margin-before: 0.67em; + -webkit-margin-after: 0.67em; + -webkit-margin-start: 0px; + -webkit-margin-end: 0px; + font-weight: bold; + } +} + +.poll-answer-form { + // Structure + display: flex; + flex-direction: column; + max-width: 95%; + margin-bottom: 15px; +} + +.poll-button-label { + // Structure + position: relative; + display: flex; + align-items: center; + margin: 5px 0px; + padding: 8px; + + // Text + color: inherit; + font-size: 16px; + font-family: "ub-lft-etica", "LFT Etica", "Helvetica Neue", "Helvetica", "Arial", sans-serif; + + // Extra + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + &:hover .poll-button .poll-button-inner { + // Extra + opacity: 1; + } + +} + +.poll-results{ + .poll-button-label{ + // Extra + cursor: auto; + } +} +.poll-voting{ + .poll-button-label{ + // Extra + cursor: pointer; + } +} + +.poll-result-bar { + // Background + background-color: $color-steel-blue; + border-radius: 5px; + + + // Structure + position: absolute; + display: flex; + left: 0; + height: 100%; + z-index: -1; + + // Extra + transition: width .5s, opacity .25s; +} + +.poll-container input { + // Structure + position: absolute; + + // Extra + cursor: pointer; + opacity: 0; +} + +.poll-percentage { + // Structure + position: absolute; + margin-left: 3px; + + // Text + font-size: 14px; +} + +.poll-button { + // Structure + position: absolute; + height: 15px; + width: 15px; + margin-left: 3px; + display: flex; + align-items: center; + justify-content: center; + + // Border + border-radius: 15px; + border: 3px solid $color-steel-blue; + + // Extra + transition: opacity .1s; + + .poll-button-inner { + // Structure + height: 11px; + width: 11px; + + // Border + border-radius: 11px; + + // Background + background-color: $color-steel-blue; + + // Extra + opacity: 0; + transition: opacity .1s; + } +} + +.poll-selected { + // Structure + width: 15px; + height: 15px; + align-self: center; + position: absolute; + right: -25px; + display: flex; + align-items: center; + justify-content: center; + + // Border + border: 3px solid $color-steel-blue; + border-radius: 15px; + + // Extra + opacity: 1; + + &:after{ + // Structure + left: 9px; + top: 5px; + width: 5px; + height: 10px; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + + // Border + border: solid white; + border-width: 0 3px 3px 0; + } + + .poll-checkmark { + // Structure + position:relative; + top: -1px; + width: 4px; + height: 9px; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + + // Border + border: solid $color-steel-blue; + border-width: 0 3px 3px 0; + } +} + +.poll-answer-text { + // Structure + margin-left: 3rem; +} + +.poll-results-button { + // Structure + margin-top: 15px; +} \ No newline at end of file diff --git a/dispatch/static/manager/src/styles/components/poll_form.scss b/dispatch/static/manager/src/styles/components/poll_form.scss new file mode 100644 index 000000000..a01c46fea --- /dev/null +++ b/dispatch/static/manager/src/styles/components/poll_form.scss @@ -0,0 +1,40 @@ +.c-poll-form-container{ + // Structure + display: flex; + flex-direction: row; +} +.c-poll-container { + // Structure + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} +.c-equal-width{ + // Structure + flex: 1; + padding: 0 1.5rem; +} +.c-poll-form__results-select { + // Structure + width: 9em; +} +.poll-form.pt-icon-standard { + // Structure + margin-right: 0.25rem; + position: absolute; + top: 0; + left: 140px; + cursor: pointer; + &:hover { + text-decoration: underline; + opacity: .8; + } +} +.poll-form .pt-icon-standard-text{ + // Structure + position: relative; + top: 2px; + margin-left: .25rem; +} diff --git a/dispatch/static/manager/yarn-error.log b/dispatch/static/manager/yarn-error.log index cc73122bb..33687c075 100644 --- a/dispatch/static/manager/yarn-error.log +++ b/dispatch/static/manager/yarn-error.log @@ -1,8 +1,8 @@ Arguments: - /Users/petersiemens/.nvm/versions/node/v6.11.5/bin/node /usr/local/bin/yarn upgrade blueprintjs/core + /Users/petersiemens/.nvm/versions/node/v6.11.5/bin/node /usr/local/bin/yarn upgrade react-ave PATH: - /Users/petersiemens/.pyenv/shims:/usr/local/Cellar/php56/5.6.33_9/bin:/Users/petersiemens/.rbenv/shims:/Users/petersiemens/.rbenv/bin:/Users/petersiemens/.nvm/versions/node/v6.11.5/bin:/Users/petersiemens/.rvm/gems/ruby-2.1.1/bin:/Users/petersiemens/.rvm/gems/ruby-2.1.1@global/bin:/Users/petersiemens/.rvm/rubies/ruby-2.1.1/bin:/opt/local/bin:/opt/local/sbin:/opt/local/bin:/opt/local/sbin:/anaconda/bin:/Users/petersiemens/anaconda/bin:/Applications/MAMP/bin/php/php5.5.3/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/share/npm/bin/:/opt/X11/bin:/usr/local/go/bin:/Applications/Wireshark.app/Contents/MacOS:/usr/local/share/npm/bin/:/Users/petersiemens/.rvm/bin + /Users/petersiemens/dev/google-cloud-sdk/bin:/usr/local/opt/imagemagick@6/bin:/Users/petersiemens/.pyenv/shims:/usr/local/Cellar/php@5.6/5.6.36/bin:/Users/petersiemens/.rbenv/shims:/Users/petersiemens/.rbenv/bin:/Users/petersiemens/.nvm/versions/node/v6.11.5/bin:/Users/petersiemens/.rvm/gems/ruby-2.1.1/bin:/Users/petersiemens/.rvm/gems/ruby-2.1.1@global/bin:/Users/petersiemens/.rvm/rubies/ruby-2.1.1/bin:/opt/local/bin:/opt/local/sbin:/opt/local/bin:/opt/local/sbin:/anaconda/bin:/Users/petersiemens/anaconda/bin:/Applications/MAMP/bin/php/php5.5.3/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/share/npm/bin/:/usr/local/go/bin:/opt/X11/bin:/Applications/Wireshark.app/Contents/MacOS:/usr/local/share/npm/bin/:/Users/petersiemens/.rvm/bin:/Users/petersiemens/dev/cloud_sql_proxy:/Users/petersiemens/bin Yarn version: 0.19.1 @@ -16,7 +16,7 @@ Platform: npm manifest: { "name": "dispatch", - "version": "0.4.3", + "version": "0.4.7", "description": "The frontend framework for the Dispatch publishing platform.", "author": "Peter Siemens ", "homepage": "https://www.ubyssey.ca", @@ -24,11 +24,11 @@ npm manifest: "Ben Cook " ], "dependencies": { - "@blueprintjs/core": "^1.9.0", + "@blueprintjs/core": "1.35.7", "@blueprintjs/datetime": "^1.16.0", "babel": "^6.23.0", "babel-core": "^6.21.0", - "babel-loader": "^6.2.10", + "babel-loader": "^7.1.4", "babel-preset-es2015": "^6.13.2", "babel-preset-react": "^6.11.1", "class-autobind": "^0.1.4", @@ -37,7 +37,7 @@ npm manifest: "es6-promise": "^3.2.1", "eslint": "^3.17.1", "eslint-loader": "^1.6.3", - "eslint-plugin-react": "^6.10.0", + "eslint-plugin-react": "^7.9.1", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^0.10.0", "history": "^2.1.2", @@ -46,28 +46,28 @@ npm manifest: "jquery": "^3.1.1", "js-cookie": "^2.1.2", "moment": "^2.17.1", - "node-sass": "^4.5.0", + "node-sass": "^4.9.0", "normalizr": "^2.2.1", "qwery": "^4.0.0", "ramda": "^0.22.1", - "react": "^15.4.2", + "react": "^16.4.0", "react-ace": "^5.0.1", "react-addons-css-transition-group": "^15.4.1", "react-dnd": "^2.4.0", "react-dnd-html5-backend": "^2.4.1", "react-document-title": "^2.0.2", - "react-dom": "^15.3.1", + "react-dom": "^16.4.0", "react-dropzone": "^3.12.3", "react-measure": "^1.4.7", "react-redux": "^4.4.5", "react-redux-loading-bar": "^2.7.2", - "react-router": "^2.7.0", + "react-router": "^4.2.0", "react-router-redux": "^4.0.5", "redux": "^3.5.2", "redux-logger": "^3.0.6", "redux-promise-middleware": "^4.0.0", "redux-thunk": "^2.1.0", - "sass-loader": "^6.0.2", + "sass-loader": "^7.0.3", "style-loader": "^0.13.1", "url": "^0.11.0", "url-loader": "^0.5.7", @@ -99,9 +99,9 @@ Lockfile: # yarn lockfile v1 - "@blueprintjs/core@^1.3.0", "@blueprintjs/core@^1.9.0": - version "1.20.0" - resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-1.20.0.tgz#3a95cb4b854b0687c6ca60ef26a39b4e016b42b0" + "@blueprintjs/core@1.35.7", "@blueprintjs/core@^1.3.0": + version "1.35.7" + resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-1.35.7.tgz#e0e8042dd5b4d0a02cdbc65bbd1cb0b985f6bf32" dependencies: "@types/dom4" "^1.5.20" "@types/tether" "^1.1.27" @@ -248,6 +248,13 @@ Lockfile: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -262,13 +269,6 @@ Lockfile: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - array.prototype.find@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -311,7 +311,7 @@ Lockfile: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" - async@^2.1.2, async@^2.1.5, async@^2.4.1: + async@^2.1.2, async@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" dependencies: @@ -472,14 +472,13 @@ Lockfile: babel-runtime "^6.22.0" babel-template "^6.24.1" - babel-loader@^6.2.10: - version "6.4.1" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42d50b8ca" + babel-loader@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.4.tgz#e3463938bd4e6d55d1c174c5485d406a188ed015" dependencies: - find-cache-dir "^0.1.1" - loader-utils "^0.2.16" + find-cache-dir "^1.0.0" + loader-utils "^1.0.2" mkdirp "^0.5.1" - object-assign "^4.0.1" babel-messages@^6.23.0: version "6.23.0" @@ -1022,6 +1021,10 @@ Lockfile: version "1.0.30000690" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000690.tgz#ee4e0750070f6aae6f40e76477984449bd6cb48a" + caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1118,14 +1121,14 @@ Lockfile: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" - clone-deep@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.3.0.tgz#348c61ae9cdbe0edfe053d91ff4cc521d790ede8" + clone-deep@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" dependencies: for-own "^1.0.0" - is-plain-object "^2.0.1" - kind-of "^3.2.2" - shallow-clone "^0.1.2" + is-plain-object "^2.0.4" + kind-of "^6.0.0" + shallow-clone "^1.0.0" clone@^1.0.2: version "1.0.2" @@ -1187,6 +1190,10 @@ Lockfile: dependencies: delayed-stream "~1.0.0" + commander@^2.9.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1260,7 +1267,7 @@ Lockfile: safe-buffer "^5.0.1" sha.js "^2.4.8" - create-react-class@^15.5.1, create-react-class@^15.6.0: + create-react-class@^15.5.1: version "15.6.0" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4" dependencies: @@ -1494,13 +1501,6 @@ Lockfile: lodash "^4.2.0" redux "^3.2.0" - doctrine@^1.2.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - doctrine@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" @@ -1508,6 +1508,12 @@ Lockfile: esutils "^2.0.2" isarray "^1.0.0" + doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + dependencies: + esutils "^2.0.2" + dom-helpers@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a" @@ -1677,15 +1683,14 @@ Lockfile: object-hash "^1.1.4" rimraf "^2.6.1" - eslint-plugin-react@^6.10.0: - version "6.10.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78" + eslint-plugin-react@^7.9.1: + version "7.9.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.9.1.tgz#101aadd15e7c7b431ed025303ac7b421a8e3dc15" dependencies: - array.prototype.find "^2.0.1" - doctrine "^1.2.2" - has "^1.0.1" - jsx-ast-utils "^1.3.4" - object.assign "^4.0.4" + doctrine "^2.1.0" + has "^1.0.2" + jsx-ast-utils "^2.0.1" + prop-types "^15.6.1" eslint@^3.17.1: version "3.19.0" @@ -1859,7 +1864,19 @@ Lockfile: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" - fbjs@^0.8.0, fbjs@^0.8.7, fbjs@^0.8.9: + fbjs@^0.8.0, fbjs@^0.8.16, fbjs@^0.8.9: + version "0.8.16" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + + fbjs@^0.8.7: version "0.8.12" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04" dependencies: @@ -1919,6 +1936,14 @@ Lockfile: mkdirp "^0.5.1" pkg-dir "^1.0.0" + find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -1926,7 +1951,7 @@ Lockfile: path-exists "^2.0.0" pinkie-promise "^2.0.0" - find-up@^2.0.0: + find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" dependencies: @@ -2009,9 +2034,9 @@ Lockfile: mkdirp ">=0.5 0" rimraf "2" - function-bind@^1.0.2, function-bind@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" + function-bind@^1.1.0, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" gauge@~2.7.3: version "2.7.4" @@ -2077,6 +2102,16 @@ Lockfile: dependencies: is-glob "^2.0.0" + glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" @@ -2119,6 +2154,15 @@ Lockfile: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + har-validator@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" @@ -2144,11 +2188,11 @@ Lockfile: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + has@^1.0.1, has@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" dependencies: - function-bind "^1.0.2" + function-bind "^1.1.1" hash-base@^2.0.0: version "2.0.2" @@ -2180,6 +2224,16 @@ Lockfile: query-string "^3.0.0" warning "^2.0.0" + history@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b" + dependencies: + invariant "^2.2.1" + loose-envify "^1.2.0" + resolve-pathname "^2.2.0" + value-equal "^0.4.0" + warning "^3.0.0" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -2196,6 +2250,10 @@ Lockfile: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" + hoist-non-react-statics@^2.3.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -2328,7 +2386,7 @@ Lockfile: version "1.0.3" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" - invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1: + invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: @@ -2352,7 +2410,7 @@ Lockfile: dependencies: binary-extensions "^1.0.0" - is-buffer@^1.0.2, is-buffer@^1.1.5: + is-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" @@ -2410,7 +2468,7 @@ Lockfile: dependencies: is-extglob "^1.0.0" - is-my-json-valid@^2.10.0: + is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: version "2.16.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" dependencies: @@ -2451,11 +2509,11 @@ Lockfile: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - is-plain-object@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.3.tgz#c15bf3e4b66b62d72efaf2925848663ecbc619b6" + is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: - isobject "^3.0.0" + isobject "^3.0.1" is-posix-bracket@^0.1.0: version "0.1.1" @@ -2507,6 +2565,10 @@ Lockfile: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2521,9 +2583,9 @@ Lockfile: dependencies: isarray "1.0.0" - isobject@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.0.tgz#39565217f3661789e8a0a0c080d5f7e6bc46e1a0" + isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: version "2.2.1" @@ -2625,17 +2687,13 @@ Lockfile: json-schema "0.2.3" verror "1.3.6" - jsx-ast-utils@^1.3.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" - - kind-of@^2.0.1: + jsx-ast-utils@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f" dependencies: - is-buffer "^1.0.2" + array-includes "^3.0.3" - kind-of@^3.0.2, kind-of@^3.2.2: + kind-of@^3.0.2: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: @@ -2647,9 +2705,13 @@ Lockfile: dependencies: is-buffer "^1.1.5" - lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" + kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + + kind-of@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" lazy-cache@^1.0.3: version "1.0.4" @@ -2698,15 +2760,6 @@ Lockfile: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" - loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" @@ -2794,6 +2847,12 @@ Lockfile: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" + make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + dependencies: + pify "^3.0.0" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -2881,7 +2940,7 @@ Lockfile: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: + "minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -2924,14 +2983,18 @@ Lockfile: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - nan@^2.3.0, nan@^2.3.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" + nan@^2.10.0, nan@^2.3.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + neo-async@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" + node-fetch@1.6.3: version "1.6.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" @@ -3006,9 +3069,9 @@ Lockfile: tar "^2.2.1" tar-pack "^3.4.0" - node-sass@^4.5.0: - version "4.5.3" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568" + node-sass@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.0.tgz#d1b8aa855d98ed684d6848db929a20771cc2ae52" dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -3022,12 +3085,13 @@ Lockfile: lodash.mergewith "^4.6.0" meow "^3.7.0" mkdirp "^0.5.1" - nan "^2.3.2" + nan "^2.10.0" node-gyp "^3.3.1" npmlog "^4.0.0" - request "^2.79.0" - sass-graph "^2.1.1" + request "~2.79.0" + sass-graph "^2.2.4" stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" "nopt@2 || 3": version "3.0.6" @@ -3115,18 +3179,10 @@ Lockfile: version "1.1.8" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.1.8.tgz#28a659cf987d96a4dabe7860289f3b5326c4a03c" - object-keys@^1.0.10, object-keys@^1.0.8: + object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" - object.assign@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc" - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.0" - object-keys "^1.0.10" - object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -3285,6 +3341,12 @@ Lockfile: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -3337,6 +3399,12 @@ Lockfile: dependencies: find-up "^1.0.0" + pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" @@ -3621,12 +3689,13 @@ Lockfile: dependencies: asap "~2.0.3" - prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8: - version "15.5.10" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" + prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1: + version "15.6.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: - fbjs "^0.8.9" + fbjs "^0.8.16" loose-envify "^1.3.1" + object-assign "^4.1.1" prr@~0.0.0: version "0.0.0" @@ -3646,11 +3715,11 @@ Lockfile: parse-asn1 "^5.0.0" randombytes "^2.0.1" - punycode@1.3.2: + punycode@1.3.2, punycode@^1.2.4: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - punycode@^1.2.4, punycode@^1.4.1: + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -3664,6 +3733,10 @@ Lockfile: version "1.5.0" resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" + qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" @@ -3765,14 +3838,14 @@ Lockfile: prop-types "^15.5.6" react-side-effect "^1.0.2" - react-dom@^15.3.1: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470" + react-dom@^16.4.0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.0.tgz#099f067dd5827ce36a29eaf9a6cdc7cbf6216b1e" dependencies: - fbjs "^0.8.9" + fbjs "^0.8.16" loose-envify "^1.1.0" - object-assign "^4.1.0" - prop-types "^15.5.10" + object-assign "^4.1.1" + prop-types "^15.6.0" react-dropzone@^3.12.3: version "3.13.2" @@ -3810,14 +3883,16 @@ Lockfile: version "4.0.8" resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-4.0.8.tgz#227403596b5151e182377dab835b5d45f0f8054e" - react-router@^2.7.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-2.8.1.tgz#73e9491f6ceb316d0f779829081863e378ee4ed7" + react-router@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.2.0.tgz#61f7b3e3770daeb24062dae3eedef1b054155986" dependencies: - history "^2.1.2" - hoist-non-react-statics "^1.2.0" - invariant "^2.2.1" - loose-envify "^1.2.0" + history "^4.7.2" + hoist-non-react-statics "^2.3.0" + invariant "^2.2.2" + loose-envify "^1.3.1" + path-to-regexp "^1.7.0" + prop-types "^15.5.4" warning "^3.0.0" react-side-effect@^1.0.2: @@ -3837,15 +3912,14 @@ Lockfile: prop-types "^15.5.6" warning "^3.0.0" - react@^15.4.2: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" + react@^16.4.0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585" dependencies: - create-react-class "^15.6.0" - fbjs "^0.8.9" + fbjs "^0.8.16" loose-envify "^1.1.0" - object-assign "^4.1.0" - prop-types "^15.5.10" + object-assign "^4.1.1" + prop-types "^15.6.0" read-pkg-up@^1.0.1: version "1.0.1" @@ -4023,7 +4097,32 @@ Lockfile: dependencies: is-finite "^1.0.0" - request@2, request@^2.79.0, request@^2.81.0: + request@2, request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + + request@^2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -4073,6 +4172,10 @@ Lockfile: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + resolve-pathname@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" + resolve@^1.1.6: version "1.3.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" @@ -4132,15 +4235,15 @@ Lockfile: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" - safe-buffer@^5.0.1, safe-buffer@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" - - safe-buffer@~5.0.1: + safe-buffer@^5.0.1, safe-buffer@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" - sass-graph@^2.1.1: + safe-buffer@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" + + sass-graph@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" dependencies: @@ -4149,14 +4252,14 @@ Lockfile: scss-tokenizer "^0.2.3" yargs "^7.0.0" - sass-loader@^6.0.2: - version "6.0.6" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" + sass-loader@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.0.3.tgz#6ca10871a1cc7549f8143db5a9958242c4e4ca2a" dependencies: - async "^2.1.5" - clone-deep "^0.3.0" + clone-deep "^2.0.1" loader-utils "^1.0.1" lodash.tail "^4.1.1" + neo-async "^2.5.0" pify "^3.0.0" sax@~1.2.1: @@ -4198,13 +4301,12 @@ Lockfile: dependencies: inherits "^2.0.1" - shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" + shallow-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571" dependencies: is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" + kind-of "^5.0.0" mixin-object "^2.0.1" shallowequal@^1.0.1: @@ -4268,8 +4370,8 @@ Lockfile: source-map "^0.5.6" source-map@^0.4.2: - version "0.4.5" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.5.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" dependencies: amdefine ">=0.0.4" @@ -4515,6 +4617,12 @@ Lockfile: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + "true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + tryit@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" @@ -4533,6 +4641,10 @@ Lockfile: dependencies: safe-buffer "^5.0.1" + tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -4631,6 +4743,10 @@ Lockfile: spdx-correct "~1.0.0" spdx-expression-parse "~1.0.0" + value-equal@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" + vendors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" @@ -4832,9 +4948,14 @@ Lockfile: window-size "0.1.0" Trace: - Error: Error connecting to repository. Please, check the url. - at /usr/local/Cellar/yarn/0.19.1/libexec/lib/node_modules/yarn/lib/resolvers/exotics/hosted-git-resolver.js:162:15 - at next (native) - at step (/usr/local/Cellar/yarn/0.19.1/libexec/lib/node_modules/yarn/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30) - at /usr/local/Cellar/yarn/0.19.1/libexec/lib/node_modules/yarn/node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13 - at process._tickCallback (internal/process/next_tick.js:109:7) + Error: https://registry.yarnpkg.com/react-ave: Not found + at Request.params.callback [as _callback] (/usr/local/Cellar/yarn/0.19.1/libexec/lib/node_modules/yarn/lib/util/request-manager.js:313:18) + at Request.self.callback (/usr/local/Cellar/yarn/0.19.1/libexec/lib/node_modules/yarn/node_modules/request/request.js:186:22) + at emitTwo (events.js:106:13) + at Request.emit (events.js:191:7) + at Request. (/usr/local/Cellar/yarn/0.19.1/libexec/lib/node_modules/yarn/node_modules/request/request.js:1081:10) + at emitOne (events.js:96:13) + at Request.emit (events.js:188:7) + at IncomingMessage. (/usr/local/Cellar/yarn/0.19.1/libexec/lib/node_modules/yarn/node_modules/request/request.js:1001:12) + at IncomingMessage.g (events.js:292:16) + at emitNone (events.js:91:20) diff --git a/dispatch/static/manager/yarn.lock b/dispatch/static/manager/yarn.lock index a221badea..30542c01f 100644 --- a/dispatch/static/manager/yarn.lock +++ b/dispatch/static/manager/yarn.lock @@ -1,8 +1,6 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 - - -"@blueprintjs/core@1.35.7": +"@blueprintjs/core@^1.3.0", "@blueprintjs/core@1.35.7": version "1.35.7" resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-1.35.7.tgz#e0e8042dd5b4d0a02cdbc65bbd1cb0b985f6bf32" dependencies: @@ -15,19 +13,6 @@ tether "^1.4" tslib "^1.5.0" -"@blueprintjs/core@^1.3.0": - version "1.20.0" - resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-1.20.0.tgz#3a95cb4b854b0687c6ca60ef26a39b4e016b42b0" - dependencies: - "@types/dom4" "^1.5.20" - "@types/tether" "^1.1.27" - classnames "^2.2" - dom4 "^1.8" - normalize.css "4.1.1" - pure-render-decorator "^1.1" - tether "^1.4" - tslib "^1.5.0" - "@blueprintjs/datetime@^1.16.0": version "1.17.0" resolved "https://registry.yarnpkg.com/@blueprintjs/datetime/-/datetime-1.17.0.tgz#edb4f55ca0fd85caae4e63f813ac48739913e2a3" @@ -164,6 +149,13 @@ array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -178,20 +170,13 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -array.prototype.find@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" -asap@^2.0.3, asap@~2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" +asap@^2.0.6, asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" asn1.js@^4.0.0: version "4.9.1" @@ -205,14 +190,14 @@ asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" +assert-plus@^1.0.0, assert-plus@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + assert@^1.1.1: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" @@ -227,7 +212,7 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" -async@^2.1.2, async@^2.1.5, async@^2.4.1: +async@^2.1.2, async@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" dependencies: @@ -388,14 +373,13 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-loader@^6.2.10: - version "6.4.1" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42d50b8ca" +babel-loader@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.4.tgz#e3463938bd4e6d55d1c174c5485d406a188ed015" dependencies: - find-cache-dir "^0.1.1" - loader-utils "^0.2.16" + find-cache-dir "^1.0.0" + loader-utils "^1.0.2" mkdirp "^0.5.1" - object-assign "^4.0.1" babel-messages@^6.23.0: version "6.23.0" @@ -633,14 +617,6 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" -babel-polyfill@6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" - dependencies: - babel-runtime "^6.22.0" - core-js "^2.4.0" - regenerator-runtime "^0.10.0" - babel-preset-es2015@^6.13.2: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" @@ -796,11 +772,9 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/brace/-/brace-0.10.0.tgz#edef4eb9b0928ba1ee5f717ffc157749a6dd5d76" - dependencies: - w3c-blob "0.0.1" +brace@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" braces@^1.8.2: version "1.8.5" @@ -938,6 +912,10 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000690" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000690.tgz#ee4e0750070f6aae6f40e76477984449bd6cb48a" +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -953,7 +931,7 @@ chain-function@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" -chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1008,12 +986,6 @@ cli-cursor@^1.0.1: dependencies: restore-cursor "^1.0.1" -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - dependencies: - restore-cursor "^2.0.0" - cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" @@ -1034,14 +1006,14 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -clone-deep@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.3.0.tgz#348c61ae9cdbe0edfe053d91ff4cc521d790ede8" +clone-deep@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" dependencies: for-own "^1.0.0" - is-plain-object "^2.0.1" - kind-of "^3.2.2" - shallow-clone "^0.1.2" + is-plain-object "^2.0.4" + kind-of "^6.0.0" + shallow-clone "^1.0.0" clone@^1.0.2: version "1.0.2" @@ -1103,6 +1075,10 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +commander@^2.9.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1333,10 +1309,6 @@ deep-diff@^0.3.5: version "0.3.8" resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84" -deep-equal@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" @@ -1389,6 +1361,10 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" +diff-match-patch@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.1.tgz#d5f880213d82fbc124d2b95111fb3c033dbad7fa" + diffie-hellman@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" @@ -1398,24 +1374,17 @@ diffie-hellman@^5.0.0: randombytes "^2.0.0" disposables@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/disposables/-/disposables-1.0.1.tgz#064727a25b54f502bd82b89aa2dfb8df9f1b39e3" + version "1.0.2" + resolved "https://registry.yarnpkg.com/disposables/-/disposables-1.0.2.tgz#36c6a674475f55a2d6913567a601444e487b4b6e" -dnd-core@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-2.4.0.tgz#c4a5bc2aea75164f8a295d769d5f551810e7d411" +dnd-core@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-2.6.0.tgz#12bad66d58742c6e5f7cf2943fb6859440f809c4" dependencies: - asap "^2.0.3" + asap "^2.0.6" invariant "^2.0.0" lodash "^4.2.0" - redux "^3.2.0" - -doctrine@^1.2.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" + redux "^3.7.1" doctrine@^2.0.0: version "2.0.0" @@ -1424,6 +1393,12 @@ doctrine@^2.0.0: esutils "^2.0.2" isarray "^1.0.0" +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + dependencies: + esutils "^2.0.2" + dom-helpers@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a" @@ -1521,7 +1496,7 @@ es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: es6-iterator "2" es6-symbol "~3.1" -es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: +es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@2: version "2.0.1" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" dependencies: @@ -1554,7 +1529,7 @@ es6-set@~0.1.5: es6-symbol "3.1.1" event-emitter "~0.3.5" -es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: +es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1, es6-symbol@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" dependencies: @@ -1593,15 +1568,14 @@ eslint-loader@^1.6.3: object-hash "^1.1.4" rimraf "^2.6.1" -eslint-plugin-react@^6.10.0: - version "6.10.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78" +eslint-plugin-react@^7.9.1: + version "7.9.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.9.1.tgz#101aadd15e7c7b431ed025303ac7b421a8e3dc15" dependencies: - array.prototype.find "^2.0.1" - doctrine "^1.2.2" - has "^1.0.1" - jsx-ast-utils "^1.3.4" - object.assign "^4.0.4" + doctrine "^2.1.0" + has "^1.0.2" + jsx-ast-utils "^2.0.1" + prop-types "^15.6.1" eslint@^3.17.1: version "3.19.0" @@ -1736,14 +1710,6 @@ extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -external-editor@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972" - dependencies: - iconv-lite "^0.4.17" - jschardet "^1.4.2" - tmp "^0.0.31" - extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" @@ -1787,6 +1753,18 @@ fbjs@^0.8.0, fbjs@^0.8.7, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.9" +fbjs@^0.8.16: + version "0.8.16" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -1794,12 +1772,6 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - dependencies: - escape-string-regexp "^1.0.5" - file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -1835,6 +1807,14 @@ find-cache-dir@^0.1.1: mkdirp "^0.5.1" pkg-dir "^1.0.0" +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -1842,7 +1822,7 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" -find-up@^2.0.0: +find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" dependencies: @@ -1925,9 +1905,9 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.0.2, function-bind@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" +function-bind@^1.1.0, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" gauge@~2.7.3: version "2.7.4" @@ -1993,6 +1973,16 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" @@ -2035,6 +2025,15 @@ har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + har-validator@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" @@ -2060,11 +2059,11 @@ has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" -has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" +has@^1.0.1, has@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" dependencies: - function-bind "^1.0.2" + function-bind "^1.1.1" hash-base@^2.0.0: version "2.0.2" @@ -2087,14 +2086,14 @@ hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" -history@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/history/-/history-2.1.2.tgz#4aa2de897a0e4867e4539843be6ecdb2986bfdec" +history@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/history/-/history-3.3.0.tgz#fcedcce8f12975371545d735461033579a6dae9c" dependencies: - deep-equal "^1.0.0" - invariant "^2.0.0" - query-string "^3.0.0" - warning "^2.0.0" + invariant "^2.2.1" + loose-envify "^1.2.0" + query-string "^4.2.2" + warning "^3.0.0" hmac-drbg@^1.0.0: version "1.0.1" @@ -2108,10 +2107,14 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: +hoist-non-react-statics@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" +hoist-non-react-statics@^2.1.0, hoist-non-react-statics@^2.3.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -2139,7 +2142,7 @@ https-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@^0.4.17, iconv-lite@~0.4.13: +iconv-lite@~0.4.13: version "0.4.18" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" @@ -2192,7 +2195,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@2: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2204,24 +2207,6 @@ ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" -inquirer@3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347" - dependencies: - ansi-escapes "^1.1.0" - chalk "^1.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.1" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx "^4.1.0" - string-width "^2.0.0" - strip-ansi "^3.0.0" - through "^2.3.6" - inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" @@ -2268,7 +2253,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5: +is-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" @@ -2326,7 +2311,7 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" -is-my-json-valid@^2.10.0: +is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: version "2.16.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" dependencies: @@ -2367,11 +2352,11 @@ is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" -is-plain-object@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.3.tgz#c15bf3e4b66b62d72efaf2925848663ecbc619b6" +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: - isobject "^3.0.0" + isobject "^3.0.1" is-posix-bracket@^0.1.0: version "0.1.1" @@ -2381,10 +2366,6 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" @@ -2423,7 +2404,7 @@ is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@^1.0.0, isarray@~1.0.0, isarray@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2437,9 +2418,9 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isobject@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.0.tgz#39565217f3661789e8a0a0c080d5f7e6bc46e1a0" +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: version "2.2.1" @@ -2486,10 +2467,6 @@ jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" -jschardet@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.4.2.tgz#2aa107f142af4121d145659d44f50830961e699a" - jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" @@ -2541,17 +2518,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" -jsx-ast-utils@^1.3.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" - -kind-of@^2.0.1: +jsx-ast-utils@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f" dependencies: - is-buffer "^1.0.2" + array-includes "^3.0.3" -kind-of@^3.0.2, kind-of@^3.2.2: +kind-of@^3.0.2: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: @@ -2563,9 +2536,13 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" lazy-cache@^1.0.3: version "1.0.4" @@ -2614,15 +2591,6 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" @@ -2710,6 +2678,12 @@ macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + dependencies: + pify "^3.0.0" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -2797,20 +2771,20 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2, "minimatch@2 || 3": version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: brace-expansion "^1.1.7" +minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - mixin-object@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" @@ -2818,7 +2792,7 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -2836,24 +2810,17 @@ mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - -nan@^2.3.0, nan@^2.3.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" +nan@^2.10.0, nan@^2.3.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" -node-fetch@1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" +neo-async@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" node-fetch@^1.0.1: version "1.7.1" @@ -2922,9 +2889,9 @@ node-pre-gyp@^0.6.36: tar "^2.2.1" tar-pack "^3.4.0" -node-sass@^4.5.0: - version "4.5.3" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568" +node-sass@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.0.tgz#d1b8aa855d98ed684d6848db929a20771cc2ae52" dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -2938,18 +2905,13 @@ node-sass@^4.5.0: lodash.mergewith "^4.6.0" meow "^3.7.0" mkdirp "^0.5.1" - nan "^2.3.2" + nan "^2.10.0" node-gyp "^3.3.1" npmlog "^4.0.0" - request "^2.79.0" - sass-graph "^2.1.1" + request "~2.79.0" + sass-graph "^2.2.4" stdout-stream "^1.4.0" - -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - dependencies: - abbrev "1" + "true-case-path" "^1.0.2" nopt@^4.0.1: version "4.0.1" @@ -2958,6 +2920,12 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" @@ -3002,7 +2970,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: +npmlog@^4.0.0, npmlog@^4.0.2, "npmlog@0 || 1 || 2 || 3 || 4": version "4.1.0" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" dependencies: @@ -3031,18 +2999,10 @@ object-hash@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.1.8.tgz#28a659cf987d96a4dabe7860289f3b5326c4a03c" -object-keys@^1.0.10, object-keys@^1.0.8: +object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" -object.assign@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc" - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.0" - object-keys "^1.0.10" - object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -3060,30 +3020,6 @@ onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - dependencies: - mimic-fn "^1.0.0" - -opencollective@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1" - dependencies: - babel-polyfill "6.23.0" - chalk "1.1.3" - inquirer "3.0.6" - minimist "1.2.0" - node-fetch "1.6.3" - opn "4.0.2" - -opn@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" @@ -3117,11 +3053,11 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -osenv@0, osenv@^0.1.4: +osenv@^0.1.4, osenv@0: version "0.1.4" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" dependencies: @@ -3253,6 +3189,12 @@ pkg-dir@^1.0.0: dependencies: find-up "^1.0.0" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" @@ -3537,12 +3479,13 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8: - version "15.5.10" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" +prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.1: + version "15.6.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: - fbjs "^0.8.9" + fbjs "^0.8.16" loose-envify "^1.3.1" + object-assign "^4.1.1" prr@~0.0.0: version "0.0.0" @@ -3562,7 +3505,7 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -punycode@1.3.2, punycode@^1.2.4: +punycode@^1.2.4, punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -3580,17 +3523,15 @@ q@^1.1.2: version "1.5.0" resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" -query-string@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-3.0.3.tgz#ae2e14b4d05071d4e9b9eb4873c35b0dcd42e638" - dependencies: - strict-uri-encode "^1.0.0" - -query-string@^4.1.0: +query-string@^4.1.0, query-string@^4.2.2: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" dependencies: @@ -3635,14 +3576,14 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-ace@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-5.0.1.tgz#a6a88a5b281019d3d1de9241464d5dfd02cef68a" +react-ace@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-6.1.1.tgz#c8339b4f0a27401bff53f477f125d3d7eac2a090" dependencies: - brace "^0.10.0" + brace "^0.11.0" + diff-match-patch "^1.0.0" lodash.get "^4.4.2" lodash.isequal "^4.1.1" - opencollective "^1.0.3" prop-types "^15.5.8" react-addons-css-transition-group@^15.4.1: @@ -3657,22 +3598,28 @@ react-day-picker@^5.3.0: dependencies: prop-types "^15.5.10" -react-dnd-html5-backend@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-2.4.1.tgz#439d2bcaf8bd8b87a51386beb51c128826182ddd" +react-day-picker@^7.1.9: + version "7.1.9" + resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-7.1.9.tgz#04b0d956faf1bf85bb6ec6f9ca7966433be13f36" + dependencies: + prop-types "^15.6.1" + +react-dnd-html5-backend@^2.5.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz#590cd1cca78441bb274edd571fef4c0b16ddcf8e" dependencies: lodash "^4.2.0" -react-dnd@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-2.4.0.tgz#96f0042cd4cd375b4f0c3413f6ec84d267b7d792" +react-dnd@^2.5.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-2.6.0.tgz#7fa25676cf827d58a891293e3c1ab59da002545a" dependencies: disposables "^1.0.1" - dnd-core "^2.4.0" - hoist-non-react-statics "^1.2.0" + dnd-core "^2.6.0" + hoist-non-react-statics "^2.1.0" invariant "^2.1.0" lodash "^4.2.0" - prop-types "^15.5.8" + prop-types "^15.5.10" react-document-title@^2.0.2: version "2.0.3" @@ -3681,35 +3628,40 @@ react-document-title@^2.0.2: prop-types "^15.5.6" react-side-effect "^1.0.2" -react-dom@^15.3.1: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470" +react-dom@^15.3.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730" dependencies: fbjs "^0.8.9" loose-envify "^1.1.0" object-assign "^4.1.0" prop-types "^15.5.10" -react-dropzone@^3.12.3: - version "3.13.2" - resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-3.13.2.tgz#318745c03007ca6b948cdd4431abc009b16a6588" +react-dropzone@^4.2.11: + version "4.2.11" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-4.2.11.tgz#c20ff6a62634c5803bed5f292ef1bd4b3e1f90c3" dependencies: attr-accept "^1.0.3" prop-types "^15.5.7" -react-measure@^1.4.7: - version "1.4.7" - resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-1.4.7.tgz#a1d2ca0dcfef04978b7ac263a765dcb6a0936fdb" +react-lifecycles-compat@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + +react-measure@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-2.0.2.tgz#072a9a5fafc01dfbadc1fa5fb09fc351037f636c" dependencies: get-node-dimensions "^1.2.0" - prop-types "^15.5.4" - resize-observer-polyfill "^1.4.1" + prop-types "^15.5.10" + resize-observer-polyfill "^1.4.2" -react-redux-loading-bar@^2.7.2: - version "2.9.2" - resolved "https://registry.yarnpkg.com/react-redux-loading-bar/-/react-redux-loading-bar-2.9.2.tgz#f0e604ee35af5ecb25addb10bf24ca3d478c95a8" +react-redux-loading-bar@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/react-redux-loading-bar/-/react-redux-loading-bar-4.0.5.tgz#a536ca27e39070f017f623e4a6d4aefc860ce650" dependencies: prop-types "^15.5.6" + react-lifecycles-compat "^3.0.2" react-redux@^4.4.5: version "4.4.8" @@ -3722,18 +3674,20 @@ react-redux@^4.4.5: loose-envify "^1.1.0" prop-types "^15.5.4" -react-router-redux@^4.0.5: +react-router-redux@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-4.0.8.tgz#227403596b5151e182377dab835b5d45f0f8054e" -react-router@^2.7.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-2.8.1.tgz#73e9491f6ceb316d0f779829081863e378ee4ed7" +react-router@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.2.1.tgz#b9a3279962bdfbe684c8bd0482b81ef288f0f244" dependencies: - history "^2.1.2" - hoist-non-react-statics "^1.2.0" + create-react-class "^15.5.1" + history "^3.0.0" + hoist-non-react-statics "^2.3.1" invariant "^2.2.1" loose-envify "^1.2.0" + prop-types "^15.5.6" warning "^3.0.0" react-side-effect@^1.0.2: @@ -3753,9 +3707,9 @@ react-transition-group@^1.2.0: prop-types "^15.5.6" warning "^3.0.0" -react@^15.4.2: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" +react@^15.3.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72" dependencies: create-react-class "^15.6.0" fbjs "^0.8.9" @@ -3863,7 +3817,7 @@ redux-thunk@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5" -redux@^3.2.0, redux@^3.5.2: +redux@^3.5.2: version "3.7.0" resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.0.tgz#07a623cafd92eee8abe309d13d16538f6707926f" dependencies: @@ -3872,6 +3826,15 @@ redux@^3.2.0, redux@^3.5.2: loose-envify "^1.1.0" symbol-observable "^1.0.3" +redux@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + dependencies: + lodash "^4.2.1" + lodash-es "^4.2.1" + loose-envify "^1.1.0" + symbol-observable "^1.0.3" + regenerate@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" @@ -3939,7 +3902,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2, request@^2.79.0, request@^2.81.0: +request@^2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -3966,6 +3929,31 @@ request@2, request@^2.79.0, request@^2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +request@~2.79.0, request@2: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -3981,9 +3969,9 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" -resize-observer-polyfill@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.4.2.tgz#a37198e6209e888acb1532a9968e06d38b6788e5" +resize-observer-polyfill@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.0.tgz#660ff1d9712a2382baa2cad450a4716209f9ca69" resolve-from@^1.0.0: version "1.0.1" @@ -4002,20 +3990,13 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: +rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1, rimraf@2: version "2.6.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: @@ -4034,29 +4015,19 @@ run-async@^0.1.0: dependencies: once "^1.3.0" -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - dependencies: - is-promise "^2.1.0" - rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" -rx@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" +safe-buffer@^5.0.1, safe-buffer@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" -safe-buffer@^5.0.1, safe-buffer@^5.1.0: +safe-buffer@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" -safe-buffer@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" - -sass-graph@^2.1.1: +sass-graph@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" dependencies: @@ -4065,14 +4036,14 @@ sass-graph@^2.1.1: scss-tokenizer "^0.2.3" yargs "^7.0.0" -sass-loader@^6.0.2: - version "6.0.6" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" +sass-loader@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.0.3.tgz#6ca10871a1cc7549f8143db5a9958242c4e4ca2a" dependencies: - async "^2.1.5" - clone-deep "^0.3.0" + clone-deep "^2.0.1" loader-utils "^1.0.1" lodash.tail "^4.1.1" + neo-async "^2.5.0" pify "^3.0.0" sax@~1.2.1: @@ -4092,7 +4063,7 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0: +semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -4114,18 +4085,17 @@ sha.js@^2.4.0, sha.js@^2.4.8: dependencies: inherits "^2.0.1" -shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" +shallow-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571" dependencies: is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" + kind-of "^5.0.0" mixin-object "^2.0.1" shallowequal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.1.tgz#4349160418200bad3b82d723ded65f2354db2a23" + version "1.0.2" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f" shebang-command@^1.2.0: version "1.2.0" @@ -4145,7 +4115,7 @@ shelljs@^0.7.5: interpret "^1.0.0" rechoir "^0.6.2" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -4252,6 +4222,16 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" +string_decoder@^0.10.25: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a533737b7491e179" + dependencies: + safe-buffer "~5.0.1" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -4267,16 +4247,6 @@ string-width@^2.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^3.0.0" -string_decoder@^0.10.25: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -string_decoder@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a533737b7491e179" - dependencies: - safe-buffer "~5.0.1" - stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -4403,12 +4373,6 @@ timers-browserify@^2.0.2: dependencies: setimmediate "^1.0.4" -tmp@^0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - dependencies: - os-tmpdir "~1.0.1" - to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -4431,6 +4395,12 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +"true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + tryit@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" @@ -4449,6 +4419,10 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -4530,7 +4504,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@0.10.3, util@^0.10.3: +util@^0.10.3, util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -4563,16 +4537,6 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -w3c-blob@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/w3c-blob/-/w3c-blob-0.0.1.tgz#b0cd352a1a50f515563420ffd5861f950f1d85b8" - -warning@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901" - dependencies: - loose-envify "^1.0.0" - warning@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" @@ -4637,7 +4601,7 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@1, which@^1.2.9: +which@^1.2.9, which@1: version "1.2.14" resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" dependencies: @@ -4653,14 +4617,14 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -4746,3 +4710,4 @@ yargs@~3.10.0: cliui "^2.1.0" decamelize "^1.0.0" window-size "0.1.0" + diff --git a/dispatch/tests/helpers.py b/dispatch/tests/helpers.py index ff717b71e..a48261df9 100644 --- a/dispatch/tests/helpers.py +++ b/dispatch/tests/helpers.py @@ -208,3 +208,19 @@ def create_video(cls, client, title='testVideo', url='testVideoURL'): url = reverse('api-videos-list') return client.post(url, data, format='json') + + @classmethod + def create_poll(cls, client, name='test name', question='test question', answers=[{'id':1,'name':'answer1'},{'id':2,'name':'answer2'}], is_open=True, show_results=True): + """Create a dummy poll instance""" + + data = { + 'name': name, + 'question': question, + 'answers_json': answers, + 'is_open': is_open, + 'show_results': show_results + } + + url = reverse('api-polls-list') + + return client.post(url, data, format='json') diff --git a/dispatch/tests/test_api_polls.py b/dispatch/tests/test_api_polls.py new file mode 100644 index 000000000..60a304fc9 --- /dev/null +++ b/dispatch/tests/test_api_polls.py @@ -0,0 +1,306 @@ +from collections import OrderedDict +from rest_framework import status + +from django.core.urlresolvers import reverse + +from dispatch.tests.cases import DispatchAPITestCase +from dispatch.tests.helpers import DispatchTestHelpers +from dispatch.models import Poll, PollAnswer, PollVote + + +class PollsTests(DispatchAPITestCase): + """Class to test the poll API methods""" + + def test_poll_creation(self): + """Test simple poll creation, checks the response and database""" + + response = DispatchTestHelpers.create_poll(self.client) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + answers = [ OrderedDict([('id',1), ('name','answer1'), ('vote_count',0)]), OrderedDict([('id',2), ('name','answer2'), ('vote_count',0)]) ] + # Check data + self.assertEqual(response.data['name'], 'test name') + self.assertEqual(response.data['question'], 'test question') + self.assertEqual(response.data['is_open'], True) + self.assertEqual(response.data['show_results'], True) + self.assertEqual(response.data['answers'], answers) + + poll = Poll.objects.get(pk=response.data['id']) + self.assertEqual(poll.name, 'test name') + self.assertEqual(poll.question, 'test question') + self.assertTrue(poll.is_open) + self.assertTrue(poll.show_results) + + def test_poll_update(self): + """Test updating a poll works, check the response and database""" + + # Create original poll + response = DispatchTestHelpers.create_poll(self.client) + + # Update this poll + url = reverse('api-polls-detail', args=[response.data['id']]) + updated_answers = [{'id':1,'name':'updated answer'}] + data = { + 'name': 'updated name', + 'answers_json': updated_answers + } + answers = [ OrderedDict([('id',1), ('name','updated answer'), ('vote_count',0)]) ] + response = self.client.patch(url, data, format='json') + + # Check data + self.assertEqual(response.data['name'], 'updated name') + self.assertEqual(response.data['question'], 'test question') + self.assertEqual(response.data['is_open'], True) + self.assertEqual(response.data['show_results'], True) + self.assertEqual(response.data['answers'], answers) + + poll = Poll.objects.get(pk=response.data['id']) + self.assertEqual(poll.name, 'updated name') + self.assertEqual(poll.question, 'test question') + self.assertTrue(poll.is_open) + self.assertTrue(poll.show_results) + + def test_unauthorized_poll_creation(self): + """Create poll should fail with unauthenticated request""" + + # Clear authentication credentials + self.client.credentials() + + response = DispatchTestHelpers.create_poll(self.client) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_unauthorized_poll_update(self): + """Updating a poll should fail with unauthenticated request""" + + # Create original poll + response = DispatchTestHelpers.create_poll(self.client) + + # Clear authentication credentials + self.client.credentials() + + # Attempt to update this poll + url = reverse('api-polls-detail', args=[response.data['id']]) + data = { + 'name': 'updated name' + } + + response = self.client.patch(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_delete_poll(self): + """Simple poll deletion test and throw error if trying to delete item + does not exist""" + + # Create original poll + response = DispatchTestHelpers.create_poll(self.client) + poll_id = response.data['id'] + url = reverse('api-polls-detail', args=[poll_id]) + + # Successful deletion should return 204 + response = self.client.delete(url, format='json') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertFalse(Poll.objects.filter(id=poll_id).exists()) + self.assertFalse(PollAnswer.objects.filter(poll_id=poll_id).exists()) + + # Can't delete a poll that has already been deleted + response = self.client.delete(url, format='json') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_unauthorized_poll_deletion(self): + """Unauthorized deletion of a poll isn't allowed""" + + response = DispatchTestHelpers.create_poll(self.client) + + # Generate detail URL + url = reverse('api-polls-detail', args=[response.data['id']]) + + # Clear authentication credentials + self.client.credentials() + + # Attempt to delete the poll + response = self.client.delete(url, format='json') + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_hide_poll_results(self): + """Unauthorized request should not be able to see results on polls + where show results is false""" + + # Create a poll to vote in + response = DispatchTestHelpers.create_poll(self.client, show_results=False) + + poll = Poll.objects.get(pk=response.data['id']) + answer1 = PollAnswer.objects.filter(poll_id=poll.id).first() + answer2 = PollAnswer.objects.filter(poll_id=poll.id).last() + # Clear credentials + self.client.credentials() + + url = reverse('api-polls-vote', args=[poll.id]) + + data = { + 'answer_id': answer1.id + } + + # Vote in the poll + response = self.client.post(url, data, format='json') + # Confirm the vote was successful + self.assertEqual(response.status_code, status.HTTP_200_OK) + + data = { + 'answer_id': answer2.id + } + + # Vote in the poll + response = self.client.post(url, data, format='json') + # Confirm the vote was successful + self.assertEqual(response.status_code, status.HTTP_200_OK) + + url = reverse('api-polls-detail', args=[poll.id]) + + response = self.client.get(url, format='json') + self.assertEqual(response.data['total_votes'], 0) + self.assertEqual(response.data['answers'][0]['vote_count'], 0) + self.assertEqual(response.data['answers'][1]['vote_count'], 0) + + + def test_poll_search(self): + """Should be able to search poll by name and question""" + + poll_1 = DispatchTestHelpers.create_poll(self.client, name='Poll 1', question='question 1') + poll_2 = DispatchTestHelpers.create_poll(self.client, name='Poll 1 and 2', question='question 1 and 2') + poll_3 = DispatchTestHelpers.create_poll(self.client, name='Poll 3', question='question 3') + + url = '%s?q=%s' % (reverse('api-polls-list'), 'Poll 1') + response = self.client.get(url, format='json') + + self.assertEqual(response.data['results'][0]['name'], 'Poll 1') + self.assertEqual(response.data['results'][1]['name'], 'Poll 1 and 2') + self.assertEqual(response.data['count'], 2) + + def test_poll_vote(self): + """Any user should be able to vote in a poll""" + + # Create a poll to vote in + response = DispatchTestHelpers.create_poll(self.client, show_results=False) + + poll_id = response.data['id'] + answer = PollAnswer.objects.filter(poll_id=poll_id).first() + + # Clear credentials + url = reverse('api-polls-vote', args=[poll_id]) + + data = { + 'answer_id': answer.id + } + + response = self.client.post(url, data, format='json') + + # Check that the vote was successful + self.assertEqual(response.status_code, status.HTTP_200_OK) + + url = reverse('api-polls-detail', args=[poll_id]) + + response = self.client.get(url) + + # Check that the results are correct + self.assertEqual(response.data['total_votes'], 1) + self.assertEqual(response.data['answers'][0]['vote_count'], 1) + + def test_poll_change_vote(self): + """A user should be able to change their vote""" + + # Create a poll to vote in + response = DispatchTestHelpers.create_poll(self.client) + + poll_id = response.data['id'] + answer_1 = PollAnswer.objects.filter(poll_id=poll_id).first() + answer_2 = PollAnswer.objects.filter(poll_id=poll_id).last() + + url = reverse('api-polls-vote', args=[poll_id]) + + data = { + 'answer_id': answer_1.id + } + + response = self.client.post(url, data, format='json') + vote_id = response.data['id'] + + # Check that the vote was successful + self.assertEqual(response.status_code, status.HTTP_200_OK) + + data = { + 'answer_id': answer_2.id, + 'vote_id': vote_id + } + + response = self.client.post(url, data, format='json') + + # Check that the vote change was successful + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['id'], vote_id) + + # Get the poll + url = reverse('api-polls-detail', args=[poll_id]) + + response = self.client.get(url) + + # Check that the results are correct + self.assertEqual(response.data['total_votes'], 1) + self.assertEqual(response.data['answers'][0]['vote_count'], 0) + self.assertEqual(response.data['answers'][1]['vote_count'], 1) + + def test_poll_vote_invalid_answer(self): + """A user should not be able to vote for an answer that is not + valid for the poll""" + + # Create polls to vote in + response = DispatchTestHelpers.create_poll(self.client, is_open=True) + + poll_1_id = response.data['id'] + poll_1 = Poll.objects.get(id=poll_1_id) + answer_poll_1 = PollAnswer.objects.filter(poll_id=poll_1_id).first() + + response = DispatchTestHelpers.create_poll(self.client, is_open=True) + + poll_2_id = response.data['id'] + poll_2 = Poll.objects.get(id=poll_2_id) + answer_poll_2 = PollAnswer.objects.filter(poll_id=poll_2_id).first() + + url = reverse('api-polls-vote', args=[poll_1_id]) + + data = { + 'answer_id': answer_poll_2.id + } + + response = self.client.post(url, data, format='json') + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_poll_vote_closed(self): + """A user should not be able to vote in a closed poll""" + + # Create a poll to vote in + response = DispatchTestHelpers.create_poll(self.client, is_open=False) + + poll_id = response.data['id'] + answer = PollAnswer.objects.filter(poll_id=poll_id).first() + + url = reverse('api-polls-vote', args=[poll_id]) + + data = { + 'answer_id': answer.id + } + response = self.client.post(url, data, format='json') + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + # Get the poll + url = reverse('api-polls-detail', args=[poll_id]) + + response = self.client.get(url) + + # Check that the results are correct + self.assertEqual(response.data['total_votes'], 0) + self.assertEqual(response.data['answers'][0]['vote_count'], 0) + self.assertEqual(response.data['answers'][1]['vote_count'], 0) diff --git a/dispatch/theme/fields.py b/dispatch/theme/fields.py index 1e7b0db6e..0fb548469 100644 --- a/dispatch/theme/fields.py +++ b/dispatch/theme/fields.py @@ -4,8 +4,8 @@ from django.utils.dateparse import parse_datetime from django.core.exceptions import ObjectDoesNotExist -from dispatch.models import Article, Image -from dispatch.api.serializers import ArticleSerializer, ImageSerializer, WidgetSerializer +from dispatch.models import Article, Image, Poll +from dispatch.api.serializers import ArticleSerializer, ImageSerializer, WidgetSerializer, PollSerializer from dispatch.theme.exceptions import InvalidField, WidgetNotFound from dispatch.theme.validators import is_valid_id @@ -327,3 +327,9 @@ def prepare_data(self, data): widget = self.get_widget(data['id']) widget.set_data(data['data']) return widget + +class PollField(ModelField): + type = 'poll' + + model = Poll + serializer = PollSerializer