mumuki-laboratory 9.0.1 → 9.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/mumuki_laboratory/application/certificate.js +17 -0
  3. data/app/assets/javascripts/mumuki_laboratory/application/codemirror.js +10 -2
  4. data/app/assets/javascripts/mumuki_laboratory/application/discussions.js +43 -2
  5. data/app/assets/javascripts/mumuki_laboratory/application/faqs.js +90 -0
  6. data/app/assets/javascripts/mumuki_laboratory/application/multiple-files.js +7 -0
  7. data/app/assets/javascripts/mumuki_laboratory/application/organization.js +32 -0
  8. data/app/assets/javascripts/mumuki_laboratory/application/submissions-store.js +1 -1
  9. data/app/assets/javascripts/mumuki_laboratory/application/user.js +49 -5
  10. data/app/assets/stylesheets/mumuki_laboratory/application/_modules.scss +3 -0
  11. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_activity.scss +12 -0
  12. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_certificate.scss +33 -0
  13. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +14 -2
  14. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_faqs.scss +84 -0
  15. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_terms.scss +3 -2
  16. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_user_menu.scss +30 -2
  17. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_user_profile.scss +1 -0
  18. data/app/controllers/certificates_controller.rb +28 -0
  19. data/app/controllers/concerns/with_certificate_render.rb +25 -0
  20. data/app/controllers/discussions_messages_controller.rb +6 -2
  21. data/app/controllers/faqs_controller.rb +6 -0
  22. data/app/controllers/users_controller.rb +17 -0
  23. data/app/helpers/certificate_helper.rb +13 -0
  24. data/app/helpers/discussions_helper.rb +5 -1
  25. data/app/helpers/links_helper.rb +8 -0
  26. data/app/helpers/menu_bar_helper.rb +14 -10
  27. data/app/helpers/multiple_file_editor_helper.rb +2 -1
  28. data/app/helpers/user_activity_helper.rb +48 -0
  29. data/app/helpers/user_menu_helper.rb +27 -5
  30. data/app/mailers/application_mailer.rb +0 -1
  31. data/app/mailers/user_mailer.rb +9 -0
  32. data/app/views/certificates/_certificate.html.erb +44 -0
  33. data/app/views/certificates/_download.html.erb +20 -0
  34. data/app/views/certificates/verify.html.erb +40 -0
  35. data/app/views/discussions/_description_message.html.erb +1 -1
  36. data/app/views/discussions/_message.html.erb +1 -1
  37. data/app/views/discussions/_new_message.html.erb +13 -2
  38. data/app/views/discussions/new.html.erb +1 -1
  39. data/app/views/faqs/index.html.erb +20 -0
  40. data/app/views/layouts/_main.html.erb +4 -0
  41. data/app/views/layouts/_user_menu.html.erb +13 -16
  42. data/app/views/layouts/application.html.erb +6 -1
  43. data/app/views/layouts/exercise_inputs/editors/_code.html.erb +2 -1
  44. data/app/views/layouts/exercise_inputs/editors/_multiple_files.html.erb +1 -1
  45. data/app/views/user_mailer/certificate.html.erb +339 -0
  46. data/app/views/user_mailer/certificate.text.erb +10 -0
  47. data/app/views/users/_activity_indicator.html.erb +17 -0
  48. data/app/views/users/activity.html.erb +37 -0
  49. data/app/views/users/certificates.html.erb +32 -0
  50. data/config/initializers/inflections.rb +3 -0
  51. data/config/routes.rb +9 -0
  52. data/lib/mumuki/laboratory/extensions.rb +1 -0
  53. data/lib/mumuki/laboratory/extensions/date_and_time.rb +11 -0
  54. data/lib/mumuki/laboratory/locales/en.yml +20 -1
  55. data/lib/mumuki/laboratory/locales/es-CL.yml +25 -3
  56. data/lib/mumuki/laboratory/locales/es.yml +25 -3
  57. data/lib/mumuki/laboratory/locales/pt.yml +26 -1
  58. data/lib/mumuki/laboratory/version.rb +1 -1
  59. data/spec/controllers/certificates_controller_spec.rb +15 -0
  60. data/spec/controllers/discussions_messages_controller_spec.rb +20 -1
  61. data/spec/dummy/db/schema.rb +20 -3
  62. data/spec/features/certificate_programs_flow_spec.rb +17 -0
  63. data/spec/features/discussion_flow_spec.rb +2 -0
  64. data/spec/features/menu_bar_spec.rb +20 -0
  65. data/spec/features/profile_flow_spec.rb +12 -0
  66. data/spec/features/user_activity_flow_spec.rb +65 -0
  67. data/spec/helpers/certificate_helper_spec.rb +15 -0
  68. data/spec/helpers/user_activity_helper_spec.rb +32 -0
  69. metadata +170 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4fc890303f1598f34b8258b73ca92a3beb1d594cbdb7f00d729401df4e44f89
4
- data.tar.gz: 8ad9aba5c0b5d11251a972f2f5354f6f3ee6c6fcb3ef62d72b592e0d6e78b66f
3
+ metadata.gz: 1923025a623999bd5e8c2c802f024660f71d980865e5b436cfdf7ee27a361445
4
+ data.tar.gz: 368eace9064bfe944f5abecce4f36c5b5f55e15f08f3d7ee949eca7033adb0c2
5
5
  SHA512:
6
- metadata.gz: 3f44cc0d2df94e3b613abea57d4ff8a1c48628a2d3fbc1797cb2a2fdca1d54c1c639c8da8e19ddfb65c581f874927758cb1fbf673cd9cf66ed8c1b9186a5d9ca
7
- data.tar.gz: c5e6c4b580ec812c0ff84290558f6439fab546dc2be009da77c032b9059700230da3db4e1fc0765a4560d846d6409d4e6a17f79dbbff830a10199ad4ae25a6ec
6
+ metadata.gz: 126c58ab5c3d42c0055c85886cfdb49ae6714b797c7b2fb206417f97cdfd3bf3edd14bd66f268acc1ad79fcb1f6fa659073522a8ed91726ceda9a84fbee7b4a2
7
+ data.tar.gz: 43e8f6368254c0dff68d2194a68d7a4511c2732226463d1a77ba6e0cfe76b6b0ee0610a096480fc116ad8feec491a457657296316ab2b4e6039e72b5ec7883d3
@@ -0,0 +1,17 @@
1
+ mumuki.load(() => {
2
+ scaleCertificate();
3
+ mumuki.resize(scaleCertificate);
4
+
5
+ function scaleCertificate() {
6
+ const $certPreview = $('.certificate-preview');
7
+ const $muCertificate = $('.mu-certificate-box');
8
+ $muCertificate.css('transform', 'scale(1)');
9
+
10
+ const scaleWidth = $muCertificate.width() / $certPreview.width();
11
+ $muCertificate.css({
12
+ 'transform': `scale(${1 / scaleWidth})`,
13
+ 'transform-origin': `0 0`,
14
+ });
15
+ $certPreview.height($muCertificate.height());
16
+ }
17
+ });
@@ -20,12 +20,20 @@ mumuki.page.editors = [];
20
20
  $("#exercise_language_id").change(updateCodeMirrorLanguage);
21
21
  }
22
22
 
23
- function resetEditor() {
23
+ function resetSimpleEditor() {
24
24
  mumuki.page.dynamicEditors.forEach(function (e) {
25
25
  setDefaultContent(e, $('#default_content').val());
26
26
  });
27
27
  }
28
28
 
29
+ function resetEditor(isMultipleFiles) {
30
+ if (!isMultipleFiles) {
31
+ resetSimpleEditor();
32
+ } else {
33
+ mumuki.multipleFileEditor.resetEditor();
34
+ }
35
+ }
36
+
29
37
  function formatContent() {
30
38
  mumuki.page.editors.each(function (_, editor) {
31
39
  editor.setSelection({line: 0, ch: 0}, {line: editor.lineCount()});
@@ -87,7 +95,7 @@ mumuki.page.editors = [];
87
95
  $('.editor-reset').click(function (event) {
88
96
  event.stopPropagation();
89
97
  const selection = confirm(this.getAttribute('data-confirm'));
90
- if(selection) resetEditor();
98
+ if (selection) resetEditor($(event.target).parent().data('multiple-files'));
91
99
  });
92
100
 
93
101
  $('.editor-resize').click(function () {
@@ -1,9 +1,13 @@
1
1
  mumuki.load(() => {
2
2
  var $subscriptionSpans = $('.discussion-subscription > span');
3
3
  var $upvoteSpans = $('.discussion-upvote > span');
4
+ let $messagePreviewButton = $('.discussion-new-message-preview-button.preview');
5
+ let $messageEditButton = $('.discussion-new-message-preview-button.edit');
6
+ let $newMessageContent = $('.discussion-new-message-content');
7
+ let $newMessagePreview = $('#discussion-new-message-preview');
4
8
 
5
9
  function createNewMessageEditor() {
6
- var $textarea = $("#new-discussion-message");
10
+ var $textarea = $("#discussion-new-message");
7
11
  var textarea = $textarea[0];
8
12
  if (!textarea) return;
9
13
 
@@ -26,7 +30,7 @@ mumuki.load(() => {
26
30
  }
27
31
 
28
32
  createReadOnlyEditors();
29
- createNewMessageEditor();
33
+ let editor = createNewMessageEditor();
30
34
 
31
35
  var Forum = {
32
36
  toggleButton: function (spans) {
@@ -67,9 +71,46 @@ mumuki.load(() => {
67
71
  const params = new URLSearchParams(location.search);
68
72
  elem.is(':checked') ? params.set(key, elem.val()) : params.delete(key);
69
73
  location.search = params.toString();
74
+ },
75
+ discussionMessagePreview: function (url) {
76
+ return Forum.tokenRequest({
77
+ url: url,
78
+ method: 'GET',
79
+ processData: false,
80
+ dataType: 'json',
81
+ data: new URLSearchParams({ content: editor.getValue() } ),
82
+ success: function (response) {
83
+ showPreview(response.preview);
84
+ },
85
+ error: function (e) {
86
+ error = $messagePreviewButton.attr('error-text');
87
+ showPreview(error);
88
+ },
89
+ xhrFields: {withCredentials: true}
90
+ });
91
+ },
92
+ hidePreviewAndShowEditor: function(elem) {
93
+ togglePreviewAndEditButtons();
94
+ togglePreviewAndContentMessage();
70
95
  }
71
96
  };
72
97
 
98
+ function showPreview(preview) {
99
+ $newMessagePreview.html($.parseHTML(preview));
100
+ togglePreviewAndEditButtons();
101
+ togglePreviewAndContentMessage();
102
+ }
103
+
104
+ function togglePreviewAndEditButtons() {
105
+ $messagePreviewButton.toggleClass('hidden');
106
+ $messageEditButton.toggleClass('hidden');
107
+ }
108
+
109
+ function togglePreviewAndContentMessage() {
110
+ $newMessagePreview.toggleClass('hidden');
111
+ $newMessageContent.toggleClass('hidden');
112
+ }
113
+
73
114
  mumuki.Forum = Forum;
74
115
 
75
116
  });
@@ -0,0 +1,90 @@
1
+ mumuki.faqs = class {
2
+
3
+ constructor() {
4
+ this.faqs = $('.mu-faqs');
5
+ this.topHierarchyElem = "H2";
6
+ }
7
+
8
+ // ================
9
+ // == Public API ==
10
+ // ================
11
+
12
+ /**
13
+ * Actually setup the faqs page
14
+ */
15
+ load() {
16
+ if(!this.faqs) return;
17
+ this._createFaqsGroups();
18
+ this._createNavbar();
19
+ }
20
+
21
+ // =================
22
+ // == Private API ==
23
+ // =================
24
+
25
+ _createNavbar() {
26
+ const $faqsNavbar = $(".mu-faqs-navbar nav ul");
27
+ $('.mu-faqs-group').each((_index, faqGroup) => {
28
+ const $navItem = this._createNavbarItem($faqsNavbar, faqGroup)
29
+ const $faqGroup = $(faqGroup);
30
+ this._configureClickFor($navItem, $faqGroup, $faqsNavbar)
31
+ });
32
+ }
33
+
34
+ _createNavbarItem($faqsNavbar, faqGroup) {
35
+ const $navItem = $(`<li><a href="#${faqGroup.id}">`)
36
+ .html(`${faqGroup.querySelector('h2').textContent}`);
37
+ $faqsNavbar.append($navItem);
38
+ return $navItem;
39
+ }
40
+
41
+ _configureClickFor($navItem, $faqGroup, $faqsNavbar) {
42
+ $navItem.click(function(e){
43
+ e.preventDefault();
44
+ $faqsNavbar.find('li').removeClass('active');
45
+ $navItem.addClass('active');
46
+ $('html, body').animate({scrollTop: $faqGroup.offset().top}, 1000);
47
+ });
48
+ }
49
+
50
+ _createFaqsGroups() {
51
+ const elemsGroups = this._buildFaqsGroups();
52
+ elemsGroups.forEach((group, index) => {
53
+ $(group).wrapAll(`<div class='mu-faqs-group' id='mu-faqs-group-${index}'>`);
54
+ });
55
+ this._createFaqsIcons();
56
+ }
57
+
58
+ _createFaqsIcons() {
59
+ const $faqIcon = $('<i class="mu-faqs-group-icon fa fa-plus">');
60
+ $faqIcon.click(function(e){
61
+ const $elem = $(this);
62
+ $elem.toggleClass('fa-plus fa-minus');
63
+ $elem.closest('.mu-faqs-group').toggleClass('active');
64
+ })
65
+ $('.mu-faqs-group').prepend($faqIcon);
66
+ }
67
+
68
+ _buildFaqsGroups() {
69
+ let newGroup = [];
70
+ let elemsGroups = [];
71
+ let previousNodeName;
72
+ $(".mu-faqs-content").children().each((index, elem) => {
73
+ if(elem.nodeName === this.topHierarchyElem && newGroup.length) {
74
+ elemsGroups.push(newGroup);
75
+ newGroup = [];
76
+ }
77
+ newGroup.push(elem);
78
+ previousNodeName = elem.nodeName
79
+ });
80
+
81
+ elemsGroups.push(newGroup);
82
+
83
+ return elemsGroups;
84
+ }
85
+
86
+ };
87
+
88
+ mumuki.load(() => {
89
+ new mumuki.faqs().load();
90
+ });
@@ -135,6 +135,13 @@ mumuki.load(() => {
135
135
  });
136
136
  }
137
137
 
138
+ resetEditor() {
139
+ const defaultContents = this._getDataFromHiddenInput('#multifile-default-content');
140
+ mumuki.page.editors.each(function (i, editor) {
141
+ editor.getDoc().setValue(defaultContents[i].content);
142
+ });
143
+ }
144
+
138
145
  _addFile() {
139
146
  let name = prompt(this.locales.insert_file_name);
140
147
  const alreadyExists = this.files.toArray().some(it => it.name === name);
@@ -0,0 +1,32 @@
1
+ mumuki.organization = {
2
+
3
+ /**
4
+ * The current organization's id
5
+ *
6
+ * @type {number?}
7
+ * */
8
+ _id: null,
9
+
10
+ /**
11
+ * The current organization's id
12
+ *
13
+ * @type {number?}
14
+ * */
15
+ get id() {
16
+ return this._id;
17
+ },
18
+
19
+ /**
20
+ * Set global current organization information
21
+ */
22
+ load() {
23
+ const $muOrganizationId = $('#mu-organization-id');
24
+ if ($muOrganizationId.length) {
25
+ this._id = $muOrganizationId.val();
26
+ } else {
27
+ this._id = null;
28
+ }
29
+ }
30
+ };
31
+
32
+ mumuki.load(() => mumuki.organization.load());
@@ -94,7 +94,7 @@ mumuki.SubmissionsStore = (() => {
94
94
  }
95
95
 
96
96
  _keyFor(exerciseId) {
97
- return `/exercise/${exerciseId}/submission`;
97
+ return `/organization/${mumuki.organization.id}/user/${mumuki.user.id}/exercise/${exerciseId}/submission`;
98
98
  }
99
99
  }();
100
100
 
@@ -1,9 +1,53 @@
1
+ mumuki.user = {
2
+
3
+ /**
4
+ * The current user's id
5
+ *
6
+ * @type {number?}
7
+ * */
8
+ _id: null,
9
+
10
+ /**
11
+ * The current user's id
12
+ *
13
+ * @type {number?}
14
+ * */
15
+ get id() {
16
+ return this._id;
17
+ },
18
+
19
+ /**
20
+ * Set global current user information
21
+ */
22
+ load() {
23
+ const $muUserId = $('#mu-user-id');
24
+ if ($muUserId.length) {
25
+ this._id = $muUserId.val();
26
+ } else {
27
+ this._id = null;
28
+ }
29
+ }
30
+ };
31
+
1
32
  mumuki.load(() => {
2
- var hash = document.location.hash;
3
- if (hash) {
4
- $(".nav-tabs a[data-target='" + hash + "']").tab('show');
33
+ const $userMenuHeader = $('.mu-user-menu-header');
34
+ let onUserProfile = document.querySelector('.mu-profile-info') !== null;
35
+
36
+ $userMenuHeader.click(() => {
37
+ $('.mu-user-menu-items').toggleClass('mu-hidden-sm-screen');
38
+ $('#mu-user-menu-header-icon').toggleClass('fa-chevron-up fa-chevron-down');
39
+ });
40
+
41
+ if (onUserProfile) {
42
+ $userMenuHeader.click();
5
43
  }
6
- $('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
7
- window.location.hash = $(e.target).attr('data-target');
44
+
45
+ $('.mu-user-menu-item .active').click((e) => {
46
+ if (onUserProfile) {
47
+ e.preventDefault();
48
+ $userMenuHeader.click();
49
+ }
8
50
  });
51
+
52
+ mumuki.user.load()
9
53
  });
@@ -1,6 +1,8 @@
1
+ @import "modules/activity";
1
2
  @import "modules/avatar";
2
3
  @import "modules/book_header";
3
4
  @import "modules/breadcrumb";
5
+ @import "modules/certificate";
4
6
  @import "modules/checkboxes";
5
7
  @import "modules/console";
6
8
  @import "modules/content_show";
@@ -10,6 +12,7 @@
10
12
  @import "modules/editor";
11
13
  @import "modules/exercise_assignment";
12
14
  @import "modules/exercise_results";
15
+ @import "modules/faqs";
13
16
  @import "modules/flash";
14
17
  @import "modules/gs-board";
15
18
  @import "modules/guide_corollary";
@@ -0,0 +1,12 @@
1
+ .mu-user-activity-indicator {
2
+ text-align: center;
3
+ border-radius: 10px;
4
+
5
+ &.jumbotron {
6
+ padding: 0.5em 0;
7
+ }
8
+ }
9
+
10
+ .mu-user-activity-selector {
11
+ padding-top: 15px;
12
+ }
@@ -0,0 +1,33 @@
1
+ .mu-certificate {
2
+ border: 1px solid $mu-color-dark-separator;
3
+ }
4
+
5
+ .mu-certificate-name {
6
+ margin-top: 0;
7
+ margin-bottom: 1em;
8
+ }
9
+
10
+ .mu-certificate-data {
11
+ margin-top: 30px;
12
+ }
13
+
14
+ .mu-certificate-buttons {
15
+ margin-bottom: 30px;
16
+ }
17
+
18
+ .mu-certificate-download-btn {
19
+ height: 35px;
20
+ padding: 0;
21
+ width: 157px;
22
+ border: none;
23
+ }
24
+ .mu-certificate-download-btn-icon {
25
+ border-right: 1px solid darken($mu-color-complementary, 10%);
26
+ padding: 8px 6px 9px 6px;
27
+ width: 35px;
28
+ }
29
+ .mu-certificate-download-btn-text {
30
+ display: inline-block;
31
+ width: 115px;
32
+ text-align: center;
33
+ }
@@ -287,9 +287,21 @@ summary.discussion-summary {
287
287
  }
288
288
  }
289
289
 
290
+ .discussion-new-message-buttons {
291
+ display: flex;
292
+ margin: 0 -10px;
293
+
294
+ .btn {
295
+ margin: 15px 10px;
296
+ }
297
+ }
298
+
299
+ .discussion-new-message-preview-button {
300
+ flex-grow: 0.2;
301
+ }
302
+
290
303
  .discussion-new-message-button {
291
- border-radius: 0;
292
- margin-top: 15px;
304
+ flex-grow: 1;
293
305
  }
294
306
 
295
307
  .discussion-message {
@@ -0,0 +1,84 @@
1
+ .mu-faqs-container {
2
+ position: relative;
3
+ display: table;
4
+ width: 100%;
5
+
6
+ .mu-faqs-content {
7
+ padding-top: 50px;
8
+
9
+ width: 100%;
10
+
11
+ @media (min-width: 768px) {
12
+ width: 75%;
13
+ float: right;
14
+ }
15
+
16
+ .mu-faqs-group {
17
+ padding: 32px;
18
+ box-shadow: 0 0.375em 2.8125em 0 #d2d5d9;
19
+ margin-bottom: 32px;
20
+
21
+ @media (max-width: 767px) {
22
+ &:not(.active) {
23
+ h3, p {
24
+ display: none
25
+ }
26
+ h2 {
27
+ margin-bottom: 0px;
28
+ }
29
+ }
30
+ }
31
+
32
+ h2 {
33
+ margin-top: 0;
34
+ margin-bottom: 20px;
35
+ }
36
+
37
+ h3 {
38
+ color: #1A94A3; // should be $brand-secondary
39
+ margin-top: 40px;
40
+ }
41
+
42
+ .mu-faqs-group-icon {
43
+ float: right;
44
+ margin-top: 10px;
45
+ cursor: pointer;
46
+ display: none;
47
+ @media (max-width: 767px) {
48
+ display: unset;
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ .mu-faqs-navbar {
55
+ top: 0;
56
+ position: sticky;
57
+ width: 25%;
58
+ padding-top: 50px;
59
+
60
+ ul {
61
+ border-left: 1px solid $mu-color-disabled;
62
+ list-style: none;
63
+ padding-left: 0px;
64
+ li {
65
+ padding: 8px 10px 8px 15px;
66
+ cursor: pointer;
67
+ &.active {
68
+ border-left: 3px solid transparent;
69
+ border-color: darken($brand-primary, 5%);
70
+ margin-left: -3px;
71
+ }
72
+ a {
73
+ color: $brand-primary;
74
+ &:hover {
75
+ text-decoration: none;
76
+ }
77
+ @media (min-width: $container-lg) {
78
+ font-size: 1.1em;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }