diff --git a/.travis.yml b/.travis.yml index 0f68b0a617..1d791b7b34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ notifications: # sauce_connect: true language: node_js matrix: + allow_failures: + - node_js: stable include: # Run everything with v5 (match production) # Skip e2e test during mustang build @@ -23,8 +25,7 @@ matrix: env: TEST_SUITE=lint - node_js: 5 env: TEST_SUITE=unit - # Run unit test with 0.12 and current stable too - - node_js: 0.12 + - node_js: 6 env: TEST_SUITE=unit - node_js: stable env: TEST_SUITE=unit diff --git a/app/common/common-module.js b/app/common/common-module.js index a62a1f01cc..e30bcc94df 100644 --- a/app/common/common-module.js +++ b/app/common/common-module.js @@ -74,6 +74,8 @@ angular.module('ushahidi.common', [ .directive('ushModalContainer', require('./directives/modal-container.directive.js')) .directive('modalBody', require('./directives/modal-body.directive.js')) .directive('layoutClass', require('./directives/layout-class.directive.js')) +.directive('embedOnly', require('./directives/embed-only.directive.js')) +.directive('ushLogo', require('./directives/ush-logo.directive.js')) .directive('filterSearchbar', require('./directives/filter-system/filter-searchbar.js')) .directive('filterRole', require('./directives/filter-system/filter-role.js')) diff --git a/app/common/controllers/intercom.js b/app/common/controllers/intercom.js index befbf4f370..2232fe7726 100644 --- a/app/common/controllers/intercom.js +++ b/app/common/controllers/intercom.js @@ -14,9 +14,10 @@ function ( $window ) { var pattern = /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/g; - $rootScope.$on('event:authentication:login:succeeded', function () { - $scope.startIntercom(); + if ($window.self === $window.top) { + $scope.startIntercom(); + } }); $scope.startIntercom = function () { diff --git a/app/common/controllers/navigation.js b/app/common/controllers/navigation.js index c6a42cad50..6bef6d384c 100644 --- a/app/common/controllers/navigation.js +++ b/app/common/controllers/navigation.js @@ -15,6 +15,7 @@ function NavigationController(Authentication, ConfigEndpoint, BootstrapConfig, $ $rootScope.$on('event:update:header', reloadSiteConfig); function activate() { + Features.loadFeatures().then(function () { vm.activityIsAvailable = Features.isViewEnabled('activity'); vm.planIsAvailable = Features.isViewEnabled('plan'); diff --git a/app/common/directives/embed-only.directive.js b/app/common/directives/embed-only.directive.js new file mode 100644 index 0000000000..37d9be4c22 --- /dev/null +++ b/app/common/directives/embed-only.directive.js @@ -0,0 +1,20 @@ +module.exports = EmbedOnlyDirective; + +EmbedOnlyDirective.$inject = []; +function EmbedOnlyDirective() { + return { + restrict: 'A', + controller: EmbedOnlyController + }; +} + +EmbedOnlyController.$inject = ['$scope', '$element', '$attrs', '$rootScope', '_', '$window']; +function EmbedOnlyController($scope, $element, $attrs, $rootScope, _, $window) { + var globalEmbed = ($window.self !== $window.top) ? true : false; + + if (globalEmbed && ($attrs.embedOnly === 'false')) { + $element.addClass('hidden'); + } else if (!globalEmbed && ($attrs.embedOnly === 'true')) { + $element.addClass('hidden'); + } +} diff --git a/app/common/directives/file-upload.directive.js b/app/common/directives/file-upload.directive.js index 987509f683..8a16f3a8e1 100644 --- a/app/common/directives/file-upload.directive.js +++ b/app/common/directives/file-upload.directive.js @@ -4,10 +4,10 @@ function FileUpload() { return { restrict: 'E', templateUrl: 'templates/common/directives/file-upload.html', + replace: true, scope: { - fileContainer: '=' + container: '=' }, - controller: [ '$scope', '$attrs', function ( @@ -15,7 +15,16 @@ function FileUpload() { ) { $scope.required = typeof $attrs.required !== 'undefined'; $scope.uploadFile = function ($event) { - $scope.fileContainer.file = $event.target.files[0]; + $scope.container.file = $event.target.files[0]; + var reader = new FileReader(); + reader.onload = function () { + var dataURL = reader.result; + $scope.container.dataURI = dataURL; + $scope.container.changed = true; + $scope.$apply(); + }; + reader.readAsDataURL($event.target.files[0]); + }; }] }; diff --git a/app/common/directives/file-upload.html b/app/common/directives/file-upload.html index 3e5fa59082..25e9d34e68 100644 --- a/app/common/directives/file-upload.html +++ b/app/common/directives/file-upload.html @@ -1,2 +1 @@ - - + diff --git a/app/common/directives/first-time-config.html b/app/common/directives/first-time-config.html index c7c2f568bb..4cba879a16 100644 --- a/app/common/directives/first-time-config.html +++ b/app/common/directives/first-time-config.html @@ -33,7 +33,7 @@

settings.customize_your_new_deployment

settings.appearance_header_image_instructions

- +
diff --git a/app/common/directives/layout-class.directive.js b/app/common/directives/layout-class.directive.js index 58b4d7babe..197ce624ac 100644 --- a/app/common/directives/layout-class.directive.js +++ b/app/common/directives/layout-class.directive.js @@ -11,7 +11,19 @@ function LayoutClassDirective() { }; } -LayoutClassController.$inject = ['$scope', '$rootScope']; -function LayoutClassController($scope, $rootScope) { - $rootScope.setLayout('layout-' + $scope.layout); +LayoutClassController.$inject = ['$scope', '$rootScope', '$window', 'Util']; +function LayoutClassController($scope, $rootScope, $window, Util) { + var isEmbed = ($window.self !== $window.top) ? true : false; + // In the case of map we omit the layout-a class + var isMap = (Util.currentUrl()) ? Util.currentUrl().indexOf('map') > 0 : false; + + if (!isEmbed) { + $rootScope.setLayout('layout-' + $scope.layout); + } else if (isEmbed && isMap) { + $rootScope.setLayout('layout-embed'); + } else { + // If we are in embed mode + // we must append the layout to the embed layout + $rootScope.setLayout('layout-embed layout-' + $scope.layout); + } } diff --git a/app/common/directives/ush-logo.directive.js b/app/common/directives/ush-logo.directive.js new file mode 100644 index 0000000000..64520fda78 --- /dev/null +++ b/app/common/directives/ush-logo.directive.js @@ -0,0 +1,15 @@ +module.exports = UshLogoDirective; + +UshLogoDirective.$inject = []; +function UshLogoDirective() { + return { + restrict: 'E', + controller: UshLogoController, + replace: true, + templateUrl: 'templates/common/directives/ush-logo.html' + }; +} + +UshLogoController.$inject = []; +function UshLogoController() { +} diff --git a/app/common/directives/ush-logo.html b/app/common/directives/ush-logo.html new file mode 100644 index 0000000000..4a61cbafb1 --- /dev/null +++ b/app/common/directives/ush-logo.html @@ -0,0 +1,4 @@ + + + + diff --git a/app/common/global/event-handlers.js b/app/common/global/event-handlers.js index 5b5b3bb6ba..8ecff59791 100644 --- a/app/common/global/event-handlers.js +++ b/app/common/global/event-handlers.js @@ -10,7 +10,6 @@ function ( $rootScope.setLayout = function (layout) { $rootScope.globalLayout = layout; }; - // Setup PL modal visible and switching function $rootScope.modalVisible = false; $rootScope.toggleModalVisible = function (state) { diff --git a/app/common/locales/en.json b/app/common/locales/en.json index e23ad64986..bf28d525d8 100644 --- a/app/common/locales/en.json +++ b/app/common/locales/en.json @@ -3,9 +3,12 @@ "powered_by_ushahidi" : "Powered by Ushahidi.", "support" : "Ushahidi Support", "by" : "by", + "submit" : "Submit", + "submit_another" : "Submit & Add Another", "can_see_this" : "can see this", "collections" : "Collections", "create_collection" : "Create collection", + "export_to_csv" : "Export to CSV", "create_new" : "Create new", "created_by" : "Created by {{author}}", "documentation": { @@ -59,6 +62,7 @@ "apply_filters" : "Apply filters", "show_more_less" : "Show more/less", "filters" : "Filters", + "filter_by_survey" : "Filter by survey", "configure" : "Configure", "post" : "Post", "name" : "Name", @@ -248,6 +252,8 @@ "q": "Keyword", "created_after": "Start date", "created_before": "End date", + "date_after": "Start date", + "date_before": "End date", "center_point": "Location", "tags": "Category", "form": "Survey", @@ -692,6 +698,9 @@ "continue" : "Continue", "current_header" : "Current background image", "customize_your_new_deployment" : "Customize your new deployment", + "embed" : "Embed", + "embed_info" : "To embed your map on another site use the code snippet below.", + "embed_code" : "<iframe width=\"500\" height=\"400\" src=\"{{url}}\"></iframe>", "settings_list": { "general" : "General", "general_desc" : "Change your deployment's name, description, logo, and other details.", @@ -923,6 +932,7 @@ "success" : "Registration complete! You can now log in.." }, "post" : { + "delete_image_confirm" : "Are you sure you want to remove this image?", "save_success" : "Post {{name}} saved", "save_success_review" : "Post {{name}} saved. Your post will be reviewed by a moderator before publishing.", "save_error" : "Unable to save post, please try again", @@ -974,6 +984,7 @@ "bulk_role_change_success" : "User roles changed to {{role_name}}" }, "form" : { + "add_to_survey" : "Add to survey", "save_success" : "Survey {{name}} saved", "save_stage_success" : "Survey task {{name}} saved", "save_attribute_success" : "Field {{name}} added", diff --git a/app/common/services/endpoints/MediaEndpoint.js b/app/common/services/endpoints/MediaEndpoint.js index e7c09c090a..82e6cbc62a 100644 --- a/app/common/services/endpoints/MediaEndpoint.js +++ b/app/common/services/endpoints/MediaEndpoint.js @@ -19,7 +19,7 @@ function ( } }, update: { - method: 'POST' + method: 'PUT' } }); diff --git a/app/common/services/endpoints/post-endpoint.js b/app/common/services/endpoints/post-endpoint.js index 6c852ffbfe..4f5eb0b1e8 100644 --- a/app/common/services/endpoints/post-endpoint.js +++ b/app/common/services/endpoints/post-endpoint.js @@ -15,7 +15,7 @@ function ( var PostEndpoint = $resource(Util.apiUrl('/posts/:id/:extra'), { id: '@id', order: 'desc', - orderby: 'created' + orderby: 'post_date' }, { query: { method: 'GET', diff --git a/app/common/services/maps.js b/app/common/services/maps.js index 49c47e3c3b..a8f0c1eb8d 100644 --- a/app/common/services/maps.js +++ b/app/common/services/maps.js @@ -11,6 +11,7 @@ module.exports = [ 'MediaEndpoint', '$compile', '$rootScope', + '$window', 'CONST', function ( $q, @@ -25,6 +26,7 @@ function ( MediaEndpoint, $compile, $rootScope, + $window, CONST ) { var layers = { @@ -115,6 +117,9 @@ function ( var Maps = { maps: {}, config: undefined, + getZoomControlPosition: function () { + return $window.self !== $window.top ? 'bottomleft' : 'bottomright'; + }, getMap: function (name) { if (!this.maps[name]) { this.maps[name] = Object.create(Map).init(name); @@ -129,7 +134,7 @@ function ( getInitialScope: function () { return { defaults: { - zoomControlPosition: 'bottomright', + zoomControlPosition: this.getZoomControlPosition(), scrollWheelZoom: false }, center: { // Default to centered on Nairobi diff --git a/app/common/services/util.js b/app/common/services/util.js index f88824759c..3f3d926f91 100644 --- a/app/common/services/util.js +++ b/app/common/services/util.js @@ -1,18 +1,28 @@ module.exports = [ '_', 'CONST', + '$window', function ( _, - CONST + CONST, + $window ) { var Util = { + currentUrl: function () { + return $window.location.href; + }, url: function (relative_url) { return CONST.BACKEND_URL + relative_url; }, apiUrl: function (relative_url) { return CONST.API_URL + relative_url; }, + deploymentUrl: function (relative_url) { + var pattern = /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/g; + var deploymentUrl = pattern.exec(this.apiUrl(relative_url)); + return deploymentUrl[0].replace('api.', ''); + }, transformResponse: function (response, omitKeys) { omitKeys = (omitKeys || []).concat(['allowed_methods']); return _.omit(angular.fromJson(response), omitKeys); diff --git a/app/main/posts/collections/collections-controller.js b/app/main/posts/collections/collections-controller.js index 4b3019121d..791b0beddd 100644 --- a/app/main/posts/collections/collections-controller.js +++ b/app/main/posts/collections/collections-controller.js @@ -39,8 +39,10 @@ module.exports = [ // Extend filters, always adding the current collection id var extendFilters = function (filters) { - filters = angular.copy(filters, { set : [] }); + //filters = angular.copy(filters, { set : []}); + filters.set = []; filters.set.push(collection.id); + return filters; }; @@ -53,7 +55,11 @@ module.exports = [ }, true); // Reset GlobalFilter + add set filter + // Ensure that ALL posts are visible under collections + // Set default collection status filters PostFilters.clearFilters(); + PostFilters.setFilters({ status: ['archived', 'draft', 'published'] }); $scope.filters = extendFilters(PostFilters.getFilters()); + } ]; diff --git a/app/main/posts/collections/mode-context.html b/app/main/posts/collections/mode-context.html index 6225e25d4d..2ff778e0cd 100644 --- a/app/main/posts/collections/mode-context.html +++ b/app/main/posts/collections/mode-context.html @@ -118,6 +118,8 @@

+ + diff --git a/app/main/posts/common/post-actions.directive.js b/app/main/posts/common/post-actions.directive.js index eb9d3b0e24..82af1d598f 100644 --- a/app/main/posts/common/post-actions.directive.js +++ b/app/main/posts/common/post-actions.directive.js @@ -49,4 +49,3 @@ function PostActionsDirective( } } } - diff --git a/app/main/posts/common/post-actions.html b/app/main/posts/common/post-actions.html index 443a7c8522..f7b841a468 100644 --- a/app/main/posts/common/post-actions.html +++ b/app/main/posts/common/post-actions.html @@ -24,15 +24,10 @@ - @@ -123,6 +129,18 @@

{{post.form.name}}

attributes="attributes" visible-stage="visibleStage"> +

Delete this post

@@ -140,7 +158,7 @@

Delete this post

- + diff --git a/app/main/posts/modify/post-entity.service.js b/app/main/posts/modify/post-entity.service.js index 09f834127c..353ddf994f 100644 --- a/app/main/posts/modify/post-entity.service.js +++ b/app/main/posts/modify/post-entity.service.js @@ -11,7 +11,8 @@ function ( locale: CONST.DEFAULT_LOCALE, values: {}, completed_stages: [], - published_to: [] + published_to: [], + post_date: new Date() }, data); }; }]; diff --git a/app/main/posts/modify/post-media.directive.js b/app/main/posts/modify/post-media.directive.js index 24f83efe5e..5e59113b77 100644 --- a/app/main/posts/modify/post-media.directive.js +++ b/app/main/posts/modify/post-media.directive.js @@ -1,12 +1,14 @@ module.exports = [ '$http', 'MediaEndpoint', + 'MediaEditService', 'Util', 'Notify', '$q', function ( $http, MediaEndpoint, + MediaEditService, Util, Notify, $q @@ -17,84 +19,44 @@ function ( require: '^^form', scope: { mediaId: '=', + media: '=', name: '@' }, templateUrl: 'templates/main/posts/modify/media.html', link: function ($scope, element, attr, formCtrl) { - // Initialize file container - $scope.fileContainer = {file: null}; + $scope.showAdd = function () { + return (!$scope.media.id && !$scope.media.changed); + }; - // Initialize media object - $scope.media = {}; + $scope.showReplace = function () { + return $scope.media.dataURI || $scope.media.id; + }; + + $scope.showDelete = function () { + return $scope.media.id; + }; if ($scope.mediaId) { MediaEndpoint.get({id: $scope.mediaId}).$promise.then(function (media) { $scope.media = media; + // Set initial media state + $scope.media.changed = false; }); + } else { + // Initialize media object + $scope.media = {file: null, caption: null, dataURI: null, changed: false}; } - // Track file changes - $scope.canUpload = false; - - $scope.onChange = function () { - $scope.$apply(function () { - if ($scope.fileContainer.file) { - $scope.canUpload = true; - } else { - $scope.canUpload = false; - } - }); - }; - - $scope.uploadFile = function () { - //@todo Allow editing of caption for existing image - if (!$scope.fileContainer.file) { - return; - } - - // Delete current file - var promise = deleteMedia($scope.mediaId); - - // ...then upload new file - promise.then(function () { - var formData = new FormData(); - - formData.append('file', $scope.fileContainer.file); - - if ($scope.media.caption) { - formData.append('caption', $scope.media.caption); - } - - $http.post( - Util.apiUrl('/media'), - formData, - { - headers: { - 'Content-Type': undefined - } - } - ).then(function (response) { - $scope.mediaId = response.data.id; - - // We found a file so change the state of parent form - formCtrl[$scope.name].$setDirty(); - }, function (error) { - Notify.apiErrors(error); + $scope.deleteMedia = function (mediaId) { + // Mark for deletion + Notify.confirmDelete('notify.post.delete_image_confirm').then(function () { + MediaEditService.deleteMedia(mediaId).then(function () { + $scope.media = {}; + $scope.media.changed = true; + $scope.media.deleted = true; }); - }, function (error) { - Notify.apiErrors(error); }); }; - - var deleteMedia = function (mediaId) { - // Delete previous media first - if (mediaId) { - return MediaEndpoint.delete({id: mediaId}).$promise; - } - - // Return a promise anyway if there is no media to delete - return $q.when(); - }; } }; }]; diff --git a/app/main/posts/modify/post-toolbox.directive.js b/app/main/posts/modify/post-toolbox.directive.js index 224a1fc564..e1406a28be 100644 --- a/app/main/posts/modify/post-toolbox.directive.js +++ b/app/main/posts/modify/post-toolbox.directive.js @@ -25,7 +25,6 @@ function PostToolboxDirective( $scope.changeStatus = changeStatus; $scope.allowedChangeStatus = allowedChangeStatus; $scope.allowedChangeOwner = allowedChangeOwner; - $scope.allowedChangeTimestamp = allowedChangeTimestamp; $scope.editAuthor = editAuthor; $scope.showUserRealname = showUserRealname; $scope.showAuthorRealname = showAuthorRealname; @@ -54,11 +53,6 @@ function PostToolboxDirective( return false; } - // FIXME: Enable after allowing change of post timestamp in API - function allowedChangeTimestamp() { - return false; - } - function editAuthor() { $scope.showEditAuthorButton = false; $scope.showEditAuthorForm = true; @@ -83,10 +77,10 @@ function PostToolboxDirective( } function formatDates() { - $scope.displayCreated = moment($scope.post.created).format('LT MMMM D, YYYY'); + $scope.displayCreated = moment($scope.post.created).format('LLL'); if ($scope.post.updated) { - $scope.displayUpdated = moment($scope.post.updated).format('LT MMMM D, YYYY'); + $scope.displayUpdated = moment($scope.post.updated).format('LLL'); } } } diff --git a/app/main/posts/modify/post-toolbox.html b/app/main/posts/modify/post-toolbox.html index 2123157153..670ece4acd 100644 --- a/app/main/posts/modify/post-toolbox.html +++ b/app/main/posts/modify/post-toolbox.html @@ -77,7 +77,7 @@

{{ post.author_realname }}

-
+
Post date
@@ -89,23 +89,13 @@
Post date
- - +
- -
- - diff --git a/app/main/posts/modify/post-value-edit.directive.js b/app/main/posts/modify/post-value-edit.directive.js index c28312c88e..a3086234e9 100644 --- a/app/main/posts/modify/post-value-edit.directive.js +++ b/app/main/posts/modify/post-value-edit.directive.js @@ -9,7 +9,8 @@ function PostValueEdit() { form: '=', post: '=', attribute: '=', - postField: '=' + postField: '=', + medias: '=' }, controller: PostValueEditController, templateUrl: 'templates/main/posts/modify/post-value-edit.html' @@ -49,7 +50,6 @@ function PostValueEditController( activate(); function activate() { - } function taskIsMarkedCompleted() { diff --git a/app/main/posts/modify/post-value-edit.html b/app/main/posts/modify/post-value-edit.html index dad4fb3441..4a5a2c0790 100644 --- a/app/main/posts/modify/post-value-edit.html +++ b/app/main/posts/modify/post-value-edit.html @@ -114,6 +114,7 @@ ng-switch-when="upload" name="values_{{attribute.id}}" media-id="post.values[attribute.key][key]" + media="medias[attribute.key]" ng-model="post.values[attribute.key][key]" ng-required="attribute.required" > diff --git a/app/main/posts/posts-module.js b/app/main/posts/posts-module.js index 36ff97d571..357752f1f9 100644 --- a/app/main/posts/posts-module.js +++ b/app/main/posts/posts-module.js @@ -16,6 +16,7 @@ angular.module('ushahidi.posts', []) // Create / Edit Screens .service('PostEntity', require('./modify/post-entity.service.js')) .service('PostEditService', require('./modify/post-edit.service.js')) +.service('MediaEditService', require('./modify/media-edit.service.js')) .directive('postMedia', require('./modify/post-media.directive.js')) .directive('postDatetime', require('./modify/post-datetime-value.directive.js')) .directive('postLocation', require('./modify/post-location.directive.js')) @@ -37,6 +38,8 @@ angular.module('ushahidi.posts', []) .directive('addPostButton', require('./views/add-post-button.directive.js')) .directive('addPostTextButton', require('./views/add-post-text-button.directive.js')) .directive('modeContextFormFilter', require('./views/mode-context-form-filter.directive.js')) +.directive('filterBySurvey', require('./views/filter-by-survey.directive.js')) +.directive('filterBySurveyDropdown', require('./views/filter-by-survey-dropdown.directive.js')) .directive('postToolbar', require('./views/post-toolbar.directive.js')) // Filters .directive('filterPosts', require('./views/filters/filter-posts.directive.js')) @@ -47,8 +50,12 @@ angular.module('ushahidi.posts', []) .directive('filterStatus', require('./views/filters/filter-status.directive.js')) .directive('filterLocation', require('./views/filters/filter-location.directive.js')) .directive('postActiveFilters', require('./views/filters/active-filters.directive.js')) -.directive('postExport', require('./views/post-export.directive.js')) .service('PostFilters', require('./views/post-filters.service.js')) + +// Share +.directive('postShare', require('./views/share/post-share.directive.js')) +.directive('shareMenu', require('./views/share/share-menu.directive.js')) +.directive('postExport', require('./views/share/post-export.directive.js')) // @todo move elsewhere? Used in post-view and activity .directive('postViewUnavailable', require('./views/post-view-unavailable.directive.js')) diff --git a/app/main/posts/views/filter-by-survey-dropdown.directive.js b/app/main/posts/views/filter-by-survey-dropdown.directive.js new file mode 100644 index 0000000000..dd48e975ba --- /dev/null +++ b/app/main/posts/views/filter-by-survey-dropdown.directive.js @@ -0,0 +1,28 @@ +module.exports = FilterBySurveryDropdownDirective; + +FilterBySurveryDropdownDirective.$inject = []; +function FilterBySurveryDropdownDirective() { + return { + restrict: 'A', + replace: true, + controller: FilterBySurveryDropdownController + }; +} + +FilterBySurveryDropdownController.$inject = ['$scope', '$rootScope']; +function FilterBySurveryDropdownController($scope, $rootScope) { + + $scope.filtersMenuOpen = false; + + activate(); + + function activate() { + $rootScope.$on('filters:open:dropdown', function () { + externalOpenDropDown(); + }); + } + + function externalOpenDropDown() { + $scope.filtersMenuOpen = true; + } +} diff --git a/app/main/posts/views/filter-by-survey.directive.js b/app/main/posts/views/filter-by-survey.directive.js new file mode 100644 index 0000000000..df7099b2bd --- /dev/null +++ b/app/main/posts/views/filter-by-survey.directive.js @@ -0,0 +1,29 @@ +module.exports = FilterBySurveryDirective; + +FilterBySurveryDirective.$inject = []; +function FilterBySurveryDirective() { + return { + restrict: 'E', + scope: { + filters: '=' + }, + replace: true, + controller: FilterBySurveryController, + templateUrl: 'templates/main/posts/views/filter-by-survey.html' + }; +} + +FilterBySurveryController.$inject = ['$scope', '$rootScope']; +function FilterBySurveryController($scope, $rootScope) { + + $scope.openFilters = openFilters; + + activate(); + + function activate() { + } + + function openFilters() { + $rootScope.$emit('filters:open:dropdown'); + } +} diff --git a/app/main/posts/views/filter-by-survey.html b/app/main/posts/views/filter-by-survey.html new file mode 100644 index 0000000000..8fb35cb702 --- /dev/null +++ b/app/main/posts/views/filter-by-survey.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/app/main/posts/views/filters/active-filters.directive.js b/app/main/posts/views/filters/active-filters.directive.js index 6a7c07ba4b..cc15af1049 100644 --- a/app/main/posts/views/filters/active-filters.directive.js +++ b/app/main/posts/views/filters/active-filters.directive.js @@ -48,6 +48,8 @@ function ActiveFilters($translate, $filter, PostFilters, _, TagEndpoint, RoleEnd var activeFilters = angular.copy(filters); rawFilters = angular.copy(filters); + // Remove set filter as it is only relevant to collections and should be immutable in that view + delete activeFilters.set; // Remove form filter as its shown by the mode-context-form-filter already delete activeFilters.form; // Remove within_km as its shown with the center_point value @@ -98,6 +100,12 @@ function ActiveFilters($translate, $filter, PostFilters, _, TagEndpoint, RoleEnd created_after : function (value) { return $filter('date', 'longdate')(value); }, + date_before : function (value) { + return $filter('date', 'longdate')(value); + }, + date_after : function (value) { + return $filter('date', 'longdate')(value); + }, status : function (value) { return $translate.instant('post.' + value); } diff --git a/app/main/posts/views/filters/filter-date.directive.js b/app/main/posts/views/filters/filter-date.directive.js index 6e15e62dd3..ea04262936 100644 --- a/app/main/posts/views/filters/filter-date.directive.js +++ b/app/main/posts/views/filters/filter-date.directive.js @@ -5,8 +5,8 @@ function DateSelectDirective() { return { restrict: 'E', scope: { - createdBeforeModel: '=', - createdAfterModel: '=' + dateBeforeModel: '=', + dateAfterModel: '=' }, controller: DateSelectController, templateUrl: 'templates/main/posts/views/filters/filter-date.html' diff --git a/app/main/posts/views/filters/filter-date.html b/app/main/posts/views/filters/filter-date.html index 3eb586851a..b62f8e68d4 100644 --- a/app/main/posts/views/filters/filter-date.html +++ b/app/main/posts/views/filters/filter-date.html @@ -7,7 +7,7 @@ - +
to @@ -19,7 +19,7 @@ - + diff --git a/app/main/posts/views/filters/filter-posts.html b/app/main/posts/views/filters/filter-posts.html index b23af1c51b..835fb1e4e9 100644 --- a/app/main/posts/views/filters/filter-posts.html +++ b/app/main/posts/views/filters/filter-posts.html @@ -29,7 +29,7 @@ - + diff --git a/app/main/posts/views/main.html b/app/main/posts/views/main.html index 930f7bb948..c45969af23 100644 --- a/app/main/posts/views/main.html +++ b/app/main/posts/views/main.html @@ -1,11 +1,10 @@
-
+

- @@ -30,5 +29,6 @@

+
diff --git a/app/main/posts/views/mode-context-form-filter.directive.js b/app/main/posts/views/mode-context-form-filter.directive.js index 5fa9f142f1..7e78a06815 100644 --- a/app/main/posts/views/mode-context-form-filter.directive.js +++ b/app/main/posts/views/mode-context-form-filter.directive.js @@ -10,8 +10,8 @@ function ModeContextFormFilterDirective() { }; } -ModeContextFormFilter.$inject = ['$scope', 'FormEndpoint', 'PostEndpoint', '$q', '_', '$rootScope', 'PostSurveyService']; -function ModeContextFormFilter($scope, FormEndpoint, PostEndpoint, $q, _, $rootScope, PostSurveyService) { +ModeContextFormFilter.$inject = ['$scope', 'FormEndpoint', 'PostEndpoint', '$q', '_', '$rootScope', 'PostSurveyService', '$timeout']; +function ModeContextFormFilter($scope, FormEndpoint, PostEndpoint, $q, _, $rootScope, PostSurveyService, $timeout) { $scope.forms = []; $scope.showOnly = showOnly; $scope.hide = hide; diff --git a/app/main/posts/views/mode-context-form-filter.html b/app/main/posts/views/mode-context-form-filter.html index 991f1b0435..813f5177d0 100644 --- a/app/main/posts/views/mode-context-form-filter.html +++ b/app/main/posts/views/mode-context-form-filter.html @@ -1,4 +1,5 @@
+
diff --git a/app/main/posts/views/post-export.html b/app/main/posts/views/post-export.html deleted file mode 100644 index b0036df9a9..0000000000 --- a/app/main/posts/views/post-export.html +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/app/main/posts/views/post-filters.service.js b/app/main/posts/views/post-filters.service.js index d9a78d8afd..f024ab4aa1 100644 --- a/app/main/posts/views/post-filters.service.js +++ b/app/main/posts/views/post-filters.service.js @@ -57,8 +57,8 @@ function PostFiltersService(_, FormEndpoint) { function getDefaults() { return { q: '', - created_after: '', - created_before: '', + date_after: '', + date_before: '', status: ['published', 'draft'], published_to: '', center_point: '', @@ -113,7 +113,7 @@ function PostFiltersService(_, FormEndpoint) { return true; } // Is an array with all the same elements? (order doesn't matter) - if (_.isArray(defaults[key]) && _.difference(defaults[key], value).length === 0) { + if (_.isArray(defaults[key]) && _.difference(value, defaults[key]).length === 0) { return true; } // Is value empty? ..and not a date object diff --git a/app/main/posts/views/post-toolbar.html b/app/main/posts/views/post-toolbar.html index 766d473779..b767b54881 100644 --- a/app/main/posts/views/post-toolbar.html +++ b/app/main/posts/views/post-toolbar.html @@ -4,10 +4,11 @@ - + + -
+
- +
- -
diff --git a/app/main/posts/views/post-view-list.directive.js b/app/main/posts/views/post-view-list.directive.js index 31451c30d6..ac92176cdf 100644 --- a/app/main/posts/views/post-view-list.directive.js +++ b/app/main/posts/views/post-view-list.directive.js @@ -92,13 +92,13 @@ function PostListController( yesterday = moment().subtract(1, 'days'); $scope.groupedPosts = _.groupBy(postsResponse.results, function (post) { - var created = moment(post.created); - if (now.isSame(created, 'd')) { + var postDate = moment(post.post_date); + if (now.isSame(postDate, 'd')) { return $translate.instant('nav.today'); - } else if (yesterday.isSame(created, 'd')) { + } else if (yesterday.isSame(postDate, 'd')) { return $translate.instant('nav.yesterday'); } else { - return created.fromNow(); + return postDate.fromNow(); } }); $scope.totalItems = postsResponse.total_count; diff --git a/app/main/posts/views/post-view.html b/app/main/posts/views/post-view.html index 66efa28ff7..650d8dde48 100644 --- a/app/main/posts/views/post-view.html +++ b/app/main/posts/views/post-view.html @@ -1,7 +1,6 @@
-
diff --git a/app/main/posts/views/post-export.directive.js b/app/main/posts/views/share/post-export.directive.js similarity index 95% rename from app/main/posts/views/post-export.directive.js rename to app/main/posts/views/share/post-export.directive.js index b5d8d69eb2..3d7c142bcd 100644 --- a/app/main/posts/views/post-export.directive.js +++ b/app/main/posts/views/share/post-export.directive.js @@ -4,12 +4,11 @@ PostExportDirective.$inject = []; function PostExportDirective() { return { restrict: 'E', - replace: true, scope: { filters: '=' }, controller: PostExportController, - templateUrl: 'templates/main/posts/views/post-export.html' + templateUrl: 'templates/main/posts/views/share/post-export.html' }; } diff --git a/app/main/posts/views/share/post-export.html b/app/main/posts/views/share/post-export.html new file mode 100644 index 0000000000..4fe43816d0 --- /dev/null +++ b/app/main/posts/views/share/post-export.html @@ -0,0 +1 @@ +Export to CSV diff --git a/app/main/posts/views/share/post-share.directive.js b/app/main/posts/views/share/post-share.directive.js new file mode 100644 index 0000000000..21046234e0 --- /dev/null +++ b/app/main/posts/views/share/post-share.directive.js @@ -0,0 +1,52 @@ +module.exports = PostShareDirective; + +PostShareDirective.$inject = []; +function PostShareDirective() { + return { + restrict: 'E', + replace: true, + scope: { + filters: '=', + button: '=?', + postId: '=?' + }, + controller: PostShareController, + templateUrl: 'templates/main/posts/views/share/post-share.html' + }; +} + +PostShareController.$inject = [ + '$scope', + '$window', + 'ModalService' +]; +function PostShareController( + $scope, + $window, + ModalService +) { + $scope.loading = false; + $scope.openShareMenu = openShareMenu; + $scope.isButton = isButton; + $scope.isAdd = isAdd; + + activate(); + + function activate() { + } + + function isButton() { + return $scope.button; + } + + function isAdd() { + if ($window.location.href.indexOf('post') > 0) { + return true; + } + return false; + } + + function openShareMenu() { + ModalService.openTemplate('', 'app.share', 'share', $scope, true, true); + } +} diff --git a/app/main/posts/views/share/post-share.html b/app/main/posts/views/share/post-share.html new file mode 100644 index 0000000000..3dbeaca170 --- /dev/null +++ b/app/main/posts/views/share/post-share.html @@ -0,0 +1,13 @@ + +
+
+
+
+
+ + + + + Share {{isButton()}} + +
diff --git a/app/main/posts/views/share/share-menu.directive.js b/app/main/posts/views/share/share-menu.directive.js new file mode 100644 index 0000000000..5621dffc7b --- /dev/null +++ b/app/main/posts/views/share/share-menu.directive.js @@ -0,0 +1,48 @@ +module.exports = ShareMenuDirective; + +ShareMenuDirective.$inject = []; +function ShareMenuDirective() { + return { + restrict: 'E', + replace: true, + controller: ShareMenuController, + templateUrl: 'templates/main/posts/views/share/share-menu.html' + }; +} + +ShareMenuController.$inject = [ + '$scope', + '$routeParams', + 'Util', + '$window' +]; +function ShareMenuController( + $scope, + $routeParams, + Util, + $window +) { + $scope.loading = false; + $scope.shareUrl = Util.currentUrl(); + $scope.isExportable = isExportable; + + activate(); + + function activate() { + // If we are in a post action menu + // Then we have to change the url to ensure that when + // selected from either the map or timeline view for + // an individual post that the URL is correct + if ($scope.postId) { + $scope.shareUrl = $window.location.origin + '/posts/' + $scope.postId; + } + $scope.shareUrlEncoded = encodeURIComponent($scope.shareUrl); + } + // Check if current view is exportable based on URI + function isExportable() { + if ($window.location.href.indexOf('post') > 0) { + return false; + } + return true; + } +} diff --git a/app/main/posts/views/share/share-menu.html b/app/main/posts/views/share/share-menu.html new file mode 100644 index 0000000000..c541e13f4d --- /dev/null +++ b/app/main/posts/views/share/share-menu.html @@ -0,0 +1,78 @@ + diff --git a/app/settings/data-import/data-import.html b/app/settings/data-import/data-import.html index c5640c43ce..bbb25057f7 100644 --- a/app/settings/data-import/data-import.html +++ b/app/settings/data-import/data-import.html @@ -45,7 +45,7 @@

- +
diff --git a/app/settings/data-import/import-complete.html b/app/settings/data-import/import-complete.html index f0637231c8..d689a2f7e8 100644 --- a/app/settings/data-import/import-complete.html +++ b/app/settings/data-import/import-complete.html @@ -4,10 +4,8 @@

The data from your CSV spreadsheet, {{filename}}, was successfully imported into your {{form_name}} survey.

- +See imported posts +Import another CSV file + diff --git a/app/settings/settings.module.js b/app/settings/settings.module.js index 6f982eed42..a504fce194 100644 --- a/app/settings/settings.module.js +++ b/app/settings/settings.module.js @@ -7,6 +7,7 @@ angular.module('ushahidi.settings', []) .directive('surveyTaskCreate', require('./surveys/task-create.directive.js')) .directive('surveyAttributeCreate', require('./surveys/attribute-create.directive.js')) .directive('surveyAttributeEditor', require('./surveys/attribute-editor.directive.js')) +.service('SurveyNotify', require('./surveys/survey.notify.service.js')) .directive('settingsList', require('./settings-list.directive.js')) .directive('settingsMap', require('./site/map.directive.js')) diff --git a/app/settings/site/settings-editor.html b/app/settings/site/settings-editor.html index e6c50ab885..8e817dde54 100644 --- a/app/settings/site/settings-editor.html +++ b/app/settings/site/settings-editor.html @@ -65,7 +65,7 @@

app.general

- +
diff --git a/app/settings/surveys/survey-editor.directive.js b/app/settings/surveys/survey-editor.directive.js index 6da1361616..770d2f6c5d 100644 --- a/app/settings/surveys/survey-editor.directive.js +++ b/app/settings/surveys/survey-editor.directive.js @@ -24,6 +24,7 @@ SurveyEditorController.$inject = [ 'RoleEndpoint', '_', 'Notify', + 'SurveyNotify', 'ModalService', 'Features' ]; @@ -39,6 +40,7 @@ function SurveyEditorController( RoleEndpoint, _, Notify, + SurveyNotify, ModalService, Features ) { @@ -522,7 +524,7 @@ function SurveyEditorController( }); }); - Notify.notify('notify.form.edit_form_success', { name: $scope.survey.name }); + SurveyNotify.success('notify.form.edit_form_success', { name: $scope.survey.name }, { formId: $scope.survey.id}); }, handleResponseErrors); } diff --git a/app/settings/surveys/survey-success.html b/app/settings/surveys/survey-success.html new file mode 100644 index 0000000000..968e70e621 --- /dev/null +++ b/app/settings/surveys/survey-success.html @@ -0,0 +1,8 @@ +

+ {{successText}} +

+ +Add to survey + diff --git a/app/settings/surveys/survey.notify.service.js b/app/settings/surveys/survey.notify.service.js new file mode 100644 index 0000000000..88307b9e67 --- /dev/null +++ b/app/settings/surveys/survey.notify.service.js @@ -0,0 +1,31 @@ +module.exports = SurveyNotify; + +var scope; + +SurveyNotify.$inject = ['_', '$q', '$rootScope', '$translate', 'SliderService', 'ModalService']; +function SurveyNotify(_, $q, $rootScope, $translate, SliderService, ModalService) { + return { + success: success + }; + + function success(successText, translateValues, values) { + var scope = getScope(); + + function showSlider(successText) { + values.successText = successText; + scope = _.extend(scope, values); + + SliderService.openUrl('templates/settings/surveys/survey-success.html', 'thumb-up', 'confirmation', scope, false, false); + } + + $translate(successText, translateValues).then(showSlider, showSlider); + } + + function getScope() { + if (scope) { + scope.$destroy(); + } + scope = $rootScope.$new(); + return scope; + } +} diff --git a/codeship-services.yml b/codeship-services.yml index 4ca3775a7a..0b29067ec0 100644 --- a/codeship-services.yml +++ b/codeship-services.yml @@ -18,6 +18,8 @@ deploy: image: deploy dockerfile_path: docker/deploy.Dockerfile encrypted_env_file: deployment.env.encrypted + volumes: + - ./tmp/out/last_build:/vols/last_build test: build: image: test diff --git a/codeship-steps.yml b/codeship-steps.yml index f8086e303d..48480ba538 100644 --- a/codeship-steps.yml +++ b/codeship-steps.yml @@ -1,20 +1,22 @@ - name: "Test" service: test command: gulp test + +- name: "Build client" + service: build + command: build + tag: '^(v[0-9]\.[0-9]+.[0-9]+(-[a-zA-Z0-9\.]+)?)|(develop)|(production)$' + - type: parallel steps: - - name: "Bundle" - type: serial + - name: "Release bundle" + service: release + command: release tag: '^v[0-9]\.[0-9]+.[0-9]+(-[a-zA-Z0-9\.]+)?$' - steps: - - service: build - command: build - - service: release - command: release - - name: "Deploy" + - name: "Deploy staging" service: deploy tag: develop - command: "ansible-playbook -vv -i hosts/rackspace_staging platform-client-update.yml -e=\"PLATFORM_CLIENT_VERSION=$CI_COMMIT_ID\"" + command: ansible-playbook -vv -i hosts/rackspace_staging platform-client-update-rsync.yml -e PLATFORM_CLIENT_DEPLOY_SRC="/vols/last_build" - name: "Deploy Production" service: deploy tag: production diff --git a/docker/build.run.sh b/docker/build.run.sh index f003b2e327..2e1937b68b 100644 --- a/docker/build.run.sh +++ b/docker/build.run.sh @@ -20,15 +20,15 @@ check_vols_out() { # Sync from source code to the build directory, exclude any folders and file # that are result of the build process -function sync { +sync() { check_vols_src - { + _sync_excludes() { echo "- .git" echo "- .bin" echo "- node_modules" echo "- tmp" - } > /tmp/rsync_exclude - rsync -ar --exclude-from=/tmp/rsync_exclude --delete-during /vols/src/ ./ + } + rsync -ar --exclude-from=<(_sync_excludes) --delete-during /vols/src/ ./ } # Build the client @@ -36,13 +36,23 @@ build() { npm install gulp transifex-download gulp build - cp ./server/rewrite.htaccess ./server/www/ +} + +# export the build files to a shared folder +export_build() { + # copy the raw build as a deployable + check_vols_out + if [ ! -d /vols/out/last_build ]; then + mkdir /vols/out/last_build + fi + rsync -ar --delete-during ./server/www/ /vols/out/last_build/ } # Bundle the build into a tarball bundle() { check_vols_out local version=${GITHUB_VERSION:-${CI_BRANCH:-v0.0.0}} + cp ./server/rewrite.htaccess ./server/www/ gulp release --version-suffix=${version} --dest-dir=/vols/out } @@ -54,6 +64,7 @@ case "$1" in *) sync build + export_build bundle ;; esac diff --git a/docker/deploy.run.sh b/docker/deploy.run.sh index 18dfc6cf22..c8cdb21a12 100755 --- a/docker/deploy.run.sh +++ b/docker/deploy.run.sh @@ -17,7 +17,7 @@ fi cat >> /opt/ansible.cfg << EOM [ssh_connection] -ssh_args= +control_path=/dev/shm/ansible-ssh-%%h-%%p-%%r EOM # ==> Get latest deployment code from github diff --git a/package.json b/package.json index e976233139..27e5e10998 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ushahidi-platform-client", - "version": "3.6.0", + "version": "3.6.1", "description": "Ushahidi Platform Official Web Client", "main": "gulpfile.js", "repository": { @@ -44,7 +44,7 @@ "gulp-plumber": "^1.0.1", "gulp-rename": "~1.2.0", "gulp-sass": "~2.3.1", - "gulp-sourcemaps": "^1.4.0", + "gulp-sourcemaps": "^1.8.0", "gulp-tar": "^1.3.2", "gulp-uglify": "^1.0.2", "gulp-util": "^3.0.6", @@ -94,7 +94,6 @@ "checklist-model": "~0.10.0", "d3": "^3.5.17", "leaflet": "~0.7.3", - "leaflet-draw": "~0.3.0", "leaflet.locatecontrol": "^0.52.0", "leaflet.markercluster": "0.5.0", "moment": "^2.13.0", @@ -104,9 +103,9 @@ "nvd3": "^1.8.4", "selection-model": "jtrussell/angular-selection-model.git", "underscore": "^1.7.0", - "ushahidi-platform-pattern-library": "^3.6.0" + "ushahidi-platform-pattern-library": "3.6.1" }, "engines": { - "node": ">=0.10" + "node": ">=4.0" } } diff --git a/server/www/index.html b/server/www/index.html index a0f941349c..0f3c4d3903 100644 --- a/server/www/index.html +++ b/server/www/index.html @@ -41,7 +41,7 @@