diff --git a/cms/static/coffee/fixtures/tabs-edit.html b/cms/static/coffee/fixtures/tabs-edit.html
new file mode 100644
index 000000000000..c83a1456226c
--- /dev/null
+++ b/cms/static/coffee/fixtures/tabs-edit.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+ Transcripts
+
+
+ Subtitles
+
+
+
+
+
+
+
+
+
diff --git a/cms/static/coffee/spec/tabs/edit.coffee b/cms/static/coffee/spec/tabs/edit.coffee
new file mode 100644
index 000000000000..734e398c743d
--- /dev/null
+++ b/cms/static/coffee/spec/tabs/edit.coffee
@@ -0,0 +1,95 @@
+describe "TabsEditingDescriptor", ->
+ beforeEach ->
+ @isInactiveClass = "is-inactive"
+ @isCurrent = "current"
+ loadFixtures 'tabs-edit.html'
+ @descriptor = new TabsEditingDescriptor($('.base_wrapper'))
+ @html_id = 'test_id'
+ @tab_0_switch = jasmine.createSpy('tab_0_switch');
+ @tab_0_modelUpdate = jasmine.createSpy('tab_0_modelUpdate');
+ @tab_1_switch = jasmine.createSpy('tab_1_switch');
+ @tab_1_modelUpdate = jasmine.createSpy('tab_1_modelUpdate');
+ TabsEditingDescriptor.Model.addModelUpdate(@html_id, 'Tab 0 Editor', @tab_0_modelUpdate)
+ TabsEditingDescriptor.Model.addOnSwitch(@html_id, 'Tab 0 Editor', @tab_0_switch)
+ TabsEditingDescriptor.Model.addModelUpdate(@html_id, 'Tab 1 Transcripts', @tab_1_modelUpdate)
+ TabsEditingDescriptor.Model.addOnSwitch(@html_id, 'Tab 1 Transcripts', @tab_1_switch)
+
+ spyOn($.fn, 'hide').andCallThrough()
+ spyOn($.fn, 'show').andCallThrough()
+ spyOn(TabsEditingDescriptor.Model, 'initialize')
+ spyOn(TabsEditingDescriptor.Model, 'updateValue')
+
+ afterEach ->
+ TabsEditingDescriptor.Model.modules= {}
+
+ describe "constructor", ->
+ it "first tab should be visible", ->
+ expect(@descriptor.$tabs.first()).toHaveClass(@isCurrent)
+ expect(@descriptor.$content.first()).not.toHaveClass(@isInactiveClass)
+
+ describe "onSwitchEditor", ->
+ it "switching tabs changes styles", ->
+ @descriptor.$tabs.eq(1).trigger("click")
+ expect(@descriptor.$tabs.eq(0)).not.toHaveClass(@isCurrent)
+ expect(@descriptor.$content.eq(0)).toHaveClass(@isInactiveClass)
+ expect(@descriptor.$tabs.eq(1)).toHaveClass(@isCurrent)
+ expect(@descriptor.$content.eq(1)).not.toHaveClass(@isInactiveClass)
+ expect(@tab_1_switch).toHaveBeenCalled()
+
+ it "if click on current tab, nothing should happen", ->
+ spyOn($.fn, 'trigger').andCallThrough()
+ currentTab = @descriptor.$tabs.filter('.' + @isCurrent)
+ @descriptor.$tabs.eq(0).trigger("click")
+ expect(@descriptor.$tabs.filter('.' + @isCurrent)).toEqual(currentTab)
+ expect($.fn.trigger.calls.length).toEqual(1)
+
+ it "onSwitch function call", ->
+ @descriptor.$tabs.eq(1).trigger("click")
+ expect(TabsEditingDescriptor.Model.updateValue).toHaveBeenCalled()
+ expect(@tab_1_switch).toHaveBeenCalled()
+
+ describe "save", ->
+ it "function for current tab should be called", ->
+ @descriptor.$tabs.eq(1).trigger("click")
+ data = @descriptor.save().data
+ expect(@tab_1_modelUpdate).toHaveBeenCalled()
+
+ it "detach click event", ->
+ spyOn($.fn, "off")
+ @descriptor.save()
+ expect($.fn.off).toHaveBeenCalledWith(
+ 'click',
+ '.editor-tabs .tab',
+ @descriptor.onSwitchEditor
+ )
+
+ describe "editor/settings header", ->
+ it "is hidden", ->
+ expect(@descriptor.element.find(".component-edit-header").css('display')).toEqual('none')
+
+describe "TabsEditingDescriptor special save cases", ->
+ beforeEach ->
+ @isInactiveClass = "is-inactive"
+ @isCurrent = "current"
+ loadFixtures 'tabs-edit.html'
+ @descriptor = new window.TabsEditingDescriptor($('.base_wrapper'))
+ @html_id = 'test_id'
+
+ describe "save", ->
+ it "case: no init", ->
+ data = @descriptor.save().data
+ expect(data).toEqual(null)
+
+ it "case: no function in model update", ->
+ TabsEditingDescriptor.Model.initialize(@html_id)
+ data = @descriptor.save().data
+ expect(data).toEqual(null)
+
+ it "case: no function in model update, but value presented", ->
+ @tab_0_modelUpdate = jasmine.createSpy('tab_0_modelUpdate').andReturn(1)
+ TabsEditingDescriptor.Model.addModelUpdate(@html_id, 'Tab 0 Editor', @tab_0_modelUpdate)
+ @descriptor.$tabs.eq(1).trigger("click")
+ expect(@tab_0_modelUpdate).toHaveBeenCalled()
+ data = @descriptor.save().data
+ expect(data).toEqual(1)
+
diff --git a/cms/templates/widgets/tabs-aggregator.html b/cms/templates/widgets/tabs-aggregator.html
new file mode 100644
index 000000000000..d5953005b530
--- /dev/null
+++ b/cms/templates/widgets/tabs-aggregator.html
@@ -0,0 +1,21 @@
+<%! from django.utils.translation import ugettext as _ %>
+
+
+
+
+ % for tab in tabs:
+
+ <%include file="${tab['template']}" args="tabName=tab['name']"/>
+
+ % endfor
+
+
+
+
diff --git a/cms/templates/widgets/tabs/metadata-edit-tab.html b/cms/templates/widgets/tabs/metadata-edit-tab.html
new file mode 100644
index 000000000000..4fc8314afd52
--- /dev/null
+++ b/cms/templates/widgets/tabs/metadata-edit-tab.html
@@ -0,0 +1,24 @@
+<%namespace name='static' file='../../static_content.html'/>
+<%
+ import json
+%>
+
+## js templates
+
+
+
+
+
+
+
+
+
+
diff --git a/cms/templates/widgets/videoalpha/codemirror-edit.html b/cms/templates/widgets/videoalpha/codemirror-edit.html
new file mode 100644
index 000000000000..10c28d829c56
--- /dev/null
+++ b/cms/templates/widgets/videoalpha/codemirror-edit.html
@@ -0,0 +1,33 @@
+<%! from django.utils.translation import ugettext as _ %>
+<%page args="tabName"/>
+
+
+
+
+
+
diff --git a/cms/templates/widgets/videoalpha/subtitles.html b/cms/templates/widgets/videoalpha/subtitles.html
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/common/lib/xmodule/test_files/test_tabseditingdescriptor.css b/common/lib/xmodule/test_files/test_tabseditingdescriptor.css
new file mode 100644
index 000000000000..eba23dedfdc7
--- /dev/null
+++ b/common/lib/xmodule/test_files/test_tabseditingdescriptor.css
@@ -0,0 +1,4 @@
+.supertestclass{
+ color: red;
+}
+
diff --git a/common/lib/xmodule/test_files/test_tabseditingdescriptor.scss b/common/lib/xmodule/test_files/test_tabseditingdescriptor.scss
new file mode 100644
index 000000000000..eba23dedfdc7
--- /dev/null
+++ b/common/lib/xmodule/test_files/test_tabseditingdescriptor.scss
@@ -0,0 +1,4 @@
+.supertestclass{
+ color: red;
+}
+
diff --git a/common/lib/xmodule/xmodule/css/tabs/codemirror.scss b/common/lib/xmodule/xmodule/css/tabs/codemirror.scss
new file mode 100644
index 000000000000..0b10a1a09dc5
--- /dev/null
+++ b/common/lib/xmodule/xmodule/css/tabs/codemirror.scss
@@ -0,0 +1,19 @@
+.editor{
+ @include clearfix();
+
+ .CodeMirror {
+ @include box-sizing(border-box);
+ width: 100%;
+ position: relative;
+ height: 379px;
+ border: 1px solid #3c3c3c;
+ border-top: 1px solid #8891a1;
+ background: $white;
+ color: #3c3c3c;
+ }
+
+ .CodeMirror-scroll {
+ height: 100%;
+ }
+}
+
diff --git a/common/lib/xmodule/xmodule/css/tabs/tabs.scss b/common/lib/xmodule/xmodule/css/tabs/tabs.scss
new file mode 100644
index 000000000000..3991bbad2327
--- /dev/null
+++ b/common/lib/xmodule/xmodule/css/tabs/tabs.scss
@@ -0,0 +1,137 @@
+// styles duped from _unit.scss - Edit Header (Component Name, Mode-Editor, Mode-Settings)
+
+
+.tabs-wrapper{
+ padding-top: 0;
+ position: relative;
+
+ .wrapper-comp-settings {
+ // set visibility to metadata editor
+ display: block;
+ }
+}
+
+.editor-single-tab-name {
+ display: none;
+}
+
+
+.editor-with-tabs {
+ @include clearfix();
+ position: relative;
+
+
+ .edit-header {
+ @include box-sizing(border-box);
+ padding: 18px 0 18px $baseline;
+ top: 0 !important; // ugly override for second level tab override
+ right: 0;
+ background-color: $blue;
+ border-bottom: 1px solid $blue-d2;
+ color: $white;
+
+ //Component Name
+ .component-name {
+ @extend .t-copy-sub1;
+ position: relative;
+ top: 0;
+ left: 0;
+ width: 50%;
+ color: $white;
+ font-weight: 600;
+
+
+
+ em {
+ display: inline-block;
+ margin-right: ($baseline/4);
+ font-weight: 400;
+ color: $white;
+ }
+ }
+
+ //Nav-Edit Modes
+ .editor-tabs {
+ list-style: none;
+ right: 0;
+ top: ($baseline/4);
+ position: absolute;
+ padding: 12px ($baseline*0.75);
+
+ .inner_tab_wrap {
+ display: inline-block;
+ margin-left: 8px;
+
+ a.tab {
+ @include font-size(14);
+ @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0));
+ border: 1px solid $blue-d1;
+ border-radius: 3px;
+ padding: ($baseline/4) ($baseline);
+ background-color: $blue;
+ font-weight: bold;
+ color: $white;
+
+ &.current {
+ @include linear-gradient($blue, $blue);
+ color: $blue-d1;
+ box-shadow: inset 0 1px 2px 1px $shadow-l1;
+ background-color: $blue-d4;
+ cursor: default;
+ }
+
+ &:hover {
+ box-shadow: inset 0 1px 2px 1px $shadow;
+ background-image: linear-gradient(#009FE6, #009FE6) !important;
+ }
+ }
+ }
+ }
+ }
+
+ .is-inactive {
+ display: none;
+ }
+
+ .comp-subtitles-entry {
+ text-align: center;
+
+ .file-upload {
+ display: none;
+ }
+
+ .comp-subtitles-import-list {
+ > li {
+ display: block;
+ margin: $baseline/2 0px $baseline/2 0;
+ }
+
+ .blue-button {
+ font-size: 1em;
+ display: block;
+ width: 70%;
+ margin: 0 auto;
+ text-align: center;
+ }
+ }
+ }
+
+
+}
+
+.component-tab {
+ background: $white;
+ position: relative;
+ border-top: 1px solid #8891a1;
+
+ advanced {
+ padding: 0;
+ border: none;
+ }
+
+ .blue-button {
+ @include blue-button;
+ }
+}
+
+
diff --git a/common/lib/xmodule/xmodule/css/videoalpha/common_tabs_edit.scss b/common/lib/xmodule/xmodule/css/videoalpha/common_tabs_edit.scss
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/common/lib/xmodule/xmodule/editing_module.py b/common/lib/xmodule/xmodule/editing_module.py
index df4ebc564613..eed8fb310f5e 100644
--- a/common/lib/xmodule/xmodule/editing_module.py
+++ b/common/lib/xmodule/xmodule/editing_module.py
@@ -1,3 +1,5 @@
+"""Descriptors for XBlocks/Xmodules, that provide editing of atrributes"""
+
from pkg_resources import resource_string
from xmodule.mako_module import MakoModuleDescriptor
from xblock.core import Scope, String
@@ -7,6 +9,7 @@
class EditingFields(object):
+ """Contains specific template information (the raw data body)"""
data = String(scope=Scope.content, default='')
@@ -29,6 +32,46 @@ def get_context(self):
return _context
+class TabsEditingDescriptor(EditingFields, MakoModuleDescriptor):
+ """
+ Module that provides a raw editing view of its data and children. It does not
+ perform any validation on its definition---just passes it along to the browser.
+
+ This class is intended to be used as a mixin.
+
+ Engine (module_edit.js) wants for metadata editor
+ template to be always loaded, so don't forget to include
+ settings tab in your module descriptor.
+ """
+ mako_template = "widgets/tabs-aggregator.html"
+ css = {'scss': [resource_string(__name__, 'css/tabs/tabs.scss')]}
+ js = {'coffee': [resource_string(
+ __name__, 'js/src/tabs/tabs-aggregator.coffee')]}
+ js_module_name = "TabsEditingDescriptor"
+ tabs = []
+
+ def get_context(self):
+ _context = super(TabsEditingDescriptor, self).get_context()
+ _context.update({
+ 'tabs': self.tabs,
+ 'html_id': self.location.html_id(), # element_id
+ 'data': self.data,
+ })
+ return _context
+
+ @classmethod
+ def get_css(cls):
+ # load every tab's css
+ for tab in cls.tabs:
+ tab_styles = tab.get('css', {})
+ for css_type, css_content in tab_styles.items():
+ if css_type in cls.css:
+ cls.css[css_type].extend(css_content)
+ else:
+ cls.css[css_type] = css_content
+ return cls.css
+
+
class XMLEditingDescriptor(EditingDescriptor):
"""
Module that provides a raw editing view of its data as XML. It does not perform
diff --git a/common/lib/xmodule/xmodule/js/src/tabs/tabs-aggregator.coffee b/common/lib/xmodule/xmodule/js/src/tabs/tabs-aggregator.coffee
new file mode 100644
index 000000000000..7c6e85e34b04
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/tabs/tabs-aggregator.coffee
@@ -0,0 +1,125 @@
+class @TabsEditingDescriptor
+ @isInactiveClass : "is-inactive"
+
+ constructor: (element) ->
+ @element = element;
+ ###
+ Not tested on syncing of multiple editors of same type in tabs
+ (Like many CodeMirrors).
+ ###
+
+ # hide editor/settings bar
+ $('.component-edit-header').hide()
+
+ @$tabs = $(".tab", @element)
+ @$content = $(".component-tab", @element)
+
+ @element.find('.editor-tabs .tab').each (index, value) =>
+ $(value).on('click', @onSwitchEditor)
+
+ # If default visible tab is not setted or if were marked as current
+ # more than 1 tab just first tab will be shown
+ currentTab = @$tabs.filter('.current')
+ currentTab = @$tabs.first() if currentTab.length isnt 1
+ @html_id = @$tabs.closest('.wrapper-comp-editor').data('html_id')
+ currentTab.trigger("click", [true, @html_id])
+
+ onSwitchEditor: (e, firstTime, html_id) =>
+ e.preventDefault();
+
+ isInactiveClass = TabsEditingDescriptor.isInactiveClass
+ $currentTarget = $(e.currentTarget)
+
+ if not $currentTarget.hasClass('current') or firstTime is true
+ previousTab = null
+
+ @$tabs.each( (index, value) ->
+ if $(value).hasClass('current')
+ previousTab = $(value).html()
+ )
+
+ # init and save data from previous tab
+ TabsEditingDescriptor.Model.updateValue(@html_id, previousTab)
+
+ # Save data from editor in previous tab to editor in current tab here.
+ # (to be implemented when there is a use case for this functionality)
+
+ # call onswitch
+ onSwitchFunction = TabsEditingDescriptor.Model.modules[@html_id].tabSwitch[$currentTarget.text()]
+ onSwitchFunction() if $.isFunction(onSwitchFunction)
+
+ @$tabs.removeClass('current')
+ $currentTarget.addClass('current')
+
+ # Tabs are implemeted like anchors. Therefore we can use hash to find
+ # corresponding content
+ content_id = $currentTarget.attr('href')
+
+ @$content
+ .addClass(isInactiveClass)
+ .filter(content_id)
+ .removeClass(isInactiveClass)
+
+ save: ->
+ @element.off('click', '.editor-tabs .tab', @onSwitchEditor)
+ current_tab = @$tabs.filter('.current').html()
+ data: TabsEditingDescriptor.Model.getValue(@html_id, current_tab)
+
+ @Model :
+ addModelUpdate : (id, tabName, modelUpdateFunction) ->
+ ###
+ Function that registers 'modelUpdate' functions of every tab.
+ These functions are used to update value, which will be returned
+ by calling save on component.
+ ###
+ @initialize(id)
+ @modules[id].modelUpdate[tabName] = modelUpdateFunction
+
+ addOnSwitch : (id, tabName, onSwitchFunction) ->
+ ###
+ Function that registers functions invoked when switching
+ to particular tab.
+ ###
+ @initialize(id)
+ @modules[id].tabSwitch[tabName] = onSwitchFunction
+
+ updateValue : (id, tabName) ->
+ ###
+ Function that invokes when switching tabs.
+ It ensures that data from previous tab is stored.
+ If new tab need this data, it should retrieve it from
+ stored value.
+ ###
+ @initialize(id)
+ modelUpdateFunction = @modules[id]['modelUpdate'][tabName]
+ @modules[id]['value'] = modelUpdateFunction() if $.isFunction(modelUpdateFunction)
+
+ getValue : (id, tabName) ->
+ ###
+ Retrieves stored data on component save.
+ 1. When we switching tabs - previous tab data is always saved to @[id].value
+ 2. If current tab have registered 'modelUpdate' method, it should be invoked 1st.
+ (If we have edited in 1st tab, then switched to 2nd, 2nd tab should
+ care about getting data from @[id].value in onSwitch.)
+ ###
+ if not @modules[id]
+ return null
+ if $.isFunction(@modules[id]['modelUpdate'][tabName])
+ return @modules[id]['modelUpdate'][tabName]()
+ else
+ if typeof @modules[id]['value'] is 'undefined'
+ return null
+ else
+ return @modules[id]['value']
+
+ # html_id's of descriptors will be stored in modules variable as
+ # containers for callbacks.
+ modules: {}
+
+ initialize : (id) ->
+ ###
+ Initialize objects per id. Id is html_id of descriptor.
+ ###
+ @modules[id] = @modules[id] or {}
+ @modules[id].tabSwitch = @modules[id]['tabSwitch'] or {}
+ @modules[id].modelUpdate = @modules[id]['modelUpdate'] or {}
diff --git a/common/lib/xmodule/xmodule/tests/test_editing_module.py b/common/lib/xmodule/xmodule/tests/test_editing_module.py
new file mode 100644
index 000000000000..03e257940f9b
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_editing_module.py
@@ -0,0 +1,63 @@
+""" Tests for editing descriptors"""
+import unittest
+import os
+import logging
+
+from mock import Mock
+from pkg_resources import resource_string
+from xmodule.editing_module import TabsEditingDescriptor
+
+from .import get_test_system
+
+log = logging.getLogger(__name__)
+
+
+class TabsEditingDescriptorTestCase(unittest.TestCase):
+ """ Testing TabsEditingDescriptor"""
+
+ def setUp(self):
+ super(TabsEditingDescriptorTestCase, self).setUp()
+ system = get_test_system()
+ system.render_template = Mock(return_value="Test Template HTML
")
+ self.tabs = [
+ {
+ 'name': "Test_css",
+ 'template': "tabs/codemirror-edit.html",
+ 'current': True,
+ 'css': {
+ 'scss': [resource_string(__name__,
+ '../../test_files/test_tabseditingdescriptor.scss')],
+ 'css': [resource_string(__name__,
+ '../../test_files/test_tabseditingdescriptor.css')]
+ }
+ },
+ {
+ 'name': "Subtitles",
+ 'template': "videoalpha/subtitles.html",
+ },
+ {
+ 'name': "Settings",
+ 'template': "tabs/video-metadata-edit-tab.html"
+ }
+ ]
+
+ TabsEditingDescriptor.tabs = self.tabs
+ self.descriptor = TabsEditingDescriptor(
+ runtime=system,
+ model_data={})
+
+ def test_get_css(self):
+ """test get_css"""
+ css = self.descriptor.get_css()
+ test_files_dir = os.path.dirname(__file__).replace('xmodule/tests', 'test_files')
+ test_css_file = os.path.join(test_files_dir, 'test_tabseditingdescriptor.scss')
+ with open(test_css_file) as new_css:
+ added_css = new_css.read()
+ self.assertEqual(css['scss'].pop(), added_css)
+ self.assertEqual(css['css'].pop(), added_css)
+
+ def test_get_context(self):
+ """"test get_context"""
+ rendered_context = self.descriptor.get_context()
+ self.assertListEqual(rendered_context['tabs'], self.tabs)
+
diff --git a/common/lib/xmodule/xmodule/tests/test_videoalpha.py b/common/lib/xmodule/xmodule/tests/test_videoalpha.py
index f2258d1b55bd..659bfb2fa06d 100644
--- a/common/lib/xmodule/xmodule/tests/test_videoalpha.py
+++ b/common/lib/xmodule/xmodule/tests/test_videoalpha.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""Test for Video Alpha Xmodule functional logic.
-These tests data readed from xml, not from mongo.
+These tests data readed from xml or from mongo.
we have a ModuleStoreTestCase class defined in
common/lib/xmodule/xmodule/modulestore/tests/django_utils.py. You can
@@ -12,10 +12,12 @@
the course, section, subsection, unit, etc.
"""
+import unittest
from xmodule.videoalpha_module import VideoAlphaDescriptor
from . import LogicTest
from lxml import etree
-
+from pkg_resources import resource_string
+from .import get_test_system
class VideoAlphaModuleTest(LogicTest):
"""Logic tests for VideoAlpha Xmodule."""
@@ -49,3 +51,31 @@ def test_get_timeframe_with_two_parameters(self):
)
output = self.xmodule.get_timeframe(xmltree)
self.assertEqual(output, (247, 47079))
+
+
+class VideoAlphaDescriptorTest(unittest.TestCase):
+ """Test for VideoAlphaDescriptor"""
+
+ def setUp(self):
+ system = get_test_system()
+ self.descriptor = VideoAlphaDescriptor(
+ runtime=system,
+ model_data={})
+
+ def test_get_context(self):
+ """"test get_context"""
+ correct_tabs = [
+ {
+ 'name': "XML",
+ 'template': "videoalpha/codemirror-edit.html",
+ 'css': {'scss': [resource_string(__name__,
+ '../css/tabs/codemirror.scss')]},
+ 'current': True,
+ },
+ {
+ 'name': "Settings",
+ 'template': "tabs/metadata-edit-tab.html"
+ }
+ ]
+ rendered_context = self.descriptor.get_context()
+ self.assertListEqual(rendered_context['tabs'], correct_tabs)
diff --git a/common/lib/xmodule/xmodule/videoalpha_module.py b/common/lib/xmodule/xmodule/videoalpha_module.py
index 4ca337dcb0db..084389411aae 100644
--- a/common/lib/xmodule/xmodule/videoalpha_module.py
+++ b/common/lib/xmodule/xmodule/videoalpha_module.py
@@ -20,7 +20,7 @@
from django.conf import settings
from xmodule.x_module import XModule
-from xmodule.raw_module import RawDescriptor
+from xmodule.editing_module import TabsEditingDescriptor
from xmodule.modulestore.mongo import MongoModuleStore
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.content import StaticContent
@@ -187,6 +187,23 @@ def get_html(self):
})
-class VideoAlphaDescriptor(VideoAlphaFields, RawDescriptor):
+class VideoAlphaDescriptor(VideoAlphaFields, TabsEditingDescriptor):
"""Descriptor for `VideoAlphaModule`."""
module_class = VideoAlphaModule
+
+ tabs = [
+ {
+ 'name': "XML",
+ 'template': "videoalpha/codemirror-edit.html",
+ 'css': {'scss': [resource_string(__name__, 'css/tabs/codemirror.scss')]},
+ 'current': True,
+ },
+ # {
+ # 'name': "Subtitles",
+ # 'template': "videoalpha/subtitles.html",
+ # },
+ {
+ 'name': "Settings",
+ 'template': "tabs/metadata-edit-tab.html"
+ }
+ ]