mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 03:53:50 +00:00 
			
		
		
		
	Add basic widgets framework (JS side).
This commit is contained in:
		| @@ -171,6 +171,7 @@ | ||||
|         "unread_ops": false, | ||||
|         "upload": false, | ||||
|         "user_events": false, | ||||
|         "widgetize": false, | ||||
|         "submessage": false, | ||||
|         "Plotly": false, | ||||
|         "emoji_codes": false, | ||||
|   | ||||
| @@ -15,7 +15,6 @@ exports.get_message_events = function (message) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // The server should sort messages for us, but this is defensive. | ||||
|     message.submessages.sort(function (m1, m2) { | ||||
|         return parseInt(m1.id, 10) - parseInt(m2.id, 10); | ||||
|     }); | ||||
| @@ -45,11 +44,52 @@ exports.process_submessages = function (in_opts) { | ||||
|     } | ||||
|  | ||||
|     blueslip.info('submessages found for message id: ' + message_id); | ||||
|  | ||||
|     var row = in_opts.row; | ||||
|  | ||||
|     // Right now, our only use of submessages is widgets. | ||||
|  | ||||
|     var data = events[0].data; | ||||
|  | ||||
|     if (data === undefined) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     var widget_type = data.widget_type; | ||||
|  | ||||
|     if (widget_type === undefined) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     var post_to_server = exports.make_server_callback(message_id); | ||||
|  | ||||
|     widgetize.activate({ | ||||
|         widget_type: widget_type, | ||||
|         extra_data: data.extra_data, | ||||
|         events: events, | ||||
|         row: row, | ||||
|         message: message, | ||||
|         post_to_server: post_to_server, | ||||
|     }); | ||||
| }; | ||||
|  | ||||
|  | ||||
| exports.handle_event = function (event) { | ||||
|     blueslip.info('handle submessage: ' + JSON.stringify(event)); | ||||
|  | ||||
|     // Right now, our only use of submessages is widgets. | ||||
|     var msg_type = event.msg_type; | ||||
|  | ||||
|     if (msg_type !== 'widget') { | ||||
|         blueslip.warn('unknown msg_type: ' + msg_type); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     widgetize.handle_event({ | ||||
|         sender_id: event.sender_id, | ||||
|         message_id: event.message_id, | ||||
|         data: event.data, | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| exports.make_server_callback = function (message_id) { | ||||
|   | ||||
							
								
								
									
										77
									
								
								static/js/widgetize.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								static/js/widgetize.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| var widgetize = (function () { | ||||
|  | ||||
| var exports = {}; | ||||
|  | ||||
| var widgets = {}; | ||||
|  | ||||
| exports.activate = function (in_opts) { | ||||
|     var widget_type = in_opts.widget_type; | ||||
|     var extra_data = in_opts.extra_data; | ||||
|     var events = in_opts.events; | ||||
|     var row = in_opts.row; | ||||
|     var message = in_opts.message; | ||||
|     var post_to_server = in_opts.post_to_server; | ||||
|  | ||||
|     events.shift(); | ||||
|  | ||||
|     if (!widgets[widget_type]) { | ||||
|         blueslip.warn('unknown widget_type', widget_type); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     var content_holder = row.find('.message_content'); | ||||
|  | ||||
|     var widget_elem; | ||||
|     if (message.widget) { | ||||
|         // Use local to work around linter.  We can trust this | ||||
|         // value because it comes from a template. | ||||
|         widget_elem = message.widget_elem; | ||||
|         content_holder.html(widget_elem); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     var callback = function (data) { | ||||
|         post_to_server({ | ||||
|             msg_type: 'widget', | ||||
|             data: data, | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     // We depend on our widgets to use templates to build | ||||
|     // the HTML that will eventually go in this div. | ||||
|     widget_elem = $('<div>'); | ||||
|     content_holder.html(widget_elem); | ||||
|  | ||||
|     var widget = widgets[widget_type].activate({ | ||||
|         elem: widget_elem, | ||||
|         callback: callback, | ||||
|         message: message, | ||||
|         extra_data: extra_data, | ||||
|     }); | ||||
|  | ||||
|     // This is hacky, we should just maintain our own list. | ||||
|     message.widget = widget; | ||||
|     message.widget_elem = widget_elem; | ||||
|  | ||||
|     // Replay any events that already happened.  (This is common | ||||
|     // when you narrow to a message after other users have already | ||||
|     // interacted with it.) | ||||
|     if (events.length > 0) { | ||||
|         widget.handle_events(events); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| exports.handle_event = function (widget_event) { | ||||
|     var message = message_store.get(widget_event.message_id); | ||||
|  | ||||
|     var events = [widget_event]; | ||||
|  | ||||
|     message.widget.handle_events(events); | ||||
| }; | ||||
|  | ||||
| return exports; | ||||
|  | ||||
| }()); | ||||
| if (typeof module !== 'undefined') { | ||||
|     module.exports = widgetize; | ||||
| } | ||||
| @@ -22,7 +22,8 @@ STATIC_PATH = 'static/' | ||||
| def get_templates(): | ||||
|     # type: () -> List[str] | ||||
|     return (glob.glob(os.path.join(STATIC_PATH, 'templates/*.handlebars')) + | ||||
|             glob.glob(os.path.join(STATIC_PATH, 'templates/settings/*.handlebars'))) | ||||
|             glob.glob(os.path.join(STATIC_PATH, 'templates/settings/*.handlebars')) + | ||||
|             glob.glob(os.path.join(STATIC_PATH, 'templates/widgets/*.handlebars'))) | ||||
|  | ||||
| def run(): | ||||
|     # type: () -> None | ||||
|   | ||||
| @@ -189,7 +189,7 @@ def build_custom_checkers(by_lang): | ||||
|         {'pattern': '\+.*i18n\.t\(.+\)', | ||||
|          'description': 'Do not concatenate i18n strings'}, | ||||
|         {'pattern': '[.]html[(]', | ||||
|          'exclude_pattern': '[.]html[(]("|\'|templates|html|message.content|sub.rendered_description|i18n.t|rendered_|$|[)]|error_text|[$]error|[$][(]"<p>"[)])', | ||||
|          'exclude_pattern': '[.]html[(]("|\'|templates|html|message.content|sub.rendered_description|i18n.t|rendered_|$|[)]|error_text|widget_elem|[$]error|[$][(]"<p>"[)])', | ||||
|          'exclude': ['static/js/portico', 'static/js/lightbox.js', 'static/js/ui_report.js', | ||||
|                      'frontend_tests/'], | ||||
|          'description': 'Setting HTML content with jQuery .html() can lead to XSS security bugs.  Consider .text() or using rendered_foo as a variable name if content comes from handlebars and thus is already sanitized.'}, | ||||
|   | ||||
| @@ -947,6 +947,7 @@ JS_SPECS = { | ||||
|             'js/top_left_corner.js', | ||||
|             'js/stream_list.js', | ||||
|             'js/filter.js', | ||||
|             'js/widgetize.js', | ||||
|             'js/submessage.js', | ||||
|             'js/fetch_status.js', | ||||
|             'js/message_list_data.js', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user