From 894f8ecc0e2f3dee548650b460dee4318b5b6079 Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Thu, 14 Mar 2019 09:04:32 +0000 Subject: [PATCH 01/39] Support Tools can find encoded id --- http/template/modtools/support/main.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http/template/modtools/support/main.html b/http/template/modtools/support/main.html index 8f20c229f..5144029b8 100644 --- a/http/template/modtools/support/main.html +++ b/http/template/modtools/support/main.html @@ -10,7 +10,8 @@

-

You can find any user in the system. This search only matches from the left or right hand sides, otherwise it's too slow at the moment. So test@gmail.co would work, as would gmail.com, but est@gmail.com wouldn't.

+

You can find any user in the system using their email, numerical id, or the ~- encoded form of their id.

+

This search only matches from the left or right hand sides, otherwise it's too slow at the moment. So test@gmail.co would work, as would gmail.com, but est@gmail.com wouldn't.

From d47f9ed04a2f34f5aefb9c4f80b59f19551ba7e6 Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Wed, 20 Mar 2019 09:54:46 +0000 Subject: [PATCH 02/39] Existing spammers don't have a suspectreason so hide that alert --- http/template/modtools/members/spam/member.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http/template/modtools/members/spam/member.html b/http/template/modtools/members/spam/member.html index 97e3b171e..891b72051 100644 --- a/http/template/modtools/members/spam/member.html +++ b/http/template/modtools/members/spam/member.html @@ -23,9 +23,11 @@
+ <% if (obj.suspectreason) { %>
{{suspectreason}}
+ <% } %>
From c4ca2dde73cac81f53f5ddf6267d1a3354d46934 Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Fri, 22 Mar 2019 16:42:11 +0000 Subject: [PATCH 03/39] Change Newsfeed to ChitChat --- http/template/user/newsfeed/single.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/template/user/newsfeed/single.html b/http/template/user/newsfeed/single.html index 428039d91..342f304b4 100644 --- a/http/template/user/newsfeed/single.html +++ b/http/template/user/newsfeed/single.html @@ -5,7 +5,7 @@ From 4f01d9f1476b6fc4a18c14fb44650474ab7c7460 Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Sun, 24 Mar 2019 19:03:08 +0000 Subject: [PATCH 04/39] Streamline freegling of multiple items --- http/js/iznik/views/pages/user/post.js | 34 +++++++++++++++++++++++--- http/template/user/give/whatnext.html | 2 +- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/http/js/iznik/views/pages/user/post.js b/http/js/iznik/views/pages/user/post.js index 80596c004..d60226dcb 100644 --- a/http/js/iznik/views/pages/user/post.js +++ b/http/js/iznik/views/pages/user/post.js @@ -408,7 +408,13 @@ define([ events: { 'change .js-email': 'changeEmail', 'keyup .js-email': 'changeEmail', - 'click .js-next': 'doit' + 'click .js-next': 'clickDoit' + }, + + clickDoit() { + // This means they've confirmed their email + Storage.set('myemailat', (new Date()).getTime()) + this.doit() }, doit: function () { @@ -460,10 +466,14 @@ define([ } }, - changeEmail: function () { + changeEmail: function (auto) { var email = this.$('.js-email').val() try { Storage.set('myemail', email) + + if (!auto) { + Storage.set('myemailat', (new Date()).getTime()) + } } catch (e) { } @@ -478,6 +488,20 @@ define([ } }, + maybeAuto: function() { + // If we have confirmed our email address today, then don't bother asking again. This is useful if you're + // listing multiple items. + var self = this; + var last = Storage.get('myemailat') + + if (last) { + if ((new Date()).getTime() - last < 24 * 60 * 60 * 1000) { + // Recent + self.doit() + } + } + }, + render: function () { var p = Iznik.Views.Page.prototype.render.call(this) p.then(function (self) { @@ -485,14 +509,16 @@ define([ if (loggedIn) { // We know our email address from the session self.$('.js-email').val(Iznik.Session.get('me').email) - self.changeEmail() + self.changeEmail(true) + self.maybeAuto() } else { // We're not logged in - but we might have remembered one. try { var email = Storage.get('myemail') if (email) { self.$('.js-email').val(email) - self.changeEmail() + self.changeEmail(true) + self.maybeAuto() } } catch (e) { } diff --git a/http/template/user/give/whatnext.html b/http/template/user/give/whatnext.html index 0b65dad90..bb398ced5 100644 --- a/http/template/user/give/whatnext.html +++ b/http/template/user/give/whatnext.html @@ -29,7 +29,7 @@

What next?

- +
 Give something else
From 4118897aa8243c36925fb16c7713e6848f405cc1 Mon Sep 17 00:00:00 2001 From: edwh Date: Fri, 29 Mar 2019 09:17:17 +0000 Subject: [PATCH 05/39] =?UTF-8?q?Try=20donation=20ask=20at=20=C2=A33?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- http/template/communityevents/confirm.html | 2 +- http/template/user/find/share.html | 2 +- http/template/user/give/share.html | 2 +- http/template/user/landing/about.html | 2 +- http/template/user/landing/donate.html | 2 +- http/template/user/support/askdonation.html | 2 +- http/template/user/support/askdonationgroup.html | 2 +- http/template/volunteering/confirm.html | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/http/template/communityevents/confirm.html b/http/template/communityevents/confirm.html index 42d4fd5c0..a4661133f 100644 --- a/http/template/communityevents/confirm.html +++ b/http/template/communityevents/confirm.html @@ -7,7 +7,7 @@
diff --git a/http/template/user/support/askdonation.html b/http/template/user/support/askdonation.html index c6a00093a..c15dfdbb0 100644 --- a/http/template/user/support/askdonation.html +++ b/http/template/user/support/askdonation.html @@ -10,7 +10,7 @@

Freegle is a charity which is free to use, but not free to run.

This month we need to raise £2000 from donations across the UK - can you help?

-

If you can, please donate £5 to keep us running - but anything you can give is very welcome.

+

If you can, please donate £3 to keep us running - but anything you can give is very welcome.

From 77232aff9f9565ca82c92519201c3668cba04dfb Mon Sep 17 00:00:00 2001 From: edwh Date: Fri, 29 Mar 2019 13:40:32 +0000 Subject: [PATCH 07/39] Updates to council toolkit pages --- http/js/iznik/router.js | 14 +++++++++++ http/js/iznik/views/pages/user/councils.js | 4 ++++ .../template/user/councils/businesscards.html | 9 +++----- http/template/user/councils/contents.html | 3 +++ http/template/user/councils/giveandtake.html | 23 +++++++++++++++++++ http/template/user/councils/keylinks.html | 2 +- http/template/user/councils/socialmedia.html | 12 ++++++---- http/template/user/councils/workbest.html | 2 +- 8 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 http/template/user/councils/giveandtake.html diff --git a/http/js/iznik/router.js b/http/js/iznik/router.js index 1a32d1ac1..2bcffb25d 100644 --- a/http/js/iznik/router.js +++ b/http/js/iznik/router.js @@ -227,6 +227,7 @@ define([ 'councils/volunteers': 'userCouncilsVolunteers', 'councils/keylinks(/:section)': 'userCouncilsKeyLinks', 'councils/workbest(/:section)': 'userCouncilsWorkBest', + 'councils/giveandtake(/:section)': 'userCouncilsGiveAndTake', 'councils/graphics(/:section)': 'userCouncilsGraphics', 'councils/photosvideos(/:section)': 'userCouncilsPhotosVideos', 'councils/posters': 'userCouncilsPosters', @@ -2138,6 +2139,19 @@ define([ } }, + userCouncilsGiveAndTake: function (section) { + if (!MODTOOLS) { + var self = this + + require(['iznik/views/pages/user/councils'], function () { + var page = new Iznik.Views.User.Pages.Councils.GiveAndTake({ + section: section + }) + self.loadRoute({page: page}) + }) + } + }, + userCouncilsGraphics: function (section) { if (!MODTOOLS) { var self = this diff --git a/http/js/iznik/views/pages/user/councils.js b/http/js/iznik/views/pages/user/councils.js index 8c3fbde82..7677b02d3 100644 --- a/http/js/iznik/views/pages/user/councils.js +++ b/http/js/iznik/views/pages/user/councils.js @@ -76,6 +76,10 @@ define([ template: "user_councils_workbest", }); + Iznik.Views.User.Pages.Councils.GiveAndTake = Iznik.Views.User.Pages.Councils.extend({ + template: "user_councils_giveandtake", + }); + Iznik.Views.User.Pages.Councils.Graphics = Iznik.Views.User.Pages.Councils.extend({ template: "user_councils_graphics", }); diff --git a/http/template/user/councils/businesscards.html b/http/template/user/councils/businesscards.html index a55ed76fa..6f148276f 100644 --- a/http/template/user/councils/businesscards.html +++ b/http/template/user/councils/businesscards.html @@ -6,12 +6,9 @@

Business cards

- Business cards are a relatively cheap way of promoting your local Freegle community. You can get them - printed from a local printer or online printers such as - Vistaprint - or if you want to be really eco-friendly, - why not try recycled cotton business cards from - Moo. + Business cards are a simple and inexpensive way for your council to promote Freegle and are a good + alternative when a poster may not be appropriate. Business cards can be handed out at your events, + left at council owned or managed buildings for distribution or sent out with mailshots.

A generic Freegle business card graphic is available but If you want another size or a specific diff --git a/http/template/user/councils/contents.html b/http/template/user/councils/contents.html index 772eeb192..9026eeee7 100644 --- a/http/template/user/councils/contents.html +++ b/http/template/user/councils/contents.html @@ -27,6 +27,9 @@

  • Promoting your events and campaigns using Freegle
  • +

    + Run a Give and Take day +

    Graphics, logos and badges

      diff --git a/http/template/user/councils/giveandtake.html b/http/template/user/councils/giveandtake.html new file mode 100644 index 000000000..58b7868a5 --- /dev/null +++ b/http/template/user/councils/giveandtake.html @@ -0,0 +1,23 @@ +
      +
      +

      Running a Give and Take day

      +
      +
      +
      +
      +

      + Several councils run Give and Take days in partnership with Freegle. These are one-day community + events that allow local residents to give items that they no longer need and take items that + they want. Think of it like a bring and buy stall, but everything is free! Give and Take + days are great for: +

      +
        +
      • Raising awareness of waste and reuse in your local area.
      • +
      • Providing good PR for your council.
      • +
      • Diverting items from HWRCs/residual waste/bulky waste collections.
      • +
      • Building community!
      • +
      +
      +
      +
      +
      diff --git a/http/template/user/councils/keylinks.html b/http/template/user/councils/keylinks.html index aa1dacc4f..4ed535d88 100644 --- a/http/template/user/councils/keylinks.html +++ b/http/template/user/councils/keylinks.html @@ -47,7 +47,7 @@

      Heatmap

    -
    +
    in
    diff --git a/http/template/user/councils/socialmedia.html b/http/template/user/councils/socialmedia.html index a0c93b08f..095dd9f3a 100644 --- a/http/template/user/councils/socialmedia.html +++ b/http/template/user/councils/socialmedia.html @@ -6,6 +6,7 @@

    Social Media

    General

    +

    Facebook

    +

    Twitter

    +
    -
    -

    Decluttering

    +

    Twitter

    Moving house

    +

    Facebook

    Regifting

    +

    Facebook

    Fly-tipping

    +

    Twitter

    diff --git a/http/template/user/councils/workbest.html b/http/template/user/councils/workbest.html index ec675ac3c..ed5f600b4 100644 --- a/http/template/user/councils/workbest.html +++ b/http/template/user/councils/workbest.html @@ -86,7 +86,7 @@

    How Freegle can help right across your local authority

  • Housing – Providing cheap furniture and other items to families in need. - End of tenancy clearances, upgrades to interiors. + End of tenancy clearances and refurbishments.
  • Estates – When disposing of or acquiring properties, Freegle could come in useful for getting From 20dcab66a6460cd194c8d6865357510699598916 Mon Sep 17 00:00:00 2001 From: edwh Date: Fri, 29 Mar 2019 13:58:07 +0000 Subject: [PATCH 08/39] * In Find, always default to searching OFFERs even if you have previously chosen to search WANTEDs. This reduces confusion. --- http/js/iznik/views/pages/user/find.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/http/js/iznik/views/pages/user/find.js b/http/js/iznik/views/pages/user/find.js index 6eaf522df..c491c4f5b 100644 --- a/http/js/iznik/views/pages/user/find.js +++ b/http/js/iznik/views/pages/user/find.js @@ -158,15 +158,10 @@ define([ var data; + // Deliberately don't restore search type, letting it revert to searching OFFERs. There have been + // people who search WANTEDs once and then get confused. self.searchtype = 'Offer'; - - try { - var stored = Storage.get('searchtype'); - - if (stored) { - self.searchtype = stored; - } - } catch (e) {} + self.saveSearchType() self.$(".js-searchoffers").bootstrapSwitch({ onText: 'Only OFFERs', From 4c315c6654fed0779c96629aa82b12c5485fcd5b Mon Sep 17 00:00:00 2001 From: edwh Date: Sat, 30 Mar 2019 07:30:29 +0000 Subject: [PATCH 09/39] In MT, show unlinked groups on Publicity page --- http/js/iznik/views/pages/modtools/social.js | 577 +++++++++--------- .../template/modtools/socialactions/main.html | 1 + 2 files changed, 292 insertions(+), 286 deletions(-) diff --git a/http/js/iznik/views/pages/modtools/social.js b/http/js/iznik/views/pages/modtools/social.js index 12130cc3a..475518736 100644 --- a/http/js/iznik/views/pages/modtools/social.js +++ b/http/js/iznik/views/pages/modtools/social.js @@ -1,289 +1,294 @@ define([ - 'jquery', - 'underscore', - 'backbone', - 'moment', - 'iznik/base', - 'typeahead', - 'jquery.validate.min', - 'jquery.validate.additional-methods', - 'iznik/customvalidate', - 'iznik/modtools', - 'iznik/models/social', - 'iznik/views/pages/pages', - 'iznik/views/infinite', - 'iznik/views/supportus', - 'iznik/views/postaladdress' -], function($, _, Backbone, moment, Iznik) { - Iznik.Views.ModTools.Pages.SocialActions = Iznik.Views.Infinite.extend({ - modtools: true, - - template: "modtools_socialactions_main", - - retField: 'socialactions', - - events: { - 'click .js-businesscards': 'businessCards' - }, - - businessCards: function () { - var v = new Iznik.Views.User.BusinessCards(); - v.render(); - }, - - render: function () { - var self = this; - var p = Iznik.Views.Infinite.prototype.render.call(this); - - p.then(function (self) { - require(['iznik/facebook'], function (FBLoad) { - self.listenToOnce(FBLoad(), 'fbloaded', function () { - if (!FBLoad().isDisabled()) { - self.$('.js-facebookonly').show(); - } - }); - - FBLoad().render(); - }); - - var v = new Iznik.Views.Help.Box(); - v.template = 'modtools_socialactions_help'; - v.render().then(function (v) { - self.$('.js-help').html(v.el); - }) - - self.lastFetched = null; - self.context = null; - - self.collection = new Iznik.Collections.SocialActions(); - - self.collectionView = new Backbone.CollectionView({ - el: self.$('.js-list'), - modelView: Iznik.Views.ModTools.SocialAction, - collection: self.collection, - processKeyEvents: false - }); - - self.collectionView.render(); - self.fetch(); - - self.requests = new Iznik.Collections.Requests(); - - self.requestCollectionView = new Backbone.CollectionView({ - el: self.$('.js-requestlist'), - modelView: Iznik.Views.ModTools.SocialAction.Request, - collection: self.requests, - processKeyEvents: false - }); - - self.requestCollectionView.render(); - self.requests.fetch(); - - if (Iznik.Session.hasPermission('BusinessCardsAdmin')) { - self.outstanding = new Iznik.Collections.Requests(); - - self.outstandingCollectionView = new Backbone.CollectionView({ - el: self.$('.js-outstandinglist'), - modelView: Iznik.Views.ModTools.SocialAction.Outstanding, - collection: self.outstanding, - processKeyEvents: false - }); - - self.outstandingCollectionView.render(); - self.outstanding.fetch({ - data: { - outstanding: true - } - }); - - self.recent = new Iznik.Collections.Requests.Recent(); - - self.recentCollectionView = new Backbone.CollectionView({ - el: self.$('.js-recentlist'), - modelView: Iznik.Views.ModTools.SocialAction.Recent, - collection: self.recent, - processKeyEvents: false - }); - - self.recentCollectionView.render(); - self.recent.fetch({ - data: { - recent: true - } - }).then(function() { - if (self.recent.length) { - self.$('.js-recentwrapper').fadeIn('slow'); - } - }); - } - }); - - return (p); - } - }); - - Iznik.Views.ModTools.SocialAction = Iznik.View.extend({ - tagName: 'li', - - template: 'modtools_socialactions_one', - - render: function () { - var self = this; - var p = Iznik.View.prototype.render.call(this); - p.then(function (self) { - // Show buttons for the remaining Facebook groups/pages that haven't shared this. - // - // We have a list of those inside each group in our session. - self.$('.js-buttons').empty(); - var sharelist = []; - var uids = self.model.get('uids'); - var groups = Iznik.Session.get('groups'); - - _.each(uids, function (uid) { - groups.each(function (group) { - if (group.get('type') == 'Freegle') { - var facebooks = group.get('facebook'); - - if (facebooks) { - _.each(facebooks, function (facebook) { - if (facebook.uid == uid) { - // This is the one we would want to share on. - sharelist.push(new Iznik.Model(facebook)); - } - }); - } - } - }); - }); - - self.shares = new Iznik.Collection(sharelist); - self.shares.comparator = 'name'; - self.shares.sort(); - - self.shares.each(function (share) { - // Page shares happen on the server. Group ones don't so need a Facebook session. - if (share.get('type') == 'Page' || (share.get('type') == 'Group' && Iznik.Session.hasFacebook())) { - var v = new Iznik.Views.ModTools.SocialAction.FacebookGroupShare({ - model: share, - actionid: self.model.get('id'), - action: self.model - }); - - v.render().then(function () { - self.$('.js-buttons').append(v.$el); - }); - } - }); - - var v = new Iznik.Views.ModTools.SocialAction.FacebookPageHide({ - actionid: self.model.get('id'), - action: self.model, - hideWhenDone: self.$el, - shares: self.shares - }); - - v.render().then(function () { - self.$('.js-buttons').append(v.$el); - }); - }); - - return (this); - } - }); - - Iznik.Views.ModTools.SocialAction.FacebookGroupShare = Iznik.View.extend({ - template: 'modtools_socialactions_facebookshare', - - tagName: 'li', - - events: { - 'click .js-share': 'share' - }, - - share: function () { - var self = this; - - $.ajax({ - url: API + 'socialactions', - type: 'POST', - data: { - id: self.options.actionid, - uid: self.model.get('uid'), - action: 'Do' - } - }); - - self.$el.fadeOut('slow'); - } - }); - - Iznik.Views.ModTools.SocialAction.FacebookPageHide = Iznik.View.extend({ - template: 'modtools_socialactions_facebookhide', - - tagName: 'li', - - events: { - 'click .js-hide': 'hide' - }, - - hide: function () { - var self = this; - - self.options.shares.each(function (share) { - $.ajax({ - url: API + 'socialactions', - type: 'POST', - data: { - id: self.options.actionid, - uid: share.get('uid'), - action: 'Hide' - } - }); - }); - - self.options.hideWhenDone.fadeOut('slow'); - } - }); - - Iznik.Views.ModTools.SocialAction.Request = Iznik.View.Timeago.extend({ - template: 'modtools_socialactions_request', - - tagName: 'li', - - events: { - 'click .js-delete': 'deleteIt' - }, - - deleteIt: function () { - var self = this; - this.model.destroy().then(self.$el.fadeOut('slow')); + 'jquery', + 'underscore', + 'backbone', + 'moment', + 'iznik/base', + 'typeahead', + 'jquery.validate.min', + 'jquery.validate.additional-methods', + 'iznik/customvalidate', + 'iznik/modtools', + 'iznik/models/social', + 'iznik/views/pages/pages', + 'iznik/views/infinite', + 'iznik/views/supportus', + 'iznik/views/postaladdress' +], function ($, _, Backbone, moment, Iznik) { + Iznik.Views.ModTools.Pages.SocialActions = Iznik.Views.Infinite.extend({ + modtools: true, + + template: 'modtools_socialactions_main', + + retField: 'socialactions', + + events: { + 'click .js-businesscards': 'businessCards' + }, + + businessCards: function () { + var v = new Iznik.Views.User.BusinessCards() + v.render() + }, + + render: function () { + var self = this + var p = Iznik.Views.Infinite.prototype.render.call(this) + + p.then(function (self) { + require(['iznik/facebook'], function (FBLoad) { + self.listenToOnce(FBLoad(), 'fbloaded', function () { + if (!FBLoad().isDisabled()) { + self.$('.js-facebookonly').show() + } + }) + + FBLoad().render() + }) + + var v = new Iznik.Views.Help.Box() + v.template = 'modtools_socialactions_help' + v.render().then(function (v) { + self.$('.js-help').html(v.el) + }) + + var w = new Iznik.Views.ModTools.Settings.MissingFacebook() + w.render().then(function () { + self.$('.js-missingfacebook').html(w.el) + }) + + self.lastFetched = null + self.context = null + + self.collection = new Iznik.Collections.SocialActions() + + self.collectionView = new Backbone.CollectionView({ + el: self.$('.js-list'), + modelView: Iznik.Views.ModTools.SocialAction, + collection: self.collection, + processKeyEvents: false + }) + + self.collectionView.render() + self.fetch() + + self.requests = new Iznik.Collections.Requests() + + self.requestCollectionView = new Backbone.CollectionView({ + el: self.$('.js-requestlist'), + modelView: Iznik.Views.ModTools.SocialAction.Request, + collection: self.requests, + processKeyEvents: false + }) + + self.requestCollectionView.render() + self.requests.fetch() + + if (Iznik.Session.hasPermission('BusinessCardsAdmin')) { + self.outstanding = new Iznik.Collections.Requests() + + self.outstandingCollectionView = new Backbone.CollectionView({ + el: self.$('.js-outstandinglist'), + modelView: Iznik.Views.ModTools.SocialAction.Outstanding, + collection: self.outstanding, + processKeyEvents: false + }) + + self.outstandingCollectionView.render() + self.outstanding.fetch({ + data: { + outstanding: true + } + }) + + self.recent = new Iznik.Collections.Requests.Recent() + + self.recentCollectionView = new Backbone.CollectionView({ + el: self.$('.js-recentlist'), + modelView: Iznik.Views.ModTools.SocialAction.Recent, + collection: self.recent, + processKeyEvents: false + }) + + self.recentCollectionView.render() + self.recent.fetch({ + data: { + recent: true + } + }).then(function () { + if (self.recent.length) { + self.$('.js-recentwrapper').fadeIn('slow') + } + }) } - }); - - Iznik.Views.ModTools.SocialAction.Outstanding = Iznik.View.Timeago.extend({ - template: 'modtools_socialactions_outstanding', - - tagName: 'li', - - events: { - 'click .js-delete': 'deleteIt', - 'click .js-sent': 'sent' - }, - - sent: function () { - var self = this; - this.model.completed().then(self.$el.fadeOut('slow')); - }, - - deleteIt: function () { - var self = this; - this.model.destroy().then(self.$el.fadeOut('slow')); + }) + + return (p) + } + }) + + Iznik.Views.ModTools.SocialAction = Iznik.View.extend({ + tagName: 'li', + + template: 'modtools_socialactions_one', + + render: function () { + var self = this + var p = Iznik.View.prototype.render.call(this) + p.then(function (self) { + // Show buttons for the remaining Facebook groups/pages that haven't shared this. + // + // We have a list of those inside each group in our session. + self.$('.js-buttons').empty() + var sharelist = [] + var uids = self.model.get('uids') + var groups = Iznik.Session.get('groups') + + _.each(uids, function (uid) { + groups.each(function (group) { + if (group.get('type') == 'Freegle') { + var facebooks = group.get('facebook') + + if (facebooks) { + _.each(facebooks, function (facebook) { + if (facebook.uid == uid) { + // This is the one we would want to share on. + sharelist.push(new Iznik.Model(facebook)) + } + }) + } + } + }) + }) + + self.shares = new Iznik.Collection(sharelist) + self.shares.comparator = 'name' + self.shares.sort() + + self.shares.each(function (share) { + // Page shares happen on the server. Group ones don't so need a Facebook session. + if (share.get('type') == 'Page' || (share.get('type') == 'Group' && Iznik.Session.hasFacebook())) { + var v = new Iznik.Views.ModTools.SocialAction.FacebookGroupShare({ + model: share, + actionid: self.model.get('id'), + action: self.model + }) + + v.render().then(function () { + self.$('.js-buttons').append(v.$el) + }) + } + }) + + var v = new Iznik.Views.ModTools.SocialAction.FacebookPageHide({ + actionid: self.model.get('id'), + action: self.model, + hideWhenDone: self.$el, + shares: self.shares + }) + + v.render().then(function () { + self.$('.js-buttons').append(v.$el) + }) + }) + + return (this) + } + }) + + Iznik.Views.ModTools.SocialAction.FacebookGroupShare = Iznik.View.extend({ + template: 'modtools_socialactions_facebookshare', + + tagName: 'li', + + events: { + 'click .js-share': 'share' + }, + + share: function () { + var self = this + + $.ajax({ + url: API + 'socialactions', + type: 'POST', + data: { + id: self.options.actionid, + uid: self.model.get('uid'), + action: 'Do' } - }); - - Iznik.Views.ModTools.SocialAction.Recent = Iznik.View.extend({ - tagName: 'li', - - template: 'modtools_socialactions_recent' - }); -}); \ No newline at end of file + }) + + self.$el.fadeOut('slow') + } + }) + + Iznik.Views.ModTools.SocialAction.FacebookPageHide = Iznik.View.extend({ + template: 'modtools_socialactions_facebookhide', + + tagName: 'li', + + events: { + 'click .js-hide': 'hide' + }, + + hide: function () { + var self = this + + self.options.shares.each(function (share) { + $.ajax({ + url: API + 'socialactions', + type: 'POST', + data: { + id: self.options.actionid, + uid: share.get('uid'), + action: 'Hide' + } + }) + }) + + self.options.hideWhenDone.fadeOut('slow') + } + }) + + Iznik.Views.ModTools.SocialAction.Request = Iznik.View.Timeago.extend({ + template: 'modtools_socialactions_request', + + tagName: 'li', + + events: { + 'click .js-delete': 'deleteIt' + }, + + deleteIt: function () { + var self = this + this.model.destroy().then(self.$el.fadeOut('slow')) + } + }) + + Iznik.Views.ModTools.SocialAction.Outstanding = Iznik.View.Timeago.extend({ + template: 'modtools_socialactions_outstanding', + + tagName: 'li', + + events: { + 'click .js-delete': 'deleteIt', + 'click .js-sent': 'sent' + }, + + sent: function () { + var self = this + this.model.completed().then(self.$el.fadeOut('slow')) + }, + + deleteIt: function () { + var self = this + this.model.destroy().then(self.$el.fadeOut('slow')) + } + }) + + Iznik.Views.ModTools.SocialAction.Recent = Iznik.View.extend({ + tagName: 'li', + + template: 'modtools_socialactions_recent' + }) +}) \ No newline at end of file diff --git a/http/template/modtools/socialactions/main.html b/http/template/modtools/socialactions/main.html index cbb0f95c5..15432ea29 100644 --- a/http/template/modtools/socialactions/main.html +++ b/http/template/modtools/socialactions/main.html @@ -12,6 +12,7 @@

    Want some Freegle business cards or a Poster?

  • +

    Advice for discussing Freegle with residents

    @@ -66,6 +73,10 @@

    Talking to residents about bulky waste

    with residents. This is something that Cumbria County Council have done. We are happy to put you in touch with them if you would like to discuss how this has worked in their county.

    +

    + The London Borough of Richmond Council does an excellent job of reminding residents they can save + money by using Freegle instead of paying for a bulky waste collection. See here. +

    Household Waste Recycling Centres - the Freegle alternative

    By the time your residents have reached the HWRC it's generally too late for them to reconsider dumping @@ -105,6 +116,9 @@

    Promoting your events on Freegle

    for free on Freegle to help you reach more of your community. If you’d like to do this, please get in touch with your local Freegle community volunteer.

    +

    + You can also easily add your events yourself here. +

    From a59e05b57cd365c3131055e000d3421b4cd94c07 Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Tue, 2 Apr 2019 14:51:57 +0100 Subject: [PATCH 19/39] Council toolkit improvements --- http/template/user/councils/workbest.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http/template/user/councils/workbest.html b/http/template/user/councils/workbest.html index ffa702db0..15829c3a2 100644 --- a/http/template/user/councils/workbest.html +++ b/http/template/user/councils/workbest.html @@ -40,9 +40,10 @@

    Making Freegle visible on your council websites

    A-Z - If you have an A-Z of waste and recycling, please list Freegle as an option. Brighton & Hove City Council are a great example of how this can be done - see here. -
  • Collections
  • - the London Borough of Richmond Council does an excellent job of reminding residents +
  • Collections - the London Borough of Richmond Council does an excellent job of reminding residents they can save money by using Freegle instead of paying for a bulky waste collection. See here. +
  • Advice for discussing Freegle with residents

    From e7b45bc356496edfafe3f58fcec74123acf3c0ee Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Fri, 5 Apr 2019 11:02:53 +0100 Subject: [PATCH 20/39] Board page doesn't render correctly --- http/js/iznik/router.js | 1 + 1 file changed, 1 insertion(+) diff --git a/http/js/iznik/router.js b/http/js/iznik/router.js index 31c4180ce..63999ad7e 100644 --- a/http/js/iznik/router.js +++ b/http/js/iznik/router.js @@ -1983,6 +1983,7 @@ define([ self.listenToOnce(Iznik.Session, 'loggedIn', function () { require(['iznik/views/teams'], function () { var page = new Iznik.Views.ModTools.Pages.Teams() + page.modtools = false; self.loadRoute({page: page}) }) }) From 6abf39de9bbc0ae2ce93d73f1913dc41e00f28e6 Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Tue, 9 Apr 2019 17:32:34 +0100 Subject: [PATCH 21/39] * Add link to Accounts into About Us --- http/template/user/landing/about.html | 1 + 1 file changed, 1 insertion(+) diff --git a/http/template/user/landing/about.html b/http/template/user/landing/about.html index f346c8cd7..d15e1092e 100644 --- a/http/template/user/landing/about.html +++ b/http/template/user/landing/about.html @@ -68,6 +68,7 @@

    Freegle is free to use, but not free to run. We provide a free service, and keep costs phenomenally low thanks to our large number of committed volunteers - our annual budget averages just £40,000 each year. + You can see our accounts here.

    Nevertheless, there are still costs involved, and if we had more money we could get more people freegling more From 6cb62dba99858cb8353b8f1d5767d8569ba5dae8 Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Tue, 9 Apr 2019 20:07:39 +0100 Subject: [PATCH 22/39] Volunteer sidebar is showing when logged out, which it isn't supposed to --- http/js/iznik/views/pages/user/explore.js | 1662 +++++++++++---------- 1 file changed, 832 insertions(+), 830 deletions(-) diff --git a/http/js/iznik/views/pages/user/explore.js b/http/js/iznik/views/pages/user/explore.js index f8c10568c..2c6872237 100644 --- a/http/js/iznik/views/pages/user/explore.js +++ b/http/js/iznik/views/pages/user/explore.js @@ -1,887 +1,889 @@ define([ - 'jquery', - 'underscore', - 'backbone', - 'iznik/base', - 'moment', - 'googlemaps-js-rich-marker', - 'iznik/views/pages/pages', - 'iznik/views/pages/user/pages', - 'iznik/views/pages/user/group', - 'iznik/views/user/message', - 'iznik/views/group/communityevents', - 'iznik/views/group/volunteering', - 'iznik/models/group', - 'geocomplete' -], function ($, _, Backbone, Iznik, moment,r) { - var RichMarker = r.default.RichMarker; - - Iznik.Views.User.Pages.Explore = Iznik.Views.Page.extend({ - template: 'user_explore_main', - - title: 'Explore', - - events: { - 'click .js-getloc': 'getLocation', - 'click .js-locbtn': 'locButton', - 'keyup .js-location': 'keyUp' - }, + 'jquery', + 'underscore', + 'backbone', + 'iznik/base', + 'moment', + 'googlemaps-js-rich-marker', + 'iznik/views/pages/pages', + 'iznik/views/pages/user/pages', + 'iznik/views/pages/user/group', + 'iznik/views/user/message', + 'iznik/views/group/communityevents', + 'iznik/views/group/volunteering', + 'iznik/models/group', + 'geocomplete' +], function ($, _, Backbone, Iznik, moment, r) { + var RichMarker = r.default.RichMarker + + Iznik.Views.User.Pages.Explore = Iznik.Views.Page.extend({ + template: 'user_explore_main', + + title: 'Explore', + + events: { + 'click .js-getloc': 'getLocation', + 'click .js-locbtn': 'locButton', + 'keyup .js-location': 'keyUp' + }, + + keyUp: function (e) { + if (e.which === 13) { + // Just suppress - we want them to choose from the autocomplete. + e.preventDefault() + e.stopPropagation() + e.stopImmediatePropagation() + } + }, + + locButton: function () { + this.$('.js-location').trigger('geocode') + }, + + getLocation: function () { + navigator.geolocation.getCurrentPosition(_.bind(this.gotLocation, this)) + }, + + gotLocation: function (position) { + this.map.moveTo(position.coords.latitude, position.coords.longitude) + }, + + render: function () { + var self = this + + self.title = self.options.region ? ('Groups in ' + self.options.region) : 'Explore' + + var p = Iznik.Views.Page.prototype.render.call(this).then(function () { + if (!navigator.geolocation) { + self.$('.js-geoloconly').hide() + } - keyUp: function(e) { - if (e.which === 13) { - // Just suppress - we want them to choose from the autocomplete. - e.preventDefault(); - e.stopPropagation(); - e.stopImmediatePropagation(); - } - }, + self.$('.js-location').geocomplete({ + types: ['(cities)'], + componentRestrictions: { + country: ['uk'] + } + }).bind('geocode:result', function (event, result) { + self.map.moveTo(result.geometry.location.lat(), result.geometry.location.lng()) + }) + + // Get all the groups. There aren't too many, and this means we are responsive when panning or zooming. + self.collection = new Iznik.Collections.Group() + self.collection.fetch({ + data: { + grouptype: 'Freegle', + } + }).then(function () { + if (!self.options.region) { + self.$('.js-exploreall').show() + self.$('.js-exploreregion').remove() + self.$('.js-findholder').show() + + // Just centre on one of the centres of Britain. Yes, there are multiple. + self.map = new Iznik.Views.Map({ + model: new Iznik.Model({ + clat: 53.9450, + clng: -2.5209, + zoom: 5, + target: self.$('.js-maparea') + }), + collection: self.collection, + summary: true, + bounds: null, + textlist: self.$('.js-grouptextlist') + }) - locButton: function() { - this.$('.js-location').trigger('geocode'); - }, + // Add links for the different regions. + self.regions = new Iznik.Collection() + self.regions.comparator = 'id' + self.collection.each(function (group) { + var region = group.get('region') + + if (region && region.length > 0) { + self.regions.add(new Iznik.Model({ + id: region + })) + } + }) - getLocation: function() { - navigator.geolocation.getCurrentPosition(_.bind(this.gotLocation, this)); - }, + self.regionCollectionView = new Backbone.CollectionView({ + el: self.$('.js-regions'), + modelView: Iznik.Views.User.Pages.Explore.Region, + collection: self.regions, + processKeyEvents: false + }) - gotLocation: function(position) { - this.map.moveTo(position.coords.latitude, position.coords.longitude); - }, + self.regionCollectionView.render() + self.$('.js-regionholder').fadeIn('slow') + } else { + // We have a specific region to show. First find the relevant groups. + self.$('.js-region').html(self.options.region) + self.$('.js-exploreall').remove() + self.$('.js-exploreregion').show() + var newgroups = self.collection.where({ + region: self.options.region + }) + self.collection = new Iznik.Collection(newgroups) + + // Find a centre and bounding box + var swlat = 0, swlng = 0, nelat = 0, nelng = 0, clat = 0, clng = 0 + var bounds = new google.maps.LatLngBounds() + self.collection.each(function (group) { + var lat = group.get('lat') + var lng = group.get('lng') + swlat = lat < swlat ? lat : swlat + swlng = lng < swlng ? lng : swlng + nelat = lat > nelat ? lat : nelat + nelng = lng > nelng ? lng : nelng + bounds = bounds.extend(new google.maps.LatLng(lat, lng)) + }) - render: function () { - var self = this; + var clat = (swlat + nelat) / 2 + var clng = (swlng + nelng) / 2 + + self.map = new Iznik.Views.Map({ + model: new Iznik.Model({ + clat: clat, + clng: clng, + target: self.$('.js-maparea') + }), + collection: self.collection, + summary: false, + bounds: bounds, + textlist: self.$('.js-grouptextlist') + }) + } - self.title = self.options.region ? ("Groups in " + self.options.region) : 'Explore'; + self.map.render().then(function () { + if (self.options.search) { + // We've been asked to search for a place. + self.$('.js-location').val(self.options.search) + self.locButton() + } - var p = Iznik.Views.Page.prototype.render.call(this).then(function () { - if (!navigator.geolocation) { - self.$('.js-geoloconly').hide(); - } + self.$('.js-nogroup').fadeIn('slow') + }) + }) + }) - self.$('.js-location').geocomplete({ - types: ['(cities)'], - componentRestrictions: { - country: ['uk'] - } - }).bind("geocode:result", function (event, result) { - self.map.moveTo(result.geometry.location.lat(), result.geometry.location.lng()); - }); - - // Get all the groups. There aren't too many, and this means we are responsive when panning or zooming. - self.collection = new Iznik.Collections.Group(); - self.collection.fetch({ - data: { - grouptype: 'Freegle', - } - }).then(function() { - if (!self.options.region) { - self.$('.js-exploreall').show(); - self.$('.js-exploreregion').remove(); - self.$('.js-findholder').show(); - - // Just centre on one of the centres of Britain. Yes, there are multiple. - self.map = new Iznik.Views.Map({ - model: new Iznik.Model({ - clat: 53.9450, - clng: -2.5209, - zoom: 5, - target: self.$('.js-maparea') - }), - collection: self.collection, - summary: true, - bounds: null, - textlist: self.$('.js-grouptextlist') - }); - - // Add links for the different regions. - self.regions = new Iznik.Collection(); - self.regions.comparator = 'id'; - self.collection.each(function(group) { - var region = group.get('region'); - - if (region && region.length > 0) { - self.regions.add(new Iznik.Model({ - id: region - })); - } - }); - - self.regionCollectionView = new Backbone.CollectionView({ - el: self.$('.js-regions'), - modelView: Iznik.Views.User.Pages.Explore.Region, - collection: self.regions, - processKeyEvents: false - }); - - self.regionCollectionView.render(); - self.$('.js-regionholder').fadeIn('slow'); - } else { - // We have a specific region to show. First find the relevant groups. - self.$('.js-region').html(self.options.region); - self.$('.js-exploreall').remove(); - self.$('.js-exploreregion').show(); - var newgroups = self.collection.where({ - region: self.options.region - }); - self.collection = new Iznik.Collection(newgroups); - - // Find a centre and bounding box - var swlat = 0, swlng = 0, nelat = 0, nelng = 0, clat = 0, clng = 0; - var bounds = new google.maps.LatLngBounds(); - self.collection.each(function(group) { - var lat = group.get('lat'); - var lng = group.get('lng'); - swlat = lat < swlat ? lat : swlat; - swlng = lng < swlng ? lng : swlng; - nelat = lat > nelat ? lat : nelat; - nelng = lng > nelng ? lng : nelng; - bounds = bounds.extend(new google.maps.LatLng(lat, lng)); - }); - - var clat = (swlat + nelat) / 2; - var clng = (swlng + nelng) / 2; - - self.map = new Iznik.Views.Map({ - model: new Iznik.Model({ - clat: clat, - clng: clng, - target: self.$('.js-maparea') - }), - collection: self.collection, - summary: false, - bounds: bounds, - textlist: self.$('.js-grouptextlist') - }); - } - - self.map.render().then(function() { - if (self.options.search) { - // We've been asked to search for a place. - self.$('.js-location').val(self.options.search); - self.locButton(); - } - - self.$('.js-nogroup').fadeIn('slow'); - }); - }); - }); - - return (p); - } - }); + return (p) + } + }) - Iznik.Views.User.Pages.Explore.Region = Iznik.View.extend({ - template: 'user_explore_region', - tagName: 'li' - }); + Iznik.Views.User.Pages.Explore.Region = Iznik.View.extend({ + template: 'user_explore_region', + tagName: 'li' + }) - Iznik.Views.Map = Iznik.View.extend({ - infoWindow: null, + Iznik.Views.Map = Iznik.View.extend({ + infoWindow: null, - fetched: false, - lastBounds: null, + fetched: false, + lastBounds: null, - addMarker: function(marker) { - var self = this; + addMarker: function (marker) { + var self = this - marker.setMap(this.map); - this.markers.push(marker); - }, + marker.setMap(this.map) + this.markers.push(marker) + }, - fitted: false, + fitted: false, - updateMap: function() { - var self = this; + updateMap: function () { + var self = this - if (self.options.bounds && !self.fitted) { - self.fitted = true; - self.map.fitBounds(self.options.bounds); - } + if (self.options.bounds && !self.fitted) { + self.fitted = true + self.map.fitBounds(self.options.bounds) + } - // Get new markers for current map bounds. - self.bounds = self.map.getBounds(); - var boundsstr = self.bounds.toString(); + // Get new markers for current map bounds. + self.bounds = self.map.getBounds() + var boundsstr = self.bounds.toString() - if (!self.lastBounds || boundsstr != self.lastBounds) { - // Remove old markers - if (this.markers) { - _.each(this.markers, function(marker) { - marker.setMap(null); - }); + if (!self.lastBounds || boundsstr != self.lastBounds) { + // Remove old markers + if (this.markers) { + _.each(this.markers, function (marker) { + marker.setMap(null) + }) - this.markers = []; - } + this.markers = [] + } - var views = _.union(this.groupViews, this.groupTextViews); - if (views) { - _.each(views, function(view) { - view.destroyIt(); - }); + var views = _.union(this.groupViews, this.groupTextViews) + if (views) { + _.each(views, function (view) { + view.destroyIt() + }) - this.groupViews = []; - this.groupTextViews = []; - } + this.groupViews = [] + this.groupTextViews = [] + } - self.options.textlist.empty(); + self.options.textlist.empty() - if (self.options.summary) { - $('.js-numgroups').html(self.collection.length); - $('.js-groupsumm').css('visibility', 'visible'); - $('.js-groupsumm').fadeIn('slow'); - } else { - $('.js-groupsumm').hide(); - } + if (self.options.summary) { + $('.js-numgroups').html(self.collection.length) + $('.js-groupsumm').css('visibility', 'visible') + $('.js-groupsumm').fadeIn('slow') + } else { + $('.js-groupsumm').hide() + } - self.lastBounds = boundsstr; - - var within = 0; - - self.collection.each(function(group) { - if (self.bounds.contains(new google.maps.LatLng(group.get('lat'), group.get('lng'))) && - group.get('onmap')) { - within++ - } - }); - - var groupsshown = 0; - - self.collection.each(function(group) { - if (self.bounds.contains(new google.maps.LatLng(group.get('lat'), group.get('lng'))) && - group.get('onmap') && group.get('publish')) { - groupsshown++; - var latLng = new google.maps.LatLng(group.get('lat'), group.get('lng')); - - if (within > 20) { - // Switch to pins for large collections - var icon = '/images/mapmarker.gif?a=1'; - var marker = new google.maps.Marker({ - position: latLng, - icon: icon, - title: group.get('namedisplay') - }); - - google.maps.event.addListener(marker, 'click', function() { - self.map.setZoom(12); - self.map.setCenter(marker.getPosition()); - self.updateMap(); - }); - } else { - var marker = new RichMarker({ - position: latLng - }); - - var content = new Iznik.Views.Map.Group({ - model: group, - map: self.map, - marker: marker - }); - - self.groupViews.push(content); - - self.listenTo(content, 'opened', function(opened) { - self.groupViews.forEach(function(element, index, array) { - if (element != opened) { - element.close(); - } - }); - }); - - // Show the name as a tooltip below, which is always shown. This helps when we - // don't have a group logo. - content.render().then(function() { - content.$el.tooltip({ - 'trigger': 'manual', - 'placement': 'bottom', - 'title': content.model.get('namedisplay') - }); - - marker.setContent(content.el); - content.$el.tooltip('show'); - }); - - marker.setFlat(true); - } - - marker.setDraggable(false); - self.addMarker(marker); - - // We might no longer be looking at the same area. - // console.log("Consider still present", this.get('id'), this.get('nameshort'), self.bounds.contains(new google.maps.LatLng(this.get('lat'), this.get('lng')))) - if (self.bounds.contains(new google.maps.LatLng(group.get('lat'), group.get('lng')))) { - var v = new Iznik.Views.Map.GroupText({ - model: group - }); - v.render().then(function() { - if (self.options.textlist.find('.js-grouptext-' + self.model.get('id')).length === 0) { - self.options.textlist.append(v.$el); - } - }); - } - } - }); - - if (groupsshown == 0 && self.map.getZoom() > 0) { - // We aren't showing any groups. Zoom out until we are. - self.map.setZoom(self.map.getZoom() - 1); + self.lastBounds = boundsstr + + var within = 0 + + self.collection.each(function (group) { + if (self.bounds.contains(new google.maps.LatLng(group.get('lat'), group.get('lng'))) && + group.get('onmap')) { + within++ + } + }) + + var groupsshown = 0 + + self.collection.each(function (group) { + if (self.bounds.contains(new google.maps.LatLng(group.get('lat'), group.get('lng'))) && + group.get('onmap') && group.get('publish')) { + groupsshown++ + var latLng = new google.maps.LatLng(group.get('lat'), group.get('lng')) + + if (within > 20) { + // Switch to pins for large collections + var icon = '/images/mapmarker.gif?a=1' + var marker = new google.maps.Marker({ + position: latLng, + icon: icon, + title: group.get('namedisplay') + }) + + google.maps.event.addListener(marker, 'click', function () { + self.map.setZoom(12) + self.map.setCenter(marker.getPosition()) + self.updateMap() + }) + } else { + var marker = new RichMarker({ + position: latLng + }) + + var content = new Iznik.Views.Map.Group({ + model: group, + map: self.map, + marker: marker + }) + + self.groupViews.push(content) + + self.listenTo(content, 'opened', function (opened) { + self.groupViews.forEach(function (element, index, array) { + if (element != opened) { + element.close() + } + }) + }) + + // Show the name as a tooltip below, which is always shown. This helps when we + // don't have a group logo. + content.render().then(function () { + content.$el.tooltip({ + 'trigger': 'manual', + 'placement': 'bottom', + 'title': content.model.get('namedisplay') + }) + + marker.setContent(content.el) + content.$el.tooltip('show') + }) + + marker.setFlat(true) + } + + marker.setDraggable(false) + self.addMarker(marker) + + // We might no longer be looking at the same area. + // console.log("Consider still present", this.get('id'), this.get('nameshort'), self.bounds.contains(new google.maps.LatLng(this.get('lat'), this.get('lng')))) + if (self.bounds.contains(new google.maps.LatLng(group.get('lat'), group.get('lng')))) { + var v = new Iznik.Views.Map.GroupText({ + model: group + }) + v.render().then(function () { + if (self.options.textlist.find('.js-grouptext-' + self.model.get('id')).length === 0) { + self.options.textlist.append(v.$el) } + }) } - }, + } + }) - resize: function(e) { - var mapWidth = $(e.target).outerWidth(); - $(e.target).css('height', mapWidth + 'px'); - google.maps.event.trigger(this.map, "resize"); + if (groupsshown == 0 && self.map.getZoom() > 0) { + // We aren't showing any groups. Zoom out until we are. + self.map.setZoom(self.map.getZoom() - 1) + } + } + }, + + resize: function (e) { + var mapWidth = $(e.target).outerWidth() + $(e.target).css('height', mapWidth + 'px') + google.maps.event.trigger(this.map, 'resize') + }, + + moveTo: function (lat, lng) { + this.map.setCenter(new google.maps.LatLng(lat, lng)) + this.map.setZoom(11) + }, + + render: function () { + var self = this + + self.markers = [] + self.groupViews = [] + self.groupTextViews = [] + + // Note target might be outside this view. + var target = $(self.model.get('target')) + var mapWidth = target.outerWidth() + target.css('height', mapWidth + 'px') + + // Set explicit dimensions otherwise map collapses. + target.css('width', target.width()) + var height = Math.floor($(window).innerHeight() / 2) + height = height < 200 ? 200 : height + target.css('height', height) + + // Create map centred on the specified place. + var mapOptions = { + mapTypeControl: false, + streetViewControl: false, + center: new google.maps.LatLng(this.model.get('clat'), this.model.get('clng')), + panControl: mapWidth > 400, + zoomControl: mapWidth > 400, + zoom: self.model.get('zoom'), + gestureHandling: 'greedy' + } + + self.map = new google.maps.Map(target.get()[0], mapOptions) + + google.maps.event.addDomListener(window, 'resize', _.bind(self.resize, self)) + google.maps.event.addDomListener(window, 'load', _.bind(self.resize, self)) + + // Render the map + google.maps.event.addDomListener(self.map, 'idle', function () { + self.updateMap() + }) + + return (Iznik.resolvedPromise(self)) + } + }) + + Iznik.Views.Map.Group = Iznik.View.extend({ + template: 'user_explore_group', + + events: { + 'click': 'showDetails' + }, + + showDetails: function () { + if (this.model.get('external')) { + // External group - open new tab. + window.open(this.model.get('external')) + } else if (this.model.get('onyahoo') && !this.model.get('onhere')) { + // Yahoo group - open new tab. + window.open('https://groups.yahoo.com/neo/groups/' + this.model.get('nameshort')) + } else { + Router.navigate('/explore/' + this.model.get('nameshort'), true) + } + } + }) + + Iznik.Views.Map.GroupText = Iznik.View.extend({ + template: 'user_explore_grouptext', + + render: function () { + var self = this + + var p = Iznik.View.prototype.render.call(this) + + p.then(function () { + self.$el.attr('id', 'js-grouptext-' + self.model.get('id')) + }) + + return (p) + } + }) + + Iznik.Views.User.Pages.ExploreGroup = Iznik.Views.User.Pages.Group.extend({ + template: 'user_explore_single', + + events: { + 'click .js-join': 'join', + 'click .js-leave': 'leave', + 'click .js-locgive': 'locGive', + 'click .js-locfind': 'locFind' + }, + + join: function () { + Router.navigate('/explore/' + this.model.get('nameshort') + '/join', true) + }, + + leave: function () { + var self = this + + $.ajax({ + url: API + 'memberships', + type: 'POST', + headers: { + 'X-HTTP-Method-Override': 'DELETE' }, - - moveTo: function(lat, lng) { - this.map.setCenter(new google.maps.LatLng(lat, lng)); - this.map.setZoom(11); + data: { + groupid: self.model.get('id'), + userid: Iznik.Session.get('me').id }, + success: function (ret) { + if (ret.ret === 0) { + // Now force a refresh of the session. + self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { + self.model.set('role', 'Non-member') + Router.navigate('/explore/' + self.model.get('nameshort'), true) + }) - render: function(){ - var self = this; - - self.markers = []; - self.groupViews = []; - self.groupTextViews = []; - - // Note target might be outside this view. - var target = $(self.model.get('target')); - var mapWidth = target.outerWidth(); - target.css('height', mapWidth + 'px'); - - // Set explicit dimensions otherwise map collapses. - target.css('width', target.width()); - var height = Math.floor($(window).innerHeight() / 2); - height = height < 200 ? 200 : height; - target.css('height', height); - - // Create map centred on the specified place. - var mapOptions = { - mapTypeControl : false, - streetViewControl : false, - center : new google.maps.LatLng(this.model.get('clat'), this.model.get('clng')), - panControl : mapWidth > 400, - zoomControl : mapWidth > 400, - zoom : self.model.get('zoom'), - gestureHandling : 'greedy' - }; - - self.map = new google.maps.Map(target.get()[0], mapOptions); - - google.maps.event.addDomListener(window, 'resize', _.bind(self.resize, self)); - google.maps.event.addDomListener(window, 'load', _.bind(self.resize, self)); - - // Render the map - google.maps.event.addDomListener(self.map, 'idle', function() { - self.updateMap(); - }); - - return(Iznik.resolvedPromise(self)); + Iznik.Session.testLoggedIn([ + 'me', + 'groups' + ]) + } } - }); - - Iznik.Views.Map.Group = Iznik.View.extend({ - template: 'user_explore_group', - - events: { - 'click' : 'showDetails' - }, - - showDetails: function() { - if (this.model.get('external')) { - // External group - open new tab. - window.open(this.model.get('external')); - } else if (this.model.get('onyahoo') && !this.model.get('onhere')) { - // Yahoo group - open new tab. - window.open("https://groups.yahoo.com/neo/groups/" + this.model.get('nameshort')); + }) + }, + + locGive: function () { + // This group has a default location. Set it as our location and skip the "where am I" page. + var self = this + Storage.set('myhomegroup', self.model.get('id')) + Storage.set('myhomegrouptime', (new Date()).getTime()) + Storage.set('mylocation', JSON.stringify(self.model.get('defaultlocation'))) + Router.navigate('/give/whatisit', true) + }, + + locFind: function () { + // This group has a default location. Set it as our location and skip straight to post a wanted + var self = this + Storage.set('myhomegroup', self.model.get('id')) + Storage.set('myhomegrouptime', (new Date()).getTime()) + Storage.set('mylocation', JSON.stringify(self.model.get('defaultlocation'))) + Router.navigate('/find/whatisit', true) + }, + + filter: function (model) { + var thetype = model.get('type') + + if (thetype != 'Offer' && thetype != 'Wanted') { + // Not interested in this type of message. + return (false) + } else { + return (model.get('outcomes').length == 0) + } + }, + + showHideJoin: function () { + var self = this + + var id = self.model.get('id') + self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { + if (loggedIn) { + var group = Iznik.Session.getGroup(id) + + if (!_.isUndefined(group)) { + // See if we're pending. + console.log('Got group', group) + if (group.get('collection') == 'Pending') { + self.$('.js-pending').show() + self.$('.js-join').hide() + self.$('.js-leave').hide() } else { - Router.navigate('/explore/' + this.model.get('nameshort'), true); + self.$('.js-pending').hide() + self.$('.js-join').hide() + self.$('.js-leave').show() } + } else { + self.$('.js-pending').hide() + self.$('.js-join').show() + self.$('.js-leave').hide() + } + } else { + self.$('.js-join').show() + self.$('.js-leave').hide() } - }); - - Iznik.Views.Map.GroupText = Iznik.View.extend({ - template: 'user_explore_grouptext', - - render: function () { - var self = this; - - var p = Iznik.View.prototype.render.call(this); - - p.then(function() { - self.$el.attr('id', 'js-grouptext-' + self.model.get('id')) + }) + + Iznik.Session.testLoggedIn([ + 'me', + 'groups' + ]) + }, + + areaMap: function () { + var self = this + + require(['wicket-gmap3', 'wicket'], function (gm, Wkt) { + // Need to get the polygon, which isn't there by default. + self.group = new Iznik.Models.Group({ + id: self.model.get('id') + }) + + self.group.fetch({ + data: { + polygon: true + } + }).then(function () { + var wkt = new Wkt.Wkt() + var wktstr = self.group.get('polygon') + + try { // Catch any malformed WKT strings + wkt.read(wktstr) + } catch (e1) { + try { + self.Wkt.read(wktstr.replace('\n', '').replace('\r', '').replace('\t', '')) + } catch (e2) { + if (e2.name === 'WKTError') { + console.error('Ignore invalid WKT', wktstr) + return + } + } + } + + // Try to make the map not increase beyond the height of the description. + self.$('#js-areamap').css('height', self.$('.js-nameetc').height()) + + var options = { + center: new google.maps.LatLng(self.model.get('lat'), self.model.get('lng')), + zoom: 14, + disableDefaultUI: true, + mapTypeControl: false, + mapTypeId: google.maps.MapTypeId.ROADMAP, + panControl: false, + streetViewControl: false, + zoomControl: false + } + + self.areamap = new google.maps.Map(self.$('#js-areamap').get(0), options) + + var obj = null + + // No getBounds on polygon by default. + google.maps.Polygon.prototype.getBounds = function () { + var bounds = new google.maps.LatLngBounds() + var paths = this.getPaths() + var path + for (var i = 0; i < paths.getLength(); i++) { + path = paths.getAt(i) + for (var ii = 0; ii < path.getLength(); ii++) { + bounds.extend(path.getAt(ii)) + } + } + return bounds + } + + try { + obj = wkt.toObject(self.areamap.defaults) // Make an object + obj.setMap(self.areamap) + obj.setOptions({ + fillColor: 'blue', + strokeWeight: 0, + opacity: 0.5 }) - return(p); - } - }); + // Zoom the map to show the whole area. + var bounds = obj.getBounds() - Iznik.Views.User.Pages.ExploreGroup = Iznik.Views.User.Pages.Group.extend({ - template: 'user_explore_single', + var mapDim = { + height: self.$('#js-areamap').height(), + width: self.$('#js-areamap').width() + } - events: { - 'click .js-join': 'join', - 'click .js-leave': 'leave', - 'click .js-locgive': 'locGive', - 'click .js-locfind': 'locFind' - }, + var zoom = Iznik.getBoundsZoomLevel(bounds, mapDim) + self.areamap.setZoom(zoom) + } catch (e) { + console.log('WKT error', e.message, wktstr, obj) + } + }) + }) + }, + + render: function () { + var self = this + + // Create the model. If the id is a legacy group id then it will be corrected in the model we fetch, + // so we shouldn't use the options.id after this. + self.model = new Iznik.Models.Group({id: self.options.id}) + var p = self.model.fetch({ + data: { + polygon: true + } + }) - join: function() { - Router.navigate('/explore/' + this.model.get('nameshort') + '/join', true); - }, + p.then(function () { + self.title = self.model.get('namedisplay') - leave: function() { - var self = this; + // We want the raw polygon data for structured data. + var poly = self.model.get('polygon') + if (poly) { + self.model.set('rawpolygon', poly.replace('POLYGON((', '').replace('))', '')) + } - $.ajax({ + Iznik.Views.User.Pages.Group.prototype.render.call(self).then(function () { + self.adSense() + + self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { + if (loggedIn) { + // Left menu is community events + var v = new Iznik.Views.User.CommunityEventsSidebar() + v.render().then(function () { + $('#js-eventcontainer').append(v.$el) + }) + + // Right menu is volunteer vacancies + var w = new Iznik.Views.User.VolunteeringSidebar() + w.render().then(function () { + $('#js-volunteeringcontainer').append(w.$el) + }) + } + }) + + Iznik.Session.testLoggedIn([ + 'me', + 'groups' + ]) + + self.$('.js-membercount').html(self.model.get('membercount').toLocaleString()) + + var founded = self.model.get('founded') + if (founded) { + var m = new moment(founded) + self.$('.js-foundeddate').html(m.format('Do MMMM, YYYY')) + self.$('.js-founded').show() + } + + // Add the description. We use a default because that helps with SEO. + var desc = self.model.get('description') + desc = desc ? desc : 'Give and get stuff for free with ' + self.model.get('namedisplay') + '. Offer things you don\'t need, and ask for things you\'d like. Don\'t just recycle - reuse with Freegle!' + self.$('.js-description').html(desc) + + // Any links in here are real. + self.$('.js-description a').attr('data-realurl', true) + + // Add the area map. + // TODO MAPS + // self.areaMap(); + + self.collection = new Iznik.Collections.Message(null, { + modtools: false, + collection: 'Approved', + groupid: self.model.get('id') + }) + + self.collectionView = new Backbone.CollectionView({ + el: self.$('.js-list'), + modelView: Iznik.Views.User.Message.Replyable, + modelViewOptions: { + collection: self.collection, + page: self + }, + collection: self.collection, + visibleModelsFilter: _.bind(self.filter, self), + processKeyEvents: false + }) + + self.collectionView.render() + + // Add a type selector. The parent class has an event and method to re-render if we change that. + self.$('.js-type').selectpicker() + self.$('.js-type').selectPersist() + + // Get some messages + self.refetch() + + if (self.options.join) { + var group = Iznik.Session.get(self.model.get('id')) + + if (group) { + // Already a member, possibly pending. + self.showHideJoin() + } else { + $.ajax({ url: API + 'memberships', type: 'POST', headers: { - 'X-HTTP-Method-Override': 'DELETE' + 'X-HTTP-Method-Override': 'PUT' }, data: { - groupid: self.model.get('id'), - userid: Iznik.Session.get('me').id - }, - success: function(ret) { - if (ret.ret === 0) { - // Now force a refresh of the session. - self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { - self.model.set('role', 'Non-member'); - Router.navigate('/explore/' + self.model.get('nameshort'), true); - }); - - Iznik.Session.testLoggedIn([ - 'me', - 'groups' - ]); - } + groupid: self.model.get('id') + }, complete: function () { + // Update the page with the result - we might have joined, in which case + // we can now show messages and leave button, or we might be pending. + self.showHideJoin() + self.refetch() } - }) ; - }, - - locGive: function() { - // This group has a default location. Set it as our location and skip the "where am I" page. - var self = this; - Storage.set('myhomegroup', self.model.get('id')); - Storage.set('myhomegrouptime', (new Date()).getTime()); - Storage.set('mylocation', JSON.stringify(self.model.get('defaultlocation'))) - Router.navigate('/give/whatisit', true); - }, - - locFind: function() { - // This group has a default location. Set it as our location and skip straight to post a wanted - var self = this; - Storage.set('myhomegroup', self.model.get('id')); - Storage.set('myhomegrouptime', (new Date()).getTime()); - Storage.set('mylocation', JSON.stringify(self.model.get('defaultlocation'))) - Router.navigate('/find/whatisit', true); - }, - - filter: function(model) { - var thetype = model.get('type'); - - if (thetype != 'Offer' && thetype != 'Wanted') { - // Not interested in this type of message. - return(false); - } else { - return (model.get('outcomes').length == 0); + }) } - }, - - showHideJoin: function() { - var self = this; - - var id = self.model.get('id'); - self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { - if (loggedIn) { - var group = Iznik.Session.getGroup(id); - - if (!_.isUndefined(group)) { - // See if we're pending. - console.log("Got group", group); - if (group.get('collection') == 'Pending') { - self.$('.js-pending').show(); - self.$('.js-join').hide(); - self.$('.js-leave').hide(); - } else { - self.$('.js-pending').hide(); - self.$('.js-join').hide(); - self.$('.js-leave').show(); - } - } else { - self.$('.js-pending').hide(); - self.$('.js-join').show(); - self.$('.js-leave').hide(); - } - } else { - self.$('.js-join').show(); - self.$('.js-leave').hide(); - } - }); + } else { + self.showHideJoin() + } + }) + }) + + return (p) + } + }) + + Iznik.Views.User.Pages.LegacyMessage = Iznik.Views.Page.extend({ + template: 'user_explore_message', + + render: function () { + var self = this + var p = Iznik.Views.Page.prototype.render.call(self).then(function () { + self.model = new Iznik.Models.Message({ + id: 'L' + self.options.id + }) + self.model.fetch({ + data: { + groupid: self.options.groupid + }, + processData: true + }).then(function () { + // We might fail to fetch, or fetch a deleted message, or fetch a paired message. In all these + // cases the message shouldn't show. + if (self.model.get('subject') && !self.model.get('deleted')) { + var v = new Iznik.Views.User.Message.Replyable({ + model: self.model + }) - Iznik.Session.testLoggedIn([ - 'me', - 'groups' - ]); - }, + v.expanded = true - areaMap: function() { - var self = this; - - require(['wicket-gmap3', 'wicket'], function(gm, Wkt) { - // Need to get the polygon, which isn't there by default. - self.group = new Iznik.Models.Group({ - id: self.model.get('id') - }); - - self.group.fetch({ - data: { - polygon: true - } - }).then(function() { - var wkt = new Wkt.Wkt(); - var wktstr = self.group.get('polygon'); - - try { // Catch any malformed WKT strings - wkt.read(wktstr); - } catch (e1) { - try { - self.Wkt.read(wktstr.replace('\n', '').replace('\r', '').replace('\t', '')); - } catch (e2) { - if (e2.name === 'WKTError') { - console.error("Ignore invalid WKT", wktstr); - return; - } - } - } - - // Try to make the map not increase beyond the height of the description. - self.$('#js-areamap').css('height', self.$('.js-nameetc').height()); - - var options = { - center: new google.maps.LatLng(self.model.get('lat'), self.model.get('lng')), - zoom: 14, - disableDefaultUI: true, - mapTypeControl: false, - mapTypeId: google.maps.MapTypeId.ROADMAP, - panControl: false, - streetViewControl: false, - zoomControl: false - }; - - self.areamap = new google.maps.Map(self.$('#js-areamap').get(0), options); - - var obj = null; - - // No getBounds on polygon by default. - google.maps.Polygon.prototype.getBounds = function() { - var bounds = new google.maps.LatLngBounds(); - var paths = this.getPaths(); - var path; - for (var i = 0; i < paths.getLength(); i++) { - path = paths.getAt(i); - for (var ii = 0; ii < path.getLength(); ii++) { - bounds.extend(path.getAt(ii)); - } - } - return bounds; - } - - try { - obj = wkt.toObject(self.areamap.defaults); // Make an object - obj.setMap(self.areamap); - obj.setOptions({ - fillColor: 'blue', - strokeWeight: 0, - opacity: 0.5 - }); - - // Zoom the map to show the whole area. - var bounds = obj.getBounds(); - - var mapDim = { - height: self.$('#js-areamap').height(), - width: self.$('#js-areamap').width() - }; - - var zoom = Iznik.getBoundsZoomLevel(bounds, mapDim); - self.areamap.setZoom(zoom); - } catch (e) { - console.log("WKT error", e.message, wktstr, obj); - } - }); - }); + v.render().then(function () { + self.$('.js-message').append(v.el) + v.expand() + }) + } else { + self.$('.js-gone').fadeIn('slow') + } + }) + }) + + return (p) + } + }) + + Iznik.Views.User.Pages.Message = Iznik.Views.Page.extend({ + template: 'user_explore_message', + + events: { + 'click .js-join': 'join' + }, + + join: function () { + var self = this + $.ajax({ + url: API + 'memberships', + type: 'POST', + headers: { + 'X-HTTP-Method-Override': 'PUT' }, - - render: function () { - var self = this; - - // Create the model. If the id is a legacy group id then it will be corrected in the model we fetch, - // so we shouldn't use the options.id after this. - self.model = new Iznik.Models.Group({ id: self.options.id }); - var p = self.model.fetch({ - data: { - polygon: true - } - }); - - p.then(function() { - self.title = self.model.get('namedisplay'); - - // We want the raw polygon data for structured data. - var poly = self.model.get('polygon'); - if (poly) { - self.model.set('rawpolygon', poly.replace('POLYGON((', '').replace('))', '')); - } - - Iznik.Views.User.Pages.Group.prototype.render.call(self).then(function () { - self.adSense(); - - self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { - // Left menu is community events - var v = new Iznik.Views.User.CommunityEventsSidebar(); - v.render().then(function () { - $('#js-eventcontainer').append(v.$el); - }); - - // Right menu is volunteer vacancies - var w = new Iznik.Views.User.VolunteeringSidebar(); - w.render().then(function () { - $('#js-volunteeringcontainer').append(w.$el); - }); - }); - - Iznik.Session.testLoggedIn([ - 'me', - 'groups' - ]); - - self.$('.js-membercount').html(self.model.get('membercount').toLocaleString()); - - var founded = self.model.get('founded'); - if (founded) { - var m = new moment(founded); - self.$('.js-foundeddate').html(m.format('Do MMMM, YYYY')); - self.$('.js-founded').show(); - } - - // Add the description. We use a default because that helps with SEO. - var desc = self.model.get('description'); - desc = desc ? desc : "Give and get stuff for free with " + self.model.get('namedisplay') + ". Offer things you don't need, and ask for things you'd like. Don't just recycle - reuse with Freegle!"; - self.$('.js-description').html(desc); - - // Any links in here are real. - self.$('.js-description a').attr('data-realurl', true); - - // Add the area map. - // TODO MAPS - // self.areaMap(); - - self.collection = new Iznik.Collections.Message(null, { - modtools: false, - collection: 'Approved', - groupid: self.model.get('id') - }); - - self.collectionView = new Backbone.CollectionView({ - el: self.$('.js-list'), - modelView: Iznik.Views.User.Message.Replyable, - modelViewOptions: { - collection: self.collection, - page: self - }, - collection: self.collection, - visibleModelsFilter: _.bind(self.filter, self), - processKeyEvents: false - }); - - self.collectionView.render(); - - // Add a type selector. The parent class has an event and method to re-render if we change that. - self.$('.js-type').selectpicker(); - self.$('.js-type').selectPersist(); - - // Get some messages - self.refetch(); - - if (self.options.join) { - var group = Iznik.Session.get(self.model.get('id')); - - if (group) { - // Already a member, possibly pending. - self.showHideJoin(); - } else { - $.ajax({ - url: API + 'memberships', - type: 'POST', - headers: { - 'X-HTTP-Method-Override': 'PUT' - }, - data: { - groupid: self.model.get('id') - }, complete: function () { - // Update the page with the result - we might have joined, in which case - // we can now show messages and leave button, or we might be pending. - self.showHideJoin(); - self.refetch(); - } - }); - } - } else { - self.showHideJoin(); - } - }); - }); - - return (p); + data: { + groupid: self.groupid + }, complete: function () { + // Refresh our group list and re-render. + self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { + self.render() + }) + + Iznik.Session.testLoggedIn([ + 'me', + 'groups' + ]) } - }); - - Iznik.Views.User.Pages.LegacyMessage = Iznik.Views.Page.extend({ - template: 'user_explore_message', - - render: function () { - var self = this; - var p = Iznik.Views.Page.prototype.render.call(self).then(function () { - self.model = new Iznik.Models.Message({ - id: 'L' + self.options.id - }); - self.model.fetch({ - data: { - groupid: self.options.groupid - }, - processData: true - }).then(function () { - // We might fail to fetch, or fetch a deleted message, or fetch a paired message. In all these - // cases the message shouldn't show. - if (self.model.get('subject') && !self.model.get('deleted')) { - var v = new Iznik.Views.User.Message.Replyable({ - model: self.model - }); - - v.expanded = true; - - v.render().then(function () { - self.$('.js-message').append(v.el); - v.expand(); - }); - } else { - self.$('.js-gone').fadeIn('slow'); - } - }); - }); - - return (p); - } - }); - - Iznik.Views.User.Pages.Message = Iznik.Views.Page.extend({ - template: 'user_explore_message', + }) + }, + + render: function () { + var self = this + var p = Iznik.Views.Page.prototype.render.call(self).then(function () { + self.adSense() + + self.model = new Iznik.Models.Message({ + id: self.options.id + }) + self.model.fetch().then(function () { + var ret = self.model.get('ret') + + if (ret == 2) { + // Permission denied. This means it is a message which we are not allowed to see. + // + // Most commonly this is because we're logged out, and need to log in before + // we're allowed to see it. + self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { + if (!loggedIn) { + // We are logged out, and the contents are not visible to non-group members. + self.listenToOnce(Iznik.Session, 'loggedIn', function () { + self.render() + }) + + Iznik.Session.forceLogin() + } else { + // Still can't see it logged in + var groups = self.model.get('groups') + _.each(groups, function (group) { + if (!Iznik.Session.getGroup(group.id)) { + // Need to join the group. + var name = group.namedisplay + self.groupid = group.id + self.$('.js-groupname').html(name) + self.$('.js-needtojoin').fadeIn('slow') + } else { + // Probably the id for a rejected message. Can't see other people's. + self.$('.js-gone').fadeIn('slow') + } + }) + } + }) - events: { - 'click .js-join': 'join' - }, + Iznik.Session.testLoggedIn([ + 'me', + 'groups' + ]) + } else if (self.model.get('subject') && + !self.model.get('deleted') && + (!self.model.get('outcomes') || self.model.get('outcomes').length == 0) && + (self.model.get('groups').length > 0) + ) { + // We might fail to fetch, or fetch a deleted message, or fetch a completed message. In all these + // cases the message shouldn't show. + var v = new Iznik.Views.User.Message.Replyable({ + model: self.model + }) - join: function() { - var self = this; - $.ajax({ - url: API + 'memberships', - type: 'POST', - headers: { - 'X-HTTP-Method-Override': 'PUT' - }, - data: { - groupid: self.groupid - }, complete: function () { - // Refresh our group list and re-render. - self.listenToOnce(Iznik.Session, 'isLoggedIn', function (loggedIn) { - self.render(); - }); - - Iznik.Session.testLoggedIn([ - 'me', - 'groups' - ]); - } - }); - }, + v.expanded = true + v.render().then(function () { + self.$('.js-message').append(v.el) - render: function () { - var self = this; - var p = Iznik.Views.Page.prototype.render.call(self).then(function () { - self.adSense(); - - self.model = new Iznik.Models.Message({ - id: self.options.id - }); - self.model.fetch().then(function () { - var ret = self.model.get('ret'); - - if (ret == 2) { - // Permission denied. This means it is a message which we are not allowed to see. - // - // Most commonly this is because we're logged out, and need to log in before - // we're allowed to see it. - self.listenToOnce(Iznik.Session, 'isLoggedIn', function(loggedIn) { - if (!loggedIn) { - // We are logged out, and the contents are not visible to non-group members. - self.listenToOnce(Iznik.Session, 'loggedIn', function () { - self.render(); - }); - - Iznik.Session.forceLogin(); - } else { - // Still can't see it logged in - var groups = self.model.get('groups'); - _.each(groups, function(group) { - if (!Iznik.Session.getGroup(group.id)) { - // Need to join the group. - var name = group.namedisplay; - self.groupid = group.id; - self.$('.js-groupname').html(name); - self.$('.js-needtojoin').fadeIn('slow'); - } else { - // Probably the id for a rejected message. Can't see other people's. - self.$('.js-gone').fadeIn('slow'); - } - }); - } - }); - - Iznik.Session.testLoggedIn([ - 'me', - 'groups' - ]); - } else if (self.model.get('subject') && - !self.model.get('deleted') && - (!self.model.get('outcomes') || self.model.get('outcomes').length == 0) && - (self.model.get('groups').length > 0) - ) { - // We might fail to fetch, or fetch a deleted message, or fetch a completed message. In all these - // cases the message shouldn't show. - var v = new Iznik.Views.User.Message.Replyable({ - model: self.model - }); - - v.expanded = true; - v.render().then(function () { - self.$('.js-message').append(v.el); - - var group = self.model.get('groups')[0]; - self.$('.js-moregroup').html(group.namedisplay); - self.$('.js-groupurl').attr('href', '/explore/' + group.nameshort); - self.$('.js-more').show(); - }); - } else { - self.$('.js-gone').fadeIn('slow'); - } - }); - }); - - return (p); - } - }); -}); + var group = self.model.get('groups')[0] + self.$('.js-moregroup').html(group.namedisplay) + self.$('.js-groupurl').attr('href', '/explore/' + group.nameshort) + self.$('.js-more').show() + }) + } else { + self.$('.js-gone').fadeIn('slow') + } + }) + }) + + return (p) + } + }) +}) From 19241ac19163f445bf2114dbbf6686321e5e9fcf Mon Sep 17 00:00:00 2001 From: Edward Hibbert Date: Tue, 9 Apr 2019 20:08:09 +0100 Subject: [PATCH 23/39] Change "Copy to clipboard" to "Copy link" --- http/template/user/find/share.html | 2 +- http/template/user/message/replyable.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/http/template/user/find/share.html b/http/template/user/find/share.html index 4c993bc4e..30c523524 100644 --- a/http/template/user/find/share.html +++ b/http/template/user/find/share.html @@ -15,7 +15,7 @@

    -
     Copy to Clipboard +
     Copy link
    diff --git a/http/template/user/message/replyable.html b/http/template/user/message/replyable.html index ef34342d6..286c25cfd 100644 --- a/http/template/user/message/replyable.html +++ b/http/template/user/message/replyable.html @@ -119,7 +119,7 @@

    {{snippet}}... Read more >>