Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meetup-Events können nun in die Softwerkskammer gespiegelt werden #1362

Merged
merged 14 commits into from
Aug 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions commonComponents/pug/formComponents.pug
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ mixin currency(name, label, value, tooltip, placeholder, readonly)
mixin textareaPure(name, label, value, placeholder)
+textareaInternal('form-control', '4', name, label, value, null, placeholder)

mixin textarea(name, label, value, tooltip, placeholder)
+textareaInternal('md-textarea', '7', name, label, value, tooltip, placeholder)
mixin textarea(name, label, value, tooltip, placeholder, readonly)
+textareaInternal('md-textarea', '7', name, label, value, tooltip, placeholder, readonly)

mixin hightextarea(name, label, value, tooltip, placeholder)
+textareaInternal('md-textarea', '15', name, label, value, tooltip, placeholder)
Expand Down Expand Up @@ -99,15 +99,15 @@ mixin memberSubmitButtons(submitText)
mixin hidden(name, value)
input(type='hidden', name=name, value=value)

mixin date(name, label, value, tooltip)
mixin date(name, label, value, tooltip, readonly)
.form-group
+controlLabel(name, label, tooltip)
input.form-control.datepicker(id=name, type='text', name=name, value=value)
input.form-control.datepicker(id=name, type='text', name=name, value=value, disabled=readonly)

mixin time(name, value)
mixin time(name, value, readonly)
.form-group
label.control-label(for=name)  
input.form-control.timepicker(id=name, type='text', name=name, value=value)
input.form-control.timepicker(id=name, type='text', name=name, value=value, disabled=readonly)

mixin colorPicker(name, value)
.form-group
Expand All @@ -134,10 +134,10 @@ mixin controlLabel(name, label, tooltip)
-else
| #{label}:

mixin textareaInternal(classname, rowCount, name, label, value, tooltip, placeholder)
mixin textareaInternal(classname, rowCount, name, label, value, tooltip, placeholder, readonly)
.form-group
+controlLabel(name, label, tooltip)
textarea(class=classname, id=name, rows=rowCount, type='text', name=name, placeholder=placeholder) #{value}
textarea(class=classname, id=name, rows=rowCount, type='text', name=name, placeholder=placeholder, disabled=readonly) #{value}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muss readonly jetzt immer gesetzt werden? - Sonst steht da readonly = undefined...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wo steht das? Im fertigen HTML sehe ich nix davon.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Ich hätte das vermutet, wenn man es nicht setzt...


mixin reallyDeleteModalPost(header, options, extraclasses)
+reallyDeleteModalOnly(header, options)
Expand Down
1 change: 1 addition & 0 deletions config/beans.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"calendarService": {"module": "./softwerkskammer/lib/activities/calendarService"},
"dashboardService": {"module": "./softwerkskammer/lib/dashboard/dashboardService"},
"groupsService": {"module": "./softwerkskammer/lib/groups/groupsService"},
"meetupActivitiesService": {"module": "./softwerkskammer/lib/meetupActivities/meetupActivitiesService"},
"groupsAndMembersService": {"module": "./softwerkskammer/lib/groupsAndMembers/groupsAndMembersService"},
"icalService": {"module": "./softwerkskammer/lib/activities/icalService"},
"galleryService": {"module": "./softwerkskammer/lib/gallery/galleryService"},
Expand Down
5 changes: 5 additions & 0 deletions locales/translation-de.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
"already_registered": "SoCraTes only",
"billing_address": "Rechnungsanschrift",
"blog_entry": "Blogartikel zur Aktivität",
"cloned_from_meetup_text": "Diese Aktivität wurde von Meetup kopiert. Einige Informationen kannst Du daher nur auf Meetup bearbeiten. Deine Änderungen werden zeitnah hierhin übertragen.",
"count": "Anzahl",
"count_participants_interval": "(0){Bislang gibt es keine Teilnahmezusagen.};(1){Bislang hat ein Mitglied seine Teilnahme zugesagt.};(2-inf){Bislang haben {{count}} Mitglieder ihre Teilnahme zugesagt.}",
"count_participants_meetup_interval": "(0){Woanders gibt es keine Anmeldungen.};(1){Woanders gibt es eine Anmeldung.};(2-inf){Woanders gibt es {{count}} Anmeldungen.}",
"create": "Aktivität anlegen",
"created_by": "Angelegt von",
"delete": "Aktivität löschen",
Expand Down Expand Up @@ -252,6 +254,8 @@
"join": "Gruppe beitreten",
"leave": "Gruppe verlassen",
"map_label": "Label in Karte",
"meetup_clone_title": "Kopiert existierende Events von Meetup.",
"meetupURL_label": "Meetup-URL (falls für diese Gruppe erforderlich)",
"more": "mehr lesen…",
"new": "Neue Gruppe",
"other": "Andere",
Expand All @@ -262,6 +266,7 @@
"email_prefix": "steht innerhalb [] vor dem Subject in gesendeten E-Mails",
"gotogroup": "Zur Gruppenseite.",
"map_label": "Label, das in der Karte angezeigt wird",
"meetupURL_label": "Manche Gruppen kommen leider nicht ohne eine Präsenz auf Meetup aus. Sollte das auch für diese Gruppe gelten, kann hier die Meetup-URL der Gruppe eingetragen werden. Dadurch werden die Meetup-Events der Gruppe automatisch in die Softwerkskammer übernommen.",
"name": "Die Adresse, unter der die Gruppe \"@softwerkskammer.org\" erreichbar ist.",
"title": "Anzeigetext innerhalb der Site, ist auch Realname für E-Mails",
"x_coord": "Waagerechte Position auf der Karte",
Expand Down
3 changes: 2 additions & 1 deletion softwerkskammer/frontendtests/fixtures/locals.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ module.exports = {
canUnsubscribe: function () { return ''; },
hasWaitinglist: function () { return ''; }
};
}
},
clonedFromMeetup: function () { return false; }
},
groups: [],
group: {},
Expand Down
21 changes: 16 additions & 5 deletions softwerkskammer/lib/activities/activity.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ class Activity {
return this.state.editorIds || [];
}

clonedFromMeetup() {
return !!this.state.clonedFromMeetup;
}

meetupRSVPCount() {
return this.state.meetupRSVPCount || 0;
}

fillFromUI(object, editorIds) {
this.state.url = object.url;

Expand All @@ -83,6 +91,9 @@ class Activity {
this.state.startUnix = fieldHelpers.parseToUnixUsingDefaultTimezone(object.startDate, object.startTime);
this.state.endUnix = fieldHelpers.parseToUnixUsingDefaultTimezone(object.endDate, object.endTime);

this.state.clonedFromMeetup = object.clonedFromMeetup;
this.state.meetupRSVPCount = object.meetupRSVPCount;

if (!this.id() || this.id() === 'undefined') {
this.state.id = fieldHelpers.createLinkFrom([this.assignedGroup(), this.title(), this.startMoment()]);
}
Expand Down Expand Up @@ -236,11 +247,11 @@ class Activity {
const resource = this.resourceNamed(resourceName);
const memberIds = resource.registeredMembers();
return this.participants
.filter(participant => memberIds.some(memberId => memberId === participant.id()))
.map(member => {
member.registeredAt = resource.registrationDateOf(member.id());
return member;
});
.filter(participant => memberIds.some(memberId => memberId === participant.id()))
.map(member => {
member.registeredAt = resource.registrationDateOf(member.id());
return member;
});
}
}

Expand Down
10 changes: 10 additions & 0 deletions softwerkskammer/lib/activities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const icalService = beans.get('icalService');
const groupsService = beans.get('groupsService');
const activitystore = beans.get('activitystore');
const memberstore = beans.get('memberstore');
const meetupActivitiesService = beans.get('meetupActivitiesService');

const Activity = beans.get('activity');
const Group = beans.get('group');
Expand Down Expand Up @@ -91,6 +92,7 @@ function renderGdcrFor(gdcrDay, res, next) {
});
});
}

app.get('/gdcr2013', (req, res, next) => renderGdcrFor('2013-12-14', res, next));

app.get('/gdcr2014', (req, res, next) => renderGdcrFor('2014-11-15', res, next));
Expand Down Expand Up @@ -228,6 +230,13 @@ app.post('/submit', (req, res, next) => {
);
});

app.post('/clone-from-meetup', (req, res, next) => {
meetupActivitiesService.cloneActivitiesFromMeetup((err) => {
if (err) { return next(err); }
res.redirect('/activities');
});
});

app.get('/checkurl', (req, res) => misc.validate(req.query.url, req.query.previousUrl, R.partial(activitiesService.isValidUrl, [reservedURLs]), res.end));

app.get('/:url', (req, res, next) => {
Expand Down Expand Up @@ -265,6 +274,7 @@ function subscribe(body, req, res, next) {
res.redirect('/activities/' + encodeURIComponent(activityUrl));
});
}

app.post('/subscribe', (req, res, next) => subscribe(req.body, req, res, next));

app.get('/subscribe', (req, res, next) => {
Expand Down
27 changes: 15 additions & 12 deletions softwerkskammer/lib/activities/views/activities-forms.pug
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,39 @@ mixin activityform(activity, groups, editorNames, participantNames)
| #{t('activities.edit')}
else
| #{t('activities.create')}
p #{t('activities.helptext')}
a(href='/wiki/hilfe/activities-hilfe') #{t('activities.helplink')}
if (activity.clonedFromMeetup())
p #{t('activities.cloned_from_meetup_text')}
else
p #{t('activities.helptext')}
a(href='/wiki/hilfe/activities-hilfe') #{t('activities.helplink')}
.row
.col-md-6
+text('title', t('activities.title'), activity.title(), t('activities.tooltip.title'))
+text('title', t('activities.title'), activity.title(), t('activities.tooltip.title'), '', activity.clonedFromMeetup())
.row
.col-xs-6(style='padding-right:5px')
.col-xs-6(style='padding-left:0px;padding-right:3px')
+date('startDate', t('activities.start'), activity.startMoment().locale(language).format('L'), t('activities.tooltip.start'))
+date('startDate', t('activities.start'), activity.startMoment().locale(language).format('L'), t('activities.tooltip.start'), activity.clonedFromMeetup())
.col-xs-6(style='padding-left:3px;padding-right:0px')
+time('startTime', activity.startMoment().locale(language).format('LT'))
+time('startTime', activity.startMoment().locale(language).format('LT'), activity.clonedFromMeetup())
.col-xs-6(style='padding-left:5px')
.col-xs-6(style='padding-left:0px;padding-right:3px')
+date('endDate', t('activities.end'), activity.endMoment().locale(language).format('L'), t('activities.tooltip.end'))
+date('endDate', t('activities.end'), activity.endMoment().locale(language).format('L'), t('activities.tooltip.end'), activity.clonedFromMeetup())
#dates.col-xs-6(style='padding-left:3px;padding-right:0px')
+time('endTime', activity.endMoment().locale(language).format('LT'))
+textarea('description', t('general.description'), activity.description())
+textarea('direction', t('activities.directions'), activity.direction())
+time('endTime', activity.endMoment().locale(language).format('LT'), activity.clonedFromMeetup())
+textarea('description', t('general.description'), activity.description(), '', '', activity.clonedFromMeetup())
+textarea('direction', t('activities.directions'), activity.direction(), '', '', activity.clonedFromMeetup())
+editableMultiselect('editorIds', t('activities.editors'), editorNames, participantNames)
.col-md-6
.form-group
+controlLabel('url', t('activities.address_suffix'), t('activities.tooltip.address_suffix'))
input.form-control#url(type='text', name='url', value=activity.url())
+text('location', t('activities.location'), activity.location())
input.form-control#url(type='text', name='url', value=activity.url(), disabled=activity.clonedFromMeetup())
+text('location', t('activities.location'), activity.location(), '', '', activity.clonedFromMeetup())
.form-group
+controlLabel('preview', 'Preview')
#map.hidden-print(style='width: ' + '100%' + '; height: ' + '420px')
.form-group
label.control-label(for='assignedGroup') #{t('groups.group')}:
select#assignedGroup.form-control.enhance(name='assignedGroup')
select#assignedGroup.form-control.enhance(name='assignedGroup', disabled=activity.clonedFromMeetup())
for group in groups
option(value=group.id, selected = (activity.assignedGroup() != undefined && activity.assignedGroup() == group.id)) #{group.longName}
.row
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hier willst Du "readonly" setzen wenn aus Meetup. Richtig?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ich glaube, dass das hier kein Mixin ist, sondern eine Pug-Komponente, deswegen will ich hier disabled setzen.

Expand Down
2 changes: 2 additions & 0 deletions softwerkskammer/lib/activities/views/get.pug
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ block content
.hidden-print
if (allowsRegistration)
p #{t('activities.count_participants_interval', { postProcess: 'interval', count: activity.participants.length })}
if (activity.clonedFromMeetup())
p (#{t('activities.count_participants_meetup_interval', {postProcess: 'interval', count: activity.meetupRSVPCount()})})
+subscriptionButtons(activity, resourceRegistrationRenderer)
if (accessrights.isRegistered() && activity.participants.length > 0)
h4 #{t('activities.accepted_by')}:
Expand Down
31 changes: 18 additions & 13 deletions softwerkskammer/lib/activities/views/index.pug
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
extends ../../../views/layout
include ../../../../commonComponents/pug/formComponents
include activities-mixins

block title
Expand All @@ -8,18 +9,22 @@ block content
.row
.col-md-12
.page-header
.btn-group.pull-right
.btn-group
a.btn.btn-default.dropdown-toggle(data-toggle='dropdown')
| #{range}  
span.caret
ul.dropdown-menu
li: a(href='/activities/upcoming') #{t('activities.upcoming')}
li: a(href='/activities/past') #{t('activities.past')}
li: a(href='/activities') #{t('general.all')}
li: a(href='/activities/gdcr') #{t('general.gdcr_activities')}
a.btn.btn-default(href=webcalURL, title=t('activities.export_subscribe')): i.fa.fa-calendar.fa-fw
if (accessrights.canCreateActivity())
a.btn.btn-default(href='new/', title=t('activities.new')): i.fa.fa-file-o.fa-fw
form(role='form', method='POST', action='clone-from-meetup/')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warum das Form um alles herum? - Ich fände es besser, wenn es nur um den Meetup button ist

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hab ich probiert, aber dann wird die Buttonleiste unterbrochen (also in zwei Stücke gehackt). Wenn Du das reparieren kannst, ändere ich das gern wieder.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

+csrf
.btn-group.pull-right
.btn-group
a.btn.btn-default.dropdown-toggle(data-toggle='dropdown')
| #{range}  
span.caret
ul.dropdown-menu
li: a(href='/activities/upcoming') #{t('activities.upcoming')}
li: a(href='/activities/past') #{t('activities.past')}
li: a(href='/activities') #{t('general.all')}
li: a(href='/activities/gdcr') #{t('general.gdcr_activities')}
a.btn.btn-default(href=webcalURL, title=t('activities.export_subscribe')): i.fa.fa-calendar.fa-fw
if (accessrights.isSuperuser())
button.btn.btn-default(type='submit'): i.fa.fa-meetup.fa-fw
if (accessrights.canCreateActivity())
a.btn.btn-default(href='new/', title=t('activities.new')): i.fa.fa-file-o.fa-fw
h2 #{t('activities.activities')}
+activityList(activities)
17 changes: 13 additions & 4 deletions softwerkskammer/lib/commons/fieldHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ module.exports = {
return text;
},

readableDate: function readableDate(unixtimestamp) {
return moment.unix(unixtimestamp).utc().format('DD.MM.YYYY');
},

parseToUnixUsingDefaultTimezone: function parseToUnixUsingDefaultTimezone(dateString, timeString) {
const result = this.parseToMomentUsingDefaultTimezone(dateString, timeString);
return result ? result.unix() : undefined;
Expand All @@ -87,6 +83,19 @@ module.exports = {
return undefined;
},

meetupDateToActivityTimes: function meetupDateToActivityTimes(meetupStartDate, meetupStartTime, durationInMillis) {
const startPoint = moment.tz(meetupStartDate + ' ' + meetupStartTime, 'YYYY-MM-DD H:m', this.defaultTimezone());
const endPoint = startPoint.clone(); // moment is mutable, clone it first!
endPoint.add(durationInMillis, 'milliseconds');

return {
startDate: startPoint.format('DD.MM.YYYY'),
startTime: startPoint.format('HH:mm'),
endDate: endPoint.format('DD.MM.YYYY'),
endTime: endPoint.format('HH:mm')
};
},

defaultTimezone: function defaultTimezone() {
return 'Europe/Berlin';
},
Expand Down
4 changes: 4 additions & 0 deletions softwerkskammer/lib/commons/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ module.exports = {

startsWith: function startsWith(string, start) {
return string.indexOf(start) === 0;
},

stripTrailingSlash: function stripTrailingSlash(str) {
return str.substr(-1) === '/' ? str.substr(0, str.length - 1) : str;
}
};

10 changes: 10 additions & 0 deletions softwerkskammer/lib/groups/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class Group {
this.description = object.description;
this.type = object.type;
this.emailPrefix = object.emailPrefix;
this.meetupURL = object.meetupURL;
this.color = object.color;
this.organizers = misc.toArray(object.organizers);
this.mapX = object.mapX;
Expand Down Expand Up @@ -55,6 +56,15 @@ class Group {
return this.longName + ' [' + this.emailPrefix + '] - ' + this.id;
}

meetupUrlName() {
if (this.meetupURL) {
const strippedURL = misc.stripTrailingSlash(this.meetupURL);
return strippedURL.substr(strippedURL.lastIndexOf('/') + 1);
} else {
return null;
}
}

// Helper functions (static) -> look for a better place to implement
static regionalsFrom(groups) {
return groups.filter(group => group.type === regionalgruppe);
Expand Down
8 changes: 8 additions & 0 deletions softwerkskammer/lib/groups/groupstore.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ module.exports = {
persistence.getByField({emailPrefix: misc.toLowerCaseRegExp(prefix)}, R.partial(toGroup, [callback]));
},

getGroupsWithMeetupURL: function getGroupsWithMeetupURL(callback) {
persistence.listByField(
{meetupURL: {$exists: true, $nin: ['', null, undefined]}},
{},
R.partial(toGroupList, [callback])
);
},

saveGroup: function saveGroup(group, callback) {
delete group.members; // we do not want to persist the group members
persistence.save(group, callback);
Expand Down
12 changes: 12 additions & 0 deletions softwerkskammer/lib/groups/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const groupstore = beans.get('groupstore');
const wikiService = beans.get('wikiService');
const Group = beans.get('group');
const groupsAndMembers = beans.get('groupsAndMembersService');
const meetupActivitiesService = beans.get('meetupActivitiesService');
const activitystore = beans.get('activitystore');
const statusmessage = beans.get('statusmessage');

Expand Down Expand Up @@ -80,6 +81,17 @@ app.get('/edit/:groupname', (req, res, next) => {
});
});

app.post('/clone-from-meetup-for-group', (req, res, next) => {
groupstore.getGroup(req.body.groupname, (err, group) => {
if (err || !group) { return next(err); }
meetupActivitiesService.cloneActivitiesFromMeetupForGroup(group, (err2) => {
if (err2) { return next(err2); }
res.redirect('/groups/' + req.body.groupname);
});
});

});

app.get('/checkgroupname', (req, res) => {
misc.validate(req.query.id, null, groupsService.isGroupNameAvailable, res.end);
});
Expand Down
Loading