From 10a29fe20b438ea27c27c055c813bf7a3bf1cd33 Mon Sep 17 00:00:00 2001 From: DaVania Date: Sun, 10 Apr 2022 22:27:50 +0000 Subject: [PATCH] DEV: Adds age support to discourse-cakeday plugin Enables staff/admins to see the age of a given user, directly from the user card. Hides the year in json files for non staff/admin users Added an input element to user preferences, for year input. Added sitesettings to enable new feature, and select order of input fields MM-DD-YYYY vs. DD-MM-YYYY TODO: Enable user to choose age visibility for non staff/admins --- .../user-card-post-names/user-card-cakeday.js | 6 ++ .../user-date-of-birth-input.js | 12 ++- .../discourse/initializers/cakeday.js | 15 ++- assets/javascripts/discourse/lib/cakeday.js | 9 ++ .../user-card-cakeday.hbs | 1 + .../user-date-of-birth-input.hbs | 95 ++++++++++++++----- .../mobile/user-date-of-birth-input.scss | 5 + .../stylesheets/user-date-of-birth-input.scss | 5 + config/locales/client.da.yml | 3 + config/locales/client.en.yml | 4 + config/locales/server.da.yml | 2 + config/locales/server.en.yml | 2 + config/settings.yml | 6 ++ plugin.rb | 11 ++- spec/serializers/user_serializer_spec.rb | 16 ++++ 15 files changed, 163 insertions(+), 29 deletions(-) create mode 100644 assets/stylesheets/user-date-of-birth-input.scss diff --git a/assets/javascripts/discourse/connectors/user-card-post-names/user-card-cakeday.js b/assets/javascripts/discourse/connectors/user-card-post-names/user-card-cakeday.js index 778f277..167c7f2 100644 --- a/assets/javascripts/discourse/connectors/user-card-post-names/user-card-cakeday.js +++ b/assets/javascripts/discourse/connectors/user-card-post-names/user-card-cakeday.js @@ -3,6 +3,7 @@ import { cakedayBirthday, cakedayBirthdayTitle, cakedayTitle, + userAgeTitle, } from "discourse/plugins/discourse-cakeday/discourse/lib/cakeday"; export default { @@ -17,5 +18,10 @@ export default { "cakedayBirthdayTitle", cakedayBirthdayTitle(args.user, this.currentUser) ); + const isStaff = this.currentUser && this.currentUser.staff; + const isAdmin = this.currentUser && this.currentUser.admin; + if (isAdmin || isStaff) { + component.set("userAgeTitle", userAgeTitle(args.user, this.currentUser)); + } }, }; diff --git a/assets/javascripts/discourse/connectors/user-custom-preferences/user-date-of-birth-input.js b/assets/javascripts/discourse/connectors/user-custom-preferences/user-date-of-birth-input.js index 4398dfc..9b59e5f 100644 --- a/assets/javascripts/discourse/connectors/user-custom-preferences/user-date-of-birth-input.js +++ b/assets/javascripts/discourse/connectors/user-custom-preferences/user-date-of-birth-input.js @@ -1,5 +1,6 @@ export default { setupComponent(args, component) { + const year = 1904; const months = moment.months().map((month, index) => { return { name: month, value: index + 1 }; }); @@ -7,6 +8,9 @@ export default { const days = Array.from(Array(31).keys()).map((x) => (x + 1).toString()); const dateOfBirth = args.model.get("date_of_birth"); + const userBirthdayYear = dateOfBirth + ? (moment(dateOfBirth, "YYYY-MM-DD").year() !== year ? moment(dateOfBirth, "YYYY-MM-DD").year() : null) + : null; const userBirthdayMonth = dateOfBirth ? moment(dateOfBirth, "YYYY-MM-DD").month() + 1 : null; @@ -15,8 +19,10 @@ export default { : null; component.setProperties({ + year, months, days, + userBirthdayYear, userBirthdayMonth, userBirthdayDay, }); @@ -24,13 +30,17 @@ export default { const updateBirthday = function () { let date = ""; - if (component.userBirthdayMonth && component.userBirthdayDay) { + if (component.userBirthdayYear && component.userBirthdayMonth && component.userBirthdayDay) { + date = `${component.userBirthdayYear}-${component.userBirthdayMonth}-${component.userBirthdayDay}`; + } + else if (component.userBirthdayMonth && component.userBirthdayDay) { date = `1904-${component.userBirthdayMonth}-${component.userBirthdayDay}`; } args.model.set("date_of_birth", date); }; + component.addObserver("userBirthdayYear", updateBirthday); component.addObserver("userBirthdayMonth", updateBirthday); component.addObserver("userBirthdayDay", updateBirthday); }, diff --git a/assets/javascripts/discourse/initializers/cakeday.js b/assets/javascripts/discourse/initializers/cakeday.js index 98db0a2..b1670f5 100644 --- a/assets/javascripts/discourse/initializers/cakeday.js +++ b/assets/javascripts/discourse/initializers/cakeday.js @@ -30,14 +30,20 @@ function initializeCakeday(api) { }); }, - @observes("userBirthdayMonth", "userBirthdayDay") + @observes("userBirthdayYear", "userBirthdayMonth", "userBirthdayDay") _setUserDateOfBirth() { + const userBirthdayYear = this.get("userBirthdayYear"); const userBirthdayMonth = this.get("userBirthdayMonth"); const userBirthdayDay = this.get("userBirthdayDay"); const user = this.get("model"); let date = ""; - if (userBirthdayMonth !== "" && userBirthdayDay !== "") { + if (userBirthdayYear !== "" && userBirthdayMonth !== "" && userBirthdayDay !== "") { + date = `${this.get("userBirthdayYear")}-${this.get("userBirthdayMonth")}-${this.get( + "userBirthdayDay" + )}`; + } + else if (userBirthdayMonth !== "" && userBirthdayDay !== "") { date = `1904-${this.get("userBirthdayMonth")}-${this.get( "userBirthdayDay" )}`; @@ -46,6 +52,11 @@ function initializeCakeday(api) { user.set("date_of_birth", date); }, + @discourseComputed("model.date_of_birth") + userBirthdayYear(dateOfBirth) { + return moment(dateOfBirth, "YYYY-MM-DD").year() + 1; + }, + @discourseComputed("model.date_of_birth") userBirthdayMonth(dateOfBirth) { return moment(dateOfBirth, "YYYY-MM-DD").month() + 1; diff --git a/assets/javascripts/discourse/lib/cakeday.js b/assets/javascripts/discourse/lib/cakeday.js index 46a0b2f..1d0541f 100644 --- a/assets/javascripts/discourse/lib/cakeday.js +++ b/assets/javascripts/discourse/lib/cakeday.js @@ -1,4 +1,5 @@ import { isEmpty } from "@ember/utils"; +import I18n from "I18n"; export function isSameDay(date, opts) { let formatString = "YYYY"; @@ -30,6 +31,14 @@ export function cakedayBirthday(dateOfBirth) { return isSameDay(dateOfBirth); } +export function userAge(dateOfBirth) { + return dateOfBirth ? (moment(dateOfBirth, "YYYY-MM-DD").year() !== 1904 ? moment().diff(dateOfBirth, 'years') : null) : null; +} + +export function userAgeTitle(user) { + return (user.date_of_birth && moment(user.date_of_birth, "YYYY-MM-DD").year() !== 1904) ? I18n.t("cakeday.age", {age: userAge(user.date_of_birth)}) : null; +} + export function cakedayTitle(user, currentUser) { if (isSameUser(user, currentUser)) { return "user.anniversary.user_title"; diff --git a/assets/javascripts/discourse/templates/connectors/user-card-post-names/user-card-cakeday.hbs b/assets/javascripts/discourse/templates/connectors/user-card-post-names/user-card-cakeday.hbs index fe010bc..83e16e4 100644 --- a/assets/javascripts/discourse/templates/connectors/user-card-post-names/user-card-cakeday.hbs +++ b/assets/javascripts/discourse/templates/connectors/user-card-post-names/user-card-cakeday.hbs @@ -9,3 +9,4 @@ {{emoji-images list=siteSettings.cakeday_emoji title=cakedayTitle}} {{/if}} {{/if}} +{{userAgeTitle}} diff --git a/assets/javascripts/discourse/templates/connectors/user-custom-preferences/user-date-of-birth-input.hbs b/assets/javascripts/discourse/templates/connectors/user-custom-preferences/user-date-of-birth-input.hbs index 95f5396..1f2f3bd 100644 --- a/assets/javascripts/discourse/templates/connectors/user-custom-preferences/user-date-of-birth-input.hbs +++ b/assets/javascripts/discourse/templates/connectors/user-custom-preferences/user-date-of-birth-input.hbs @@ -2,31 +2,76 @@
- {{combo-box - content=months - value=userBirthdayMonth - valueAttribute="value" - valueProperty="value" - none="cakeday.none" - options=(hash - clearable=true - autoInsertNoneItem=false - ) - onChange=(action (mut userBirthdayMonth)) - }} - - {{combo-box - content=days - value=userBirthdayDay - valueProperty=null - nameProperty=null - none="cakeday.none" - options=(hash - clearable=true - autoInsertNoneItem=false - ) - onChange=(action (mut userBirthdayDay)) - }} + {{#if siteSettings.cakeday_birthday_formatdmy}} + {{combo-box + content=days + value=userBirthdayDay + valueProperty=null + nameProperty=null + none="cakeday.dd" + options=(hash + clearable=true + autoInsertNoneItem=false + ) + onChange=(action (mut userBirthdayDay)) + }} + {{#if siteSettings.cakeday_birthday_show_year}} + - + {{/if}} + {{combo-box + content=months + value=userBirthdayMonth + valueAttribute="value" + valueProperty="value" + none="cakeday.mm" + options=(hash + clearable=true + autoInsertNoneItem=false + ) + onChange=(action (mut userBirthdayMonth)) + }} + {{else}} + {{combo-box + content=months + value=userBirthdayMonth + valueAttribute="value" + valueProperty="value" + none="cakeday.mm" + options=(hash + clearable=true + autoInsertNoneItem=false + ) + onChange=(action (mut userBirthdayMonth)) + }} + {{#if siteSettings.cakeday_birthday_show_year}} + - + {{/if}} + {{combo-box + content=days + value=userBirthdayDay + valueProperty=null + nameProperty=null + none="cakeday.dd" + options=(hash + clearable=true + autoInsertNoneItem=false + ) + onChange=(action (mut userBirthdayDay)) + }} + {{/if}} + {{#if siteSettings.cakeday_birthday_show_year}} + - + {{input + type="number" + class="year" + content=year + value=userBirthdayYear + min=1905 + max=2904 + placeholder=(i18n "user.date_of_birth.yyyy") + onChange=(action (mut userBirthdayYear)) + }} + {{/if}}
{{/if}} diff --git a/assets/stylesheets/mobile/user-date-of-birth-input.scss b/assets/stylesheets/mobile/user-date-of-birth-input.scss index 1f33e78..e110a23 100644 --- a/assets/stylesheets/mobile/user-date-of-birth-input.scss +++ b/assets/stylesheets/mobile/user-date-of-birth-input.scss @@ -1,3 +1,8 @@ +.mobile-view .user-custom-preferences-outlet.user-date-of-birth-input { + input.year { + width: 62px; + } +} .user-custom-preferences-outlet.user-date-of-birth-input { select { width: 49%; diff --git a/assets/stylesheets/user-date-of-birth-input.scss b/assets/stylesheets/user-date-of-birth-input.scss new file mode 100644 index 0000000..01b1ba1 --- /dev/null +++ b/assets/stylesheets/user-date-of-birth-input.scss @@ -0,0 +1,5 @@ +.user-custom-preferences-outlet.user-date-of-birth-input { + input.year { + width: 80px; + } +} diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index a2e71c6..d364e3c 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -11,6 +11,8 @@ da: user_title: "I dag er det din fødselsdag!" title: "I dag er det min fødselsdag!" label: "Fødselsdato" + yyyy: "ÅÅÅÅ" + show_age: "Vis alder til brugere" anniversary: user_title: "I dag er årsdagen hvor du blev en del af vores fælleskab!" title: "I dag er årsdagen for at jeg blev en del af dette fælleskab!" @@ -20,6 +22,7 @@ da: tomorrow: "I morgen" upcoming: "Kommende" all: "Alle" + age: "%{age} år" birthdays: title: "Fødselsdage" month: diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index fd5af7d..63fbe1a 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -5,16 +5,20 @@ en: user_title: "Today is your birthday!" title: "Today is my birthday!" label: "Date of Birth" + yyyy: "YYYY" anniversary: user_title: "Today is the anniversary of the day you joined our community!" title: "Today is the anniversary of the day I joined this community!" cakeday: none: " " + dd: "DD" + mm: "MM" title: Cakeday today: "Today" tomorrow: "Tomorrow" upcoming: "Upcoming" all: "All" + age: "%{age} years" birthdays: title: "Birthdays" month: diff --git a/config/locales/server.da.yml b/config/locales/server.da.yml index 037a08b..aff9314 100644 --- a/config/locales/server.da.yml +++ b/config/locales/server.da.yml @@ -7,6 +7,8 @@ da: site_settings: cakeday_enabled: "Vis cakeday humørikon[er] ved siden af navnet på brugeren, på den dato, hvor de blev medlem af Discourse." + cakeday_birthday_show_year: "Vis Kagedag års vælger, for at tillade brugere at indtaste deres alder." + cakeday_birthday_formatdmy: "Vis Kagedag vælgere som DD-MM-ÅÅÅÅ, i stedet for MM-DD-ÅÅÅÅ" cakeday_emoji: "De humørikon[er], der vises ved siden af navnet på brugeren, på den dato, hvor de blev medlem af Discourse. Flere humørikoner kan specificeres ved: smile|cake|smile" cakeday_birthday_enabled: "Vis fødselsdags humørikon[er] ved siden af navnet på brugeren, på deres fødselsdag." cakeday_birthday_emoji: "De humørikon[er], der vises ved siden af navnet på brugeren, på deres fødselsdag. Flere humørikoner kan specificeres ved: smile|cake|smile" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index fc5dc7b..73802c5 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1,6 +1,8 @@ en: site_settings: cakeday_enabled: "Show cakeday emoji[s] beside the user's name on the date they joined Discourse." + cakeday_birthday_show_year: "Show cakeday year selector, to allow users to input their age." + cakeday_birthday_formatdmy: "Show cakeday selectors as DD-MM-YYYY, instead of MM-DD-YYYY" cakeday_emoji: "The emoji[s] that will be shown beside the user's name on the date that they joined Discourse. Multiple emojis can be specified by: smile|cake|smile" cakeday_birthday_enabled: "Show birthday emoji[s] beside the user's name on their birthday." cakeday_birthday_emoji: "The emoji[s] that will be shown beside the user's name on their birthday. Multiple emojis can be specified by: smile|cake|smile" diff --git a/config/settings.yml b/config/settings.yml index 688cc0e..6f2a94f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -11,3 +11,9 @@ plugins: cakeday_birthday_emoji: default: 'birthday' client: true + cakeday_birthday_show_year: + default: false + client: true + cakeday_birthday_formatdmy: + default: false + client: true diff --git a/plugin.rb b/plugin.rb index c000973..2291df7 100644 --- a/plugin.rb +++ b/plugin.rb @@ -9,6 +9,7 @@ register_asset 'stylesheets/cakeday.scss' register_asset 'stylesheets/emoji-images.scss' +register_asset 'stylesheets/user-date-of-birth-input.scss' register_asset 'stylesheets/mobile/user-date-of-birth-input.scss' register_svg_icon "birthday-cake" if respond_to?(:register_svg_icon) @@ -60,7 +61,15 @@ class ::User end add_to_serializer(:user_card, :date_of_birth, false) do - object.date_of_birth + if object.date_of_birth != nil + if (scope.is_staff? || scope.is_admin?) + object.date_of_birth + else + Date.new(1904, object.date_of_birth.month, object.date_of_birth.day) + end + else + nil + end end add_to_serializer(:user_card, :include_date_of_birth?) do diff --git a/spec/serializers/user_serializer_spec.rb b/spec/serializers/user_serializer_spec.rb index ef5ed10..939c8d1 100644 --- a/spec/serializers/user_serializer_spec.rb +++ b/spec/serializers/user_serializer_spec.rb @@ -4,10 +4,26 @@ RSpec.describe UserSerializer do let(:user) { Fabricate(:user, date_of_birth: '2017-04-05') } + let!(:admin) { Fabricate(:admin) } + let(:agesafe_date_of_birth) { '1904-04-05' } context 'when user is logged in' do let(:serializer) { described_class.new(user, scope: Guardian.new(user), root: false) } + it "should include the user's date of birth" do + expect(serializer.as_json[:date_of_birth]).to eq(agesafe_date_of_birth) + end + + it "should not include the user's date of birth when cakeday_birthday_enabled is false" do + SiteSetting.cakeday_birthday_enabled = false + + expect(serializer.as_json[:date_of_birth]).to eq(nil) + end + end + + context 'when admin is logged in' do + let(:serializer) { described_class.new(user, scope: Guardian.new(admin), root: false) } + it "should include the user's date of birth" do expect(serializer.as_json[:date_of_birth]).to eq(user.date_of_birth) end