Skip to content

Commit c7777fb

Browse files
efuquenMarkusBordihn
authored andcommitted
Create dialogue to open Classroom assignments. (#159)
* add api calls to google classroom * displays an empty classroom course dialog * show courses in list of classroom dialog * fixed decorate on classroom * Allow opening courses and course work in dialog * opens Drive file after selecting coursework * show multiple files per course * fix expand & collapse to store state * filter only openable mime types or extensions * remove console.logs * fix missing return from jsdoc
1 parent cbbdefc commit c7777fb

File tree

12 files changed

+599
-62
lines changed

12 files changed

+599
-62
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
.nyc_output
66
.project
77
.settings
8+
.idea
89
OWNERS
910
Thumbs.db
1011
coverage

app/chrome_app/manifest.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
"https://www.googleapis.com/auth/cloud-platform.read-only",
2020
"https://www.googleapis.com/auth/devstorage.full_control",
2121
"https://www.googleapis.com/auth/devstorage.read_only",
22-
"https://www.googleapis.com/auth/devstorage.read_write"
22+
"https://www.googleapis.com/auth/devstorage.read_write",
23+
"https://www.googleapis.com/auth/classroom.courses.readonly",
24+
"https://www.googleapis.com/auth/classroom.coursework.me.readonly"
2325
]
2426
},
2527
"icons": {

src/config/gdrive.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
goog.provide('cwc.config.GDrive');
2323

24+
goog.require('cwc.utils.mime.Type');
25+
2426

2527
/**
2628
* Google Drive config
@@ -31,19 +33,31 @@ cwc.config.GDrive = {
3133
ORDER_BY: 'folder,modifiedTime desc,name',
3234
FILE_FIELDS: 'files(id,mimeType,parents,iconLink,modifiedTime,name,owners)',
3335
FOLDER_MIME_TYPE: 'application/vnd.google-apps.folder',
34-
EXT_TO_MIME_TYPE: {
35-
'py': 'text/python',
36-
'html': 'text/html',
37-
'cwc': 'application/cwc',
38-
},
36+
OPENABLE_MIME_TYPES: [
37+
cwc.utils.mime.Type.CWC,
38+
cwc.utils.mime.Type.JAVASCRIPT,
39+
cwc.utils.mime.Type.TEXT,
40+
cwc.utils.mime.Type.HTML,
41+
cwc.utils.mime.Type.COFFEESCRIPT,
42+
cwc.utils.mime.Type.PYTHON,
43+
cwc.utils.mime.Type.CSS,
44+
],
45+
EXT_TO_MIME_TYPE: {},
3946
MIME_TYPES: [
40-
'text/python',
41-
'text/html',
4247
'application/cwc',
4348
],
4449
ACCEPTED_MIME_TYPE_QUERY: '(' +
45-
'mimeType = \'text/python\' or ' +
46-
'mimeType = \'text/html\' or ' +
47-
'mimeType = \'application/cwc\' or ' +
48-
'mimeType = \'application/vnd.google-apps.folder\')',
50+
'mimeType = \'application/cwc\'',
4951
};
52+
53+
cwc.config.GDrive.OPENABLE_MIME_TYPES.forEach( (mime) => {
54+
cwc.config.GDrive.MIME_TYPES.push(mime.type);
55+
cwc.config.GDrive.ACCEPTED_MIME_TYPE_QUERY += (
56+
' or mimeType =\'' + mime.type + '\'');
57+
mime.ext.forEach( (ext) => {
58+
cwc.config.GDrive.EXT_TO_MIME_TYPE[ext] = mime.type;
59+
});
60+
});
61+
62+
cwc.config.GDrive.ACCEPTED_MIME_TYPE_QUERY += (
63+
' or mimeType = \'application/vnd.google-apps.folder\')');

src/ui/account.js

Lines changed: 83 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -176,16 +176,33 @@ cwc.ui.Account.prototype.setAuthentication = function(authenticated) {
176176
menuBarInstance.setAuthenticated(authenticated);
177177
}
178178

179+
if (!authenticated) {
180+
this.accessToken = '';
181+
}
182+
this.authenticated = authenticated;
183+
179184
let navigationInstance = this.helper.getInstance('navigation');
180185
if (navigationInstance) {
181186
navigationInstance.enableOpenGoogleDriveFile(authenticated);
182187
navigationInstance.enableSaveGoogleDriveFile(authenticated);
188+
navigationInstance.enableOpenGoogleClassroom(authenticated);
189+
190+
if (authenticated) {
191+
this.request({
192+
subdomain: 'classroom',
193+
path: '/v1/courses',
194+
params: {
195+
'studentId': 'me',
196+
},
197+
}, function(response) {
198+
if (Object.keys(response).length > 0) {
199+
navigationInstance.enableOpenGoogleClassroom(true);
200+
}
201+
});
202+
} else {
203+
navigationInstance.enableOpenGoogleClassroom(false);
204+
}
183205
}
184-
185-
if (!authenticated) {
186-
this.accessToken = '';
187-
}
188-
this.authenticated = authenticated;
189206
};
190207

191208

@@ -200,58 +217,75 @@ cwc.ui.Account.prototype.setAuthentication = function(authenticated) {
200217
* - token: Authorization bearer token.
201218
* - raw: if true opts.path becomes the entire URI.
202219
* @param {function(?)=} callback Called when http request completes.
220+
* @return {Promise} to wait on the completion of the http request.
203221
*/
204222
cwc.ui.Account.prototype.request = function(opts, callback) {
205223
let params = opts.params || {};
224+
let subdomain = 'www';
225+
if (opts.subdomain && typeof(opts.subdomain) === 'string' &&
226+
opts.subdomain.match(/^[0-9a-zA-Z]+$/)) {
227+
subdomain = opts.subdomain;
228+
}
206229

207-
let handleRequest = (function() {
208-
let subdomain = 'www';
209-
if (opts.subdomain && typeof(opts.subdomain) === 'string' &&
210-
opts.subdomain.match(/^[0-9a-zA-Z]+$/)) {
211-
subdomain = opts.subdomain;
212-
}
230+
let uri = subdomain + '.googleapis.com';
231+
let url = goog.Uri.create('https', null, uri, null, opts.path);
232+
if (opts.raw) {
233+
url = new goog.Uri(opts.path);
234+
}
235+
let method = opts.method || 'GET';
236+
let content = opts.content;
237+
let token = opts.token || this.accessToken || '';
213238

214-
let uri = subdomain + '.googleapis.com';
215-
let url = goog.Uri.create('https', null, uri, null, opts.path);
216-
if (opts.raw) {
217-
url = new goog.Uri(opts.path);
239+
for (let i in params) {
240+
if (Object.prototype.hasOwnProperty.call(params, i)) {
241+
url.setParameterValue(i, params[i]);
218242
}
219-
let method = opts.method || 'GET';
220-
let content = opts.content;
221-
let token = opts.token || this.accessToken || '';
243+
}
222244

223-
for (let i in params) {
224-
if (Object.prototype.hasOwnProperty.call(params, i)) {
225-
url.setParameterValue(i, params[i]);
245+
let headers = new Map(Object.entries(opts.header || {}));
246+
headers.set('Authorization', 'Bearer ' + token);
247+
headers.set('X-JavaScript-User-Agent', 'Coding with Chrome');
248+
249+
return new Promise((resolve, reject) => {
250+
let handleRequest = (function() {
251+
let xhrRepsonseEvent = (event) => {
252+
this.handleXhrResponse(event, (response) => {
253+
if (callback) {
254+
callback(response);
255+
}
256+
resolve(response);
257+
});
258+
};
259+
260+
let xhrErrorEvent = (event) => {
261+
this.handleXhrError(event);
262+
reject(event);
263+
};
264+
265+
let xhrTimeoutEvent = (event) => {
266+
this.handleXhrTimeout(event);
267+
reject(event);
268+
};
269+
270+
/** @type {goog.net.XhrIo} */
271+
let xhr = new goog.net.XhrIo();
272+
goog.events.listen(xhr, goog.net.EventType.COMPLETE, xhrRepsonseEvent,
273+
false, this);
274+
goog.events.listen(xhr, goog.net.EventType.ERROR, xhrErrorEvent,
275+
false, this);
276+
goog.events.listen(xhr, goog.net.EventType.TIMEOUT, xhrTimeoutEvent,
277+
false, this);
278+
279+
this.log_.info('Request: ' + method + ' ' + url);
280+
xhr.send(url, method, content, headers);
281+
}).bind(this);
282+
283+
if (!this.authenticated) {
284+
this.authenticate(handleRequest);
285+
} else {
286+
handleRequest();
226287
}
227-
}
228-
229-
let headers = new Map(Object.entries(opts.header || {}));
230-
headers.set('Authorization', 'Bearer ' + token);
231-
headers.set('X-JavaScript-User-Agent', 'Coding with Chrome');
232-
233-
let xhrRepsonseEvent = function(event) {
234-
this.handleXhrResponse(event, callback);
235-
};
236-
237-
/** @type {goog.net.XhrIo} */
238-
let xhr = new goog.net.XhrIo();
239-
goog.events.listen(xhr, goog.net.EventType.COMPLETE, xhrRepsonseEvent,
240-
false, this);
241-
goog.events.listen(xhr, goog.net.EventType.ERROR, this.handleXhrError,
242-
false, this);
243-
goog.events.listen(xhr, goog.net.EventType.TIMEOUT, this.handleXhrTimeout,
244-
false, this);
245-
246-
this.log_.info('Request: ' + method + ' ' + url);
247-
xhr.send(url, method, content, headers);
248-
}).bind(this);
249-
250-
if (!this.authenticated) {
251-
this.authenticate(handleRequest);
252-
} else {
253-
handleRequest();
254-
}
288+
});
255289
};
256290

257291

src/ui/builder.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ goog.require('cwc.ui.Console');
4646
goog.require('cwc.ui.Debug');
4747
goog.require('cwc.ui.Documentation');
4848
goog.require('cwc.ui.Experimental');
49+
goog.require('cwc.ui.GClassroom');
4950
goog.require('cwc.ui.GCloud');
5051
goog.require('cwc.ui.GDrive');
5152
goog.require('cwc.ui.Gui');
@@ -137,6 +138,7 @@ cwc.ui.supportedProtocols = {
137138
*/
138139
cwc.ui.oauth2Helpers = {
139140
'account': cwc.ui.Account,
141+
'gclassroom': cwc.ui.GClassroom,
140142
'gcloud': cwc.ui.GCloud,
141143
'gdrive': cwc.ui.GDrive,
142144
};

src/ui/gclassroom/gclassroom.gss

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Copyright 2018 The Coding with Chrome Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* @author [email protected] (Edwin Fuquen)
17+
*/
18+
19+
#{$prefix}gclassroom-content ul li:hover {
20+
background-color: rgba(53,104,89, 0.04);
21+
cursor: pointer;
22+
}
23+
24+
.{$prefix}gclassroom-course_work {
25+
flex-direction: column;
26+
align-items: start;
27+
}

0 commit comments

Comments
 (0)