From c5fc30c58b7039d303c0ccf1e3f5f9297b4d1b7d Mon Sep 17 00:00:00 2001 From: Andy Armstrong Date: Thu, 30 Jan 2014 13:53:10 -0500 Subject: [PATCH 1/4] First rough cut of a container page. --- .../contentstore/views/component.py | 154 ++ cms/static/sass/style-app-extend1.scss | 1 + cms/static/sass/views/_container.scss | 1260 +++++++++++++++++ cms/templates/container.html | 191 +++ cms/urls.py | 1 + 5 files changed, 1607 insertions(+) create mode 100644 cms/static/sass/views/_container.scss create mode 100644 cms/templates/container.html diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index 8c1bcb7a9d..f1aa917955 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -35,6 +35,7 @@ __all__ = ['OPEN_ENDED_COMPONENT_TYPES', 'ADVANCED_COMPONENT_POLICY_KEY', 'subsection_handler', 'unit_handler', + 'container_handler', 'component_handler' ] @@ -304,6 +305,159 @@ def unit_handler(request, tag=None, package_id=None, branch=None, version_guid=N return HttpResponseBadRequest("Only supports html requests") +@require_http_methods(["GET"]) +@login_required +def container_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None): + """ + The restful handler for unit-specific requests. + + GET + html: return html page for editing a unit + json: not currently supported + """ + if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'): + locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block) + try: + old_location, course, item, lms_link = _get_item_in_course(request, locator) + except ItemNotFoundError: + return HttpResponseBadRequest() + + component_templates = defaultdict(list) + for category in COMPONENT_TYPES: + component_class = _load_mixed_class(category) + # add the default template + # TODO: Once mixins are defined per-application, rather than per-runtime, + # this should use a cms mixed-in class. (cpennington) + if hasattr(component_class, 'display_name'): + display_name = component_class.display_name.default or 'Blank' + else: + display_name = 'Blank' + component_templates[category].append(( + display_name, + category, + False, # No defaults have markdown (hardcoded current default) + None # no boilerplate for overrides + )) + # add boilerplates + if hasattr(component_class, 'templates'): + for template in component_class.templates(): + filter_templates = getattr(component_class, 'filter_templates', None) + if not filter_templates or filter_templates(template, course): + component_templates[category].append(( + template['metadata'].get('display_name'), + category, + template['metadata'].get('markdown') is not None, + template.get('template_id') + )) + + # Check if there are any advanced modules specified in the course policy. + # These modules should be specified as a list of strings, where the strings + # are the names of the modules in ADVANCED_COMPONENT_TYPES that should be + # enabled for the course. + course_advanced_keys = course.advanced_modules + + # Set component types according to course policy file + if isinstance(course_advanced_keys, list): + for category in course_advanced_keys: + if category in ADVANCED_COMPONENT_TYPES: + # Do I need to allow for boilerplates or just defaults on the + # class? i.e., can an advanced have more than one entry in the + # menu? one for default and others for prefilled boilerplates? + try: + component_class = _load_mixed_class(category) + + component_templates['advanced'].append( + ( + component_class.display_name.default or category, + category, + False, + None # don't override default data + ) + ) + except PluginMissingError: + # dhm: I got this once but it can happen any time the + # course author configures an advanced component which does + # not exist on the server. This code here merely + # prevents any authors from trying to instantiate the + # non-existent component type by not showing it in the menu + pass + else: + log.error( + "Improper format for course advanced keys! %s", + course_advanced_keys + ) + + components = [ + loc_mapper().translate_location( + course.location.course_id, component.location, False, True + ) + for component + in item.get_children() + ] + + # TODO (cpennington): If we share units between courses, + # this will need to change to check permissions correctly so as + # to pick the correct parent subsection + + containing_subsection_locs = modulestore().get_parent_locations(old_location, None) + containing_subsection = modulestore().get_item(containing_subsection_locs[0]) + containing_section_locs = modulestore().get_parent_locations( + containing_subsection.location, None + ) + containing_section = modulestore().get_item(containing_section_locs[0]) + + # cdodge hack. We're having trouble previewing drafts via jump_to redirect + # so let's generate the link url here + + # need to figure out where this item is in the list of children as the + # preview will need this + index = 1 + for child in containing_subsection.get_children(): + if child.location == item.location: + break + index = index + 1 + + preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE') + + preview_lms_link = ( + '//{preview_lms_base}/courses/{org}/{course}/' + '{course_name}/courseware/{section}/{subsection}/{index}' + ).format( + preview_lms_base=preview_lms_base, + lms_base=settings.LMS_BASE, + org=course.location.org, + course=course.location.course, + course_name=course.location.name, + section=containing_section.location.name, + subsection=containing_subsection.location.name, + index=index + ) + + return render_to_response('container.html', { + 'context_course': course, + 'unit': item, + 'unit_locator': locator, + 'components': components, + 'component_templates': component_templates, + 'draft_preview_link': preview_lms_link, + 'published_preview_link': lms_link, + 'subsection': containing_subsection, + 'release_date': ( + get_default_time_display(containing_subsection.start) + if containing_subsection.start is not None else None + ), + 'section': containing_section, + 'new_unit_category': 'vertical', + 'unit_state': compute_unit_state(item), + 'published_date': ( + get_default_time_display(item.published_date) + if item.published_date is not None else None + ), + }) + else: + return HttpResponseBadRequest("Only supports html requests") + + @login_required def _get_item_in_course(request, locator): """ diff --git a/cms/static/sass/style-app-extend1.scss b/cms/static/sass/style-app-extend1.scss index 2702157ee6..87c022df6c 100644 --- a/cms/static/sass/style-app-extend1.scss +++ b/cms/static/sass/style-app-extend1.scss @@ -41,6 +41,7 @@ @import 'views/static-pages'; @import 'views/subsection'; @import 'views/unit'; +@import 'views/container'; @import 'views/users'; @import 'views/checklists'; @import 'views/textbooks'; diff --git a/cms/static/sass/views/_container.scss b/cms/static/sass/views/_container.scss new file mode 100644 index 0000000000..4dc66f5edc --- /dev/null +++ b/cms/static/sass/views/_container.scss @@ -0,0 +1,1260 @@ +// studio - views - container +// ========================== + +body.course.container,.view-container { + + .main-wrapper { + margin-top: ($baseline*2); + } + + //Problem Selector tab menu requirements + .js .tabs .tab { + display: none; + } + //end problem selector reqs + + .main-column { + clear: both; + float: left; + width: 70%; + } + + .unit-body.published { + .components > li { + border: none; + + .rendered-component { + padding: 0 $baseline; + } + } + } + + .unit-body { + + .unit-name-input { + padding: $baseline 2*$baseline; + + label { + display: block; + } + + input { + width: 100%; + font-size: 20px; + } + } + + .breadcrumbs { + border-radius: 3px 3px 0 0; + border-bottom: 1px solid #cbd1db; + @include linear-gradient(top, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0) 70%); + background-color: #edf1f5; + box-shadow: 0 1px 0 rgba(255, 255, 255, .7) inset; + @include clearfix; + + li { + float: left; + } + + a, + .current-page { + display: block; + padding: 15px 35px 15px 30px; + font-size: 14px; + background: url(../img/breadcrumb-arrow.png) no-repeat right center; + } + } + + h2 { + margin: 30px 40px 30px 0; + color: #646464; + font-size: 19px; + font-weight: 300; + letter-spacing: 1px; + text-transform: uppercase; + } + // ==================== + + // Component List Meta + .components { + + > li { + position: relative; + z-index: 10; + margin: $baseline 2*$baseline; + + .title { + margin: 0 0 15px 0; + color: $mediumGrey; + + .value { + } + } + + // ==================== + + // New Components + &.new-component-item { + margin: $baseline 0px; + border-top: 1px solid $mediumGrey; + box-shadow: 0 2px 1px rgba(182, 182, 182, 0.75) inset; + background-color: $lightGrey; + margin-bottom: 0px; + padding-bottom: $baseline; + + .new-component-button { + display: block; + padding: $baseline; + text-align: center; + color: #edf1f5; + } + + h5 { + margin: $baseline 0px; + color: #fff; + font-weight: 600; + font-size: 18px; + } + + .rendered-component { + display: none; + background: #fff; + border-radius: 3px 3px 0 0; + } + + .new-component-type { + + a, + li { + display: inline-block; + } + + a { + border: 1px solid $mediumGrey; + width: 100px; + height: 100px; + color: #fff; + margin-right: 15px; + margin-bottom: $baseline; + border-radius: 8px; + font-size: 15px; + line-height: 14px; + text-align: center; + box-shadow: 0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset; + + .name { + position: absolute; + bottom: 5px; + left: 0; + width: 100%; + padding: $baseline/2; + @include box-sizing(border-box); + color: #fff; + } + } + } + + .new-component-templates { + display: none; + margin: $baseline 2*$baseline; + border-radius: 3px; + border: 1px solid $mediumGrey; + background-color: #fff; + box-shadow: 0 1px 1px rgba(0, 0, 0, .2), 0 1px 0 rgba(255, 255, 255, .4) inset; + @include clearfix; + + .cancel-button { + margin: $baseline 0px $baseline/2 $baseline/2; + @include white-button; + } + + .problem-type-tabs { + display: none; + } + + // specific menu types + &.new-component-problem { + padding-bottom: $baseline/2; + + [class^="icon-"], .editor-indicator { + display: inline-block; + } + + .problem-type-tabs { + display: inline-block; + } + } + } + + .new-component-type, + .new-component-template { + @include clearfix; + + a { + position: relative; + border: 1px solid $darkGreen; + background: tint($green,20%); + color: #fff; + + &:hover { + background: $brightGreen; + } + } + } + + .problem-type-tabs { + list-style-type: none; + border-radius: 0; + width: 100%; + @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); + background-color: $lightBluishGrey; + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset; + + li:first-child { + margin-left: $baseline; + } + + li { + float:left; + display:inline-block; + text-align:center; + width: auto; + @include linear-gradient(top, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0)); + background-color: tint($lightBluishGrey, 10%); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 -1px 0 rgba(0, 0, 0, 0.2) inset; + opacity: 0.8; + + &:hover { + opacity: 0.9; + background-color: tint($lightBluishGrey, 20%); + } + + &.ui-state-active { + border: 0px; + @include active; + opacity: 1.0; + } + } + + a { + display: block; + padding: 15px 25px; + font-size: 15px; + line-height: 16px; + text-align: center; + color: #3c3c3c; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3); + } + } + + .new-component-template { + + a { + @include transition(none); + background: #fff; + border: 0px; + color: #3c3c3c; + + &:hover { + @include transition(background-color $tmg-f2 linear 0s); + background: tint($green,30%); + color: #fff; + } + } + + li { + border:none; + border-bottom: 1px dashed $lightGrey; + color: #fff; + } + + li:first-child { + a { + border-top: 0px; + } + } + + li:nth-child(2) { + a { + border-radius: 0px; + } + } + + a { + @include clearfix(); + display: block; + padding: 7px $baseline; + border-bottom: none; + font-weight: 500; + + .name { + float: left; + + [class^="icon-"] { + @include transition(opacity $tmg-f2 linear 0s); + display: inline-block; + top: 1px; + margin-right: 5px; + opacity: 0.5; + width: 17; + height: 21px; + vertical-align: middle; + } + } + + .editor-indicator { + @include transition(opacity $tmg-f2 linear 0s); + float: right; + position: relative; + top: 3px; + font-size: 12px; + opacity: 0.3; + } + + [class^="icon-"], .editor-indicator { + display: none; + } + + &:hover { + color: #fff; + + [class^="icon-"] { + opacity: 1.0; + } + + .editor-indicator { + opacity: 1.0; + } + } + } + + // specific editor types + .empty { + + a { + line-height: 1.4; + font-weight: 400; + background: #fff; + color: #3c3c3c; + + + &:hover { + background: tint($green,30%); + color: #fff; + } + } + } + } + + .new-component { + text-align: center; + + h5 { + color: $darkGreen; + } + + } + } + + .wrapper-alert-error { + margin-top: ($baseline*1.25); + box-shadow: none; + border-top: 5px solid $red-l1; + + .copy, + .title { + color: $white; + } + + } + + + } + } + + // ==================== + + // Component Drag and Drop, Non-Edit Module Rendering, Styling + .component { + border: 1px solid $lightBluishGrey2; + border-radius: 3px; + background: #fff; + @include transition(none); + + &:hover { + border-color: #6696d7; + + .drag-handle { + background-color: $blue; + border-color: $blue; + } + } + + &.editing { + border: 1px solid $lightBluishGrey2; + z-index: auto; + + .drag-handle, + .component-actions { + display: none; + } + } + + &.component-placeholder { + border-color: #6696d7; + } + + .drag-handle { + position: absolute; + display: block; + top: -1px; + right: -16px; + z-index: 10; + width: 15px; + height: 100%; + border-radius: 0 3px 3px 0; + border: 1px solid $lightBluishGrey2; + background: url(../img/white-drag-handles.png) center no-repeat $lightBluishGrey2; + cursor: move; + @include transition(none); + } + } + + .xmodule_display { + padding: 2*$baseline $baseline $baseline; + overflow-x: auto; + + h1 { + float: none; + margin-left: 0; + } + } + + // UI: DnD - specific elems/cases - unit + .courseware-unit { + + // STATE: was dropped + &.was-dropped { + + > .section-item { + background-color: $ui-update-color !important; // nasty, but needed for specificity + } + } + } + + // ==================== + + // Component Editing + .wrapper-component-editor { + z-index: 9999; + position: relative; + background: $white; + } + + .component-editor { + @include edit-box; + box-shadow: none; + display: none; + padding: 0; + border-radius: 2px 2px 0 0; + + h3 { + margin-bottom: $baseline/2; + font-size: 18px; + font-weight: 700; + } + + h5 { + margin-bottom: 8px; + color: #fff; + font-weight: 700; + } + + .row { + margin-bottom: 0px; + } + + // Module Actions, also used for Static Pages + .module-actions { + box-shadow: inset 0 1px 2px $shadow; + border-top: 1px solid $gray-l1; + padding: ($baseline*0.75) $baseline; + background: $gray-l6; + + .action { + display: inline-block; + vertical-align: middle; + margin-right: ($baseline/4); + + &:last-child { + margin-right: 0; + } + } + + .action-primary { + @include blue-button; + @extend %t-action2; + @include transition(all .15s); + display: inline-block; + padding: ($baseline/5) $baseline; + font-weight: 600; + text-transform: uppercase; + } + + .action-secondary { + @include grey-button; + @extend %t-action2; + @include transition(all .15s); + display: inline-block; + padding: ($baseline/5) $baseline; + font-weight: 600; + text-transform: uppercase; + } + } + } + } +} + +// Edit Header (Component Name, Mode-Editor, Mode-Settings) +.component-edit-header { + @include box-sizing(border-box); + padding: 18px 0 18px $baseline; + top: 0; + right: 0; + background-color: $blue; + border-bottom: 1px solid $blue-d2; + color: $white; + + //Component Name + .component-name { + @extend %t-copy-sub1; + width: 50%; + color: $white; + font-weight: 600; + + em { + display: inline-block; + margin-right: ($baseline/4); + font-weight: 400; + color: $white; + } + } + + //Nav-Edit Modes + .nav-edit-modes { + list-style: none; + right: 0; + top: 0; + position: absolute; + padding: 12px ($baseline*0.75); + + .mode { + display: inline-block; + margin-left: 8px; + + &.inactive-mode{ + display: none; + } + + &.active-mode a { + + @include blue-button; + + &.is-set { + @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; + } + } + } + } + } +} + +// Editor Wrapper +.wrapper-comp-editor { + display: block; + + // Because the editor may be a CodeMirror editor (which must be visible at the time it is created + // in order for it to properly initialize), we must toggle "is-inactive" instead of the more common "is-active". + &.is-inactive { + display: none; + } +} + +// Settings Wrapper +.wrapper-comp-settings { + display: none; + + &.is-active { + display: block; + } + + //settings-list + .list-input.settings-list { + margin: 0; + padding: 0; + list-style: none; + + overflow: auto; + max-height: 400px; + + //chrome scrollbar visibility correction + &::-webkit-scrollbar { + -webkit-appearance: none; + width: 11px; + height: 11px; + } + + &::-webkit-scrollbar-thumb { + border-radius: 8px; + border: 2px solid $gray-l2; + background-color: rgba(0, 0, 0, .5); + } + + //component-setting-entry + .field.comp-setting-entry { + background-color: $white; + padding: $baseline; + border-bottom: 1px solid $gray-l2; + opacity: 0.7; + + &:last-child { + //margin-bottom: 0; + } + + //no required component settings currently + &.required { + label { + //font-weight: 600; + } + label:after { + //margin-left: ($baseline/4); + //content: "*"; + } + } + + &:hover { + @include transition(opacity $tmg-f2 ease-in-out 0s); + opacity: 1.0; + } + + &.is-set { + opacity: 1.0; + background-color: $white; + + .setting-input { + color: $blue-l1; + } + } + + .wrapper-comp-setting { + display: inline-block; + min-width: 300px; + width: 55%; + top: 0; + vertical-align: top; + margin-bottom:5px; + position: relative; + } + + .setting-label { + @extend %t-copy-sub1; + @include transition(color $tmg-f2 ease-in-out 0s); + vertical-align: middle; + display: inline-block; + position: relative; + left: 0; + width: 33%; + min-width: 100px; + margin-right: ($baseline/2); + font-weight: 600; + + &.is-focused { + color: $blue; + } + } + + input, select, input[type="number"] { + @include placeholder($gray-l4); + @include font-size(16); + @include size(100%,100%); + padding: ($baseline/2); + min-width: 100px; + width: 45%; + + //&.long { + // + //} + + //&.short { + //} + + //&.error { + // border-color: $red; + //} + + //&:focus { + // + .tip { + // color: $gray; + // } + border-radius: 3px; + border: 1px solid $gray-l2; + text-overflow: ellipsis; + } + + //Allows users to copy full value of disabled inputs. + input.is-disabled{ + text-overflow: clip; + opacity: .5; + } + + input[type="number"] { + + width: 38.5%; + box-shadow: 0 1px 2px $shadow-l1 inset; + //For webkit browsers which render number fields differently, make input wider. + -moz-column-width: { + width: 32%; + } + + &:active { + background-color: #FFFCF1; + } + } + + select { + //box-shadow: 0 1px 2px $shadow-l1 inset; + + &:focus { + box-shadow: 0 0 1px $shadow; + @include transition(opacity $tmg-f2 ease-in-out 0s); + background-color: $yellow; + } + + &:active { + background-color: $yellow; + } + } + + .action.setting-clear { + @include font-size(11); + color: $gray; + width: 25px; + height: 25px; + vertical-align: middle; + padding: 5px; + border-radius: 50%; + margin: 0 $baseline/2; + box-shadow: none; + text-shadow: none; + border: 1px solid $gray-l1; + background-color: $gray-l4; + + &:hover { + box-shadow: 0 1px 1px $shadow; + @include transition(opacity $tmg-f2 ease-in-out 0s); + background-color: $blue-s3; + border: 1px solid $blue-s3; + color: $white; + } + + &.inactive { + visibility: hidden; + } + } + + .setting-help { + @include font-size(12); + display: inline-block; + font-color: $gray-l6; + min-width: ($baseline*10); + width: 35%; + vertical-align: top; + } + + + + // TYPE: enumerated lists of metadata sets + .metadata-list-enum { + + * { + @include box-sizing(border-box); + } + + // label + .setting-label { + vertical-align: top; + margin-top: ($baseline/2); + } + + // inputs and labels + .wrapper-list-settings { + @include size(45%,100%); + display: inline-block; + min-width: ($baseline*5); + + // enumerated fields + .list-settings { + margin: 0; + + .list-settings-item { + margin-bottom: ($baseline/2); + } + + // inputs + .input { + width: 80%; + margin-right: ($baseline/2); + vertical-align: middle; + } + } + } + + // actions + .create-action, .remove-action, .setting-clear { + + } + + .setting-clear { + vertical-align: top; + margin-top: ($baseline/4); + } + + .create-setting { + @extend %ui-btn-flat-outline; + @extend %t-action3; + display: block; + width: 100%; + padding: ($baseline/2); + font-weight: 600; + + *[class^="icon-"] { + margin-right: ($baseline/4); + } + + // STATE: disabled + &.is-disabled { + + } + } + + .remove-setting { + @include transition(color 0.25s ease-in-out); + @include font-size(20); + display: inline-block; + background: transparent; + color: $blue-l3; + + &:hover { + color: $blue; + } + + // STATE: disabled + &.is-disabled { + + } + } + } + } + } +} +// ==================== + +// Editing Units from Courseware +//uses similar styling as static-pages.scss +body.unit { + + .component { + padding-top: 30px; + + + .wrapper-component-action-header { + @include box-sizing(border-box); + position: absolute; + width: 100%; + padding: $baseline/4 $baseline/2; + top: 0; + left: 0; + border-bottom: 1px solid $gray-l4; + background: $gray-l5; + } + + .component-header { + display: inline-block; + width: 50%; + vertical-align: middle; + } + + .component-actions { + display: inline-block; + float: right; + vertical-align: middle; + text-align: center; + } + + &.editing { + padding-top: 0; + } + } +} + +// ==================== + +// Component Header Actions +// uses similar styling as assets.scss, static-pages.scss + +body.unit { + + .component-actions { + + .action-item { + display: inline-block; + margin: ($baseline/4) 0 ($baseline/4) ($baseline/4); + + .action-button { + @include transition(all $tmg-f2 ease-in-out 0s); + display: block; + padding: 0 $baseline/2; + width: auto; + height: ($baseline*1.5); + border-radius: 3px; + color: $gray-l1; + + &:hover { + background-color: $blue; + color: $gray-l6; + } + + .action-button-text { + padding-left: 1px; + vertical-align: bottom; + line-height: 17px; + } + + &.delete-button:hover { + background-color: $gray-l1; + } + } + + [class^="icon-"] { + display: inline-block; + vertical-align: bottom; + } + } + } +} + +// ==================== + +// Unit Page Sidebar + +.sidebar { + + label { + @extend %t-title8; + } +} + +.unit-settings { + + .window-contents { + padding: $baseline/2 $baseline; + } + + .unit-actions { + border-bottom: none; + padding-bottom: 0; + } + + .published-alert { + display: none; + padding: $baseline/2; + border: 1px solid #edbd3c; + border-radius: 3px; + background: #fbf6e1; + font-size: 14px; + line-height: 1.4; + + div { + margin-top: 15px; + } + } + + + + input[type="radio"] { + margin-right: 7px; + } + + .status { + @extend %t-copy-sub2; + + strong { + font-weight: 700; + } + } + + .preview-button, .view-button { + @include white-button; + margin-bottom: $baseline/2; + } + + .publish-button { + @include orange-button; + } + + .delete-button { + @include blue-button; + } + + .delete-draft { + display: inline-block; + } + + .delete-button, + .preview-button, + .publish-button, + .view-button { + font-size: 11px; + margin-top: $baseline/2; + padding: 6px 15px 8px; + } +} + +.unit-history { + &.collapsed { + h4 { + border-bottom: none; + border-radius: 3px; + } + + .window-contents { + display: none; + } + } + + ol { + border: 1px solid #ced2db; + + li { + display: block; + padding: 6px 8px 8px $baseline/2; + background: #edf1f5; + font-size: 12px; + + &:hover { + background: #fffcf1; + + .item-actions { + display: block; + } + } + + &.checked { + background: #d1dae3; + } + + .item-actions { + display: none; + } + + input[type="radio"] { + margin-right: 7px; + } + } + } +} + +.unit-location { + + // unit id + .wrapper-unit-id { + + .unit-id { + + .label { + @extend %t-title7; + margin-bottom: ($baseline/4); + color: $gray-d1; + } + + .value { + margin-bottom: 0; + } + } + } + + .url { + box-shadow: none; + width: 100%; + margin-bottom: $baseline/2; + } + + .draft-tag, + .hidden-tag, + .private-tag, + .has-new-draft-tag { + font-size: 8px; + } + + .unit-tree-location { + + .section-name { + @extend %t-title8; + } + + + .subsection, + .courseware-unit { + margin: ($baseline/4) 0 0 ($baseline); + } + + .courseware-unit .section-item { + background-color: transparent; + } + + .section-item { + @include transition(background $tmg-avg ease-in-out 0); + @include box-sizing(border-box); + @extend %t-copy-sub2; + display: inline-block; + width: 100%; + background: $gray-l5; + padding: 6px 8px 8px 16px; + vertical-align: top; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + color: $gray; + + &:hover { + background: $blue-l5; + color: $blue; + } + + &.editing { + background-color: $orange-l3; + } + + .public-item { + color: $black; + } + + .private-item { + color: $gray-l1; + } + + .draft-item { + color: $yellow-d1; + } + + .public-item:hover, + .private-item:hover, + .draft-item:hover { + color: $blue; + } + + .draft-item:after, + .public-item:after, + .private-item:after { + @include font-size(9); + margin-left: 3px; + font-weight: 600; + text-transform: uppercase; + } + + .draft-item:after { + content: "- draft"; + } + + .private-item:after { + content: "- private"; + } + } + + .new-unit-item { + @extend %ui-btn-flat-outline; + @extend %t-action4; + width: 90%; + margin: 0 0 ($baseline/2) ($baseline/4); + border: 1px solid transparent; + padding: ($baseline/4) ($baseline/2); + font-weight: normal; + color: $gray-l2; + text-align: left; + + &:hover { + box-shadow: none; + background-image: none; + } + } + } +} + +.edit-state-draft { + .visibility, + + .edit-draft-message, + .view-button { + display: none; + } + + .published-alert { + display: block; + } +} + +.edit-state-public { + .delete-draft, + .wrapper-component-action-header, + .new-component-item, + .editing-draft-alert, + .publish-draft-message, + .preview-button { + display: none; + } + + .published-alert { + display: block; + } + + .drag-handle { + display: none !important; + } +} + +.edit-state-private { + .delete-draft, + .publish-draft, + .editing-draft-alert, + .create-draft, + .view-button { + display: none; + } +} +// ==================== + +// Latex Compiler +.launch-latex-compiler { + background-color: $white; + padding: $baseline/2 0 $baseline/2 $baseline; + border-bottom: 1px solid $gray-l2; + opacity: 0.8; + + + &:hover { + @include transition(opacity $tmg-f2 ease-in-out 0s); + opacity: 1.0s; + } +} + +// hides latex compiler button if settings mode is-active +div.wrapper-comp-editor.is-inactive ~ div.launch-latex-compiler{ + display: none; +} diff --git a/cms/templates/container.html b/cms/templates/container.html new file mode 100644 index 0000000000..3fecc55d81 --- /dev/null +++ b/cms/templates/container.html @@ -0,0 +1,191 @@ +<%inherit file="base.html" /> +<%! + from django.core.urlresolvers import reverse + from django.utils.translation import ugettext as _ +%> +<%block name="title">${_("Container")} +<%block name="bodyclass">is-signedin course uploads view-container + +<%namespace name='static' file='static_content.html'/> + +<%block name="header_extras"> +% for template_name in [ ]: + +% endfor + + +<%block name="jsextra"> + + + + +<%block name="content"> + + +
+
+

+ ${_("Content")} + > ${_("Container")} +

+ + +
+
+ +
+
+
+

+
    + % for locator in components: +
  1. + % endfor +
  2. +
    +
    ${_("Add New Component")}
    + +
    + % for type, templates in sorted(component_templates.items()): + % if len(templates) > 1 or type == 'advanced': +
    + % if type == "problem": +
    + + % endif +
    +
      + % for name, category, has_markdown, boilerplate_name in sorted(templates): + % if has_markdown or type != "problem": + % if boilerplate_name is None: +
    • + + ${name} + +
    • + + % else: +
    • + + ${name} + +
    • + % endif + % endif + + %endfor +
    +
    + % if type == "problem": +
    +
      + % for name, category, has_markdown, boilerplate_name in sorted(templates): + % if not has_markdown: +
    • + + ${name} + +
    • + % endif + % endfor +
    +
    +
    + % endif + Cancel +
    + % endif + % endfor +
  3. +
+
+ + +
+
+ + + +<%block name="view_alerts"> + +
+
+ + +
+

${_('Your file has been deleted.')}

+
+ + + + ${_('close alert')} + +
+
+ diff --git a/cms/urls.py b/cms/urls.py index 8192b45886..a6d49d15cc 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -70,6 +70,7 @@ urlpatterns += patterns( url(r'(?ix)^course($|/){}$'.format(parsers.URL_RE_SOURCE), 'course_handler'), url(r'(?ix)^subsection($|/){}$'.format(parsers.URL_RE_SOURCE), 'subsection_handler'), url(r'(?ix)^unit($|/){}$'.format(parsers.URL_RE_SOURCE), 'unit_handler'), + url(r'(?ix)^container($|/){}$'.format(parsers.URL_RE_SOURCE), 'container_handler'), url(r'(?ix)^checklists/{}(/)?(?P\d+)?$'.format(parsers.URL_RE_SOURCE), 'checklists_handler'), url(r'(?ix)^orphan/{}$'.format(parsers.URL_RE_SOURCE), 'orphan_handler'), url(r'(?ix)^assets/{}(/)?(?P.+)?$'.format(parsers.URL_RE_SOURCE), 'assets_handler'), From 70ba10433eaec2f20cd842ac5c557404db3a831d Mon Sep 17 00:00:00 2001 From: Andy Armstrong Date: Fri, 31 Jan 2014 23:19:40 -0500 Subject: [PATCH 2/4] Couple more changes --- cms/static/sass/views/_container.scss | 4 + cms/templates/container.html | 108 ++++---------------------- 2 files changed, 17 insertions(+), 95 deletions(-) diff --git a/cms/static/sass/views/_container.scss b/cms/static/sass/views/_container.scss index 4dc66f5edc..530f5ae222 100644 --- a/cms/static/sass/views/_container.scss +++ b/cms/static/sass/views/_container.scss @@ -901,6 +901,10 @@ body.unit { &.editing { padding-top: 0; } + + .drag-handle { + display: none; + } } } diff --git a/cms/templates/container.html b/cms/templates/container.html index 3fecc55d81..8073c0f4ff 100644 --- a/cms/templates/container.html +++ b/cms/templates/container.html @@ -1,12 +1,13 @@ <%inherit file="base.html" /> <%! - from django.core.urlresolvers import reverse - from django.utils.translation import ugettext as _ +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ %> <%block name="title">${_("Container")} <%block name="bodyclass">is-signedin course uploads view-container <%namespace name='static' file='static_content.html'/> +<%namespace name="units" file="widgets/units.html" /> <%block name="header_extras"> % for template_name in [ ]: @@ -20,17 +21,19 @@ -% endfor - - -<%block name="jsextra"> - - - - -<%block name="content"> - - -
-
-

- - ${_("Parent of Unit Page")} - ${_("Unit Page Super Long Title Name Goes Here Yeah Whats Up Parent")} - - > - ${_("Container")} -

- - -
-
- -
-
-
-

-
    - % for locator in components: -
  1. - % endfor -
-
- - -
-
- - - -<%block name="view_alerts"> - -
-
- - -
-

${_('Your file has been deleted.')}

-
- - - - ${_('close alert')} - -
-
- diff --git a/cms/templates/js/asset.underscore b/cms/templates/js/asset.underscore index 73f815f9e1..938ec10471 100644 --- a/cms/templates/js/asset.underscore +++ b/cms/templates/js/asset.underscore @@ -17,7 +17,7 @@ -
    +
    • <%= gettext('Delete this asset') %>
    • diff --git a/cms/templates/ux/reference/container.html b/cms/templates/ux/reference/container.html new file mode 100644 index 0000000000..00f5e7caea --- /dev/null +++ b/cms/templates/ux/reference/container.html @@ -0,0 +1,442 @@ +<%inherit file="../../base.html" /> +<%! +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ +%> +<%block name="title">${_("Container")} +<%block name="bodyclass">is-signedin course uploads view-container + +<%namespace name='static' file='../../static_content.html'/> +<%namespace name="units" file="../../widgets/units.html" /> + +<%block name="content"> +
      + +
      +
      +

      + + Parent of Unit Page + Unit Page Super Long Title Name Goes Here Yeah Whats Up Parent + Container Name + +

      + + +
      +
      +
      +
      +
      +
      +
      +
      +
      + Container Name +
      +
      + +
      +
      +
      +
        +
      1. +
        +
        +
        + + + Expand or Collapse + + Condition 1 +
        +
        +
          +
        • No Actions
        • +
        +
        +
        +
        +
          +
        1. +
          +
          +
          + Video +
          + +
          + +
          +
        2. +
          +
          +
          + Randomize Block +
          +
          + +
          +
          +
          Shows Element - Example Randomize Block could be here.
          +
          +
        3. +
          +
          +
          + Element Level Header +
          + +
          +
          Shows Element - Example HTML could be here.
          +
          +
        4. +
        +
        +
        +
      2. +
      3. +
        +
        +
        + + + Expand or Collapse + + Condition 2 +
        +
        +
          +
        • No Actions
        • +
        +
        +
        +
        +
          +
        1. +
          +
          +
          + Video +
          + +
          +
          Shows Element - Example Video could be here.
          +
          +
        2. +
          +
          +
          + Common Problem +
          + +
          +
          +
          +
          + + +

          + Multiple Choice +

          + +
          (1 point possible)
          + +
          +
          +

          + A multiple choice problem presents radio buttons for student + input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets.

          +

          One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make. +

          +

          What Apple device competed with the portable CD player?

          +
          +
          + +
          + +
          + + + + + + +
          + +
          +
          + +
          + +
          + + + +
          +
          +
          + +
          +
          +
          +
        3. +
          +
          +
          + HTML +
          + +
          +
          +
          +
            +
          1. +

            September 21

            +
            +
            +

            Words of encouragement! This is a short note that most students will read.

            +

            Anant Agarwal (6.002x Principal Instructor)

            +
            +

            Primary versus Secondary Updates:

            Unfortunately, the internet throws a lot of text at students, and they + do not read everything that they are given. However, many students do read all that they are + given, and so detailed explanations in this section will benefit the most conscientious. + Any essential information should be extremely quickly summarized in the primary section for skimmers.

            +

            Star Forum Poster

            + Students appreciate knowing that the course staff is reading what they post, and one of several ways + that you can do this is by acknowledging the star posters in your announcements. +

            +
            +
          2. +
          + +
          +
          +
          +
        4. +
        +
        +
        +
      4. +
      +
      +
      +
      + +
      +
      +
      + + +
      + diff --git a/cms/templates/ux/reference/index.html b/cms/templates/ux/reference/index.html new file mode 100644 index 0000000000..719fec1a71 --- /dev/null +++ b/cms/templates/ux/reference/index.html @@ -0,0 +1,29 @@ +<%inherit file="../../base.html" /> +<%block name="title">UX Style Reference +<%block name="bodyclass">is-signedin course uploads view-container + +<%block name="content"> +
      +
      +
      + +
      +
      +
      + diff --git a/cms/templates/ux/reference/unit.html b/cms/templates/ux/reference/unit.html new file mode 100644 index 0000000000..636dce9152 --- /dev/null +++ b/cms/templates/ux/reference/unit.html @@ -0,0 +1,886 @@ +<%inherit file="../../base.html" /> +<%! +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext as _ +%> +<%namespace name="units" file="../../widgets/units.html" /> +<%block name="title">${_("Individual Unit")} +<%block name="bodyclass">is-signedin course unit view-unit + +<%block name="content"> +
      +
      +
      +

      You are editing a draft. +

      + View the Live Version +
      +
      +
      +

      + + +

      +
        +
      1. +
        +
        +
        + + +
        + +
        +
        +
        +
        +
        + + +
        + + +
        +
        +
        + +
        +
        +
        +
        + Save + Cancel +
        +
        +
        + + +
        +
          +
        1. +

          September 21

          +
          +
          +

          Words of encouragement! This is a short note that most students will read.

          +

          Anant Agarwal (6.002x Principal Instructor)

          +
          +

          Primary versus Secondary Updates:

          Unfortunately, the internet throws a lot of text at students, and they + do not read everything that they are given. However, many students do read all that they are + given, and so detailed explainations in this section will benefit the most concientious. + Any essential information should be extremely quickly summarized in the primary section for skimmers.

          +

          Star Forum Poster

          + Students appriciate knowing that the course staff is reading what they post, and one of several ways + that you can do this is by acknowledging the star posters in your announcements. +

          +
          +
        2. +
        +
        +
      2. +
      3. + + +
        +
        +
        + + +
        + +
        +
        +
        + +
        +
        +
        + + +
        +
        +
        + + +
        +
        + + + + + + +
        +
        +
        +
        + + +
        +
        +
        +
        + Save + Cancel +
        +
        +
        + + +
        + + +

        Video

        + +
        +
        + +
        + Skip to a navigable version of this video's transcript. + + + +
          +
        1. +
        +
        + + Go back to start of transcript. + +
        +
          +
        +
        + +
        + + +
      4. +
      5. +
        +
        +
        + Randomize Block +
        +
        + +
        +
        +
        Shows Element - Example Randomize Block could be here.
        +
        +
      6. +
      7. +
        +
        +
        + + +
        + +
        +
        +
        + + + +
        +
        +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        + +
        + + +
        +
        + + +
        + + + + + + + + +
        + +
        +
        +
        + Save + Cancel +
        +
        +
        + + +
        +
        + + +

        + Blank Common Problem +

        + +
        (0 points)
        + +
        +
        + +
        + + + + +
        +
        +
        + +
        + + +
      8. +
        +
        Add New Component
        + +
        +
        +
        + +
        + Cancel +
        + + +
      9. +
      +
      +
      + + + +
      +
      + diff --git a/cms/urls.py b/cms/urls.py index a6d49d15cc..8192b45886 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -70,7 +70,6 @@ urlpatterns += patterns( url(r'(?ix)^course($|/){}$'.format(parsers.URL_RE_SOURCE), 'course_handler'), url(r'(?ix)^subsection($|/){}$'.format(parsers.URL_RE_SOURCE), 'subsection_handler'), url(r'(?ix)^unit($|/){}$'.format(parsers.URL_RE_SOURCE), 'unit_handler'), - url(r'(?ix)^container($|/){}$'.format(parsers.URL_RE_SOURCE), 'container_handler'), url(r'(?ix)^checklists/{}(/)?(?P\d+)?$'.format(parsers.URL_RE_SOURCE), 'checklists_handler'), url(r'(?ix)^orphan/{}$'.format(parsers.URL_RE_SOURCE), 'orphan_handler'), url(r'(?ix)^assets/{}(/)?(?P.+)?$'.format(parsers.URL_RE_SOURCE), 'assets_handler'), diff --git a/common/static/sass/_mixins.scss b/common/static/sass/_mixins.scss index 71ce36a43a..c5cb72f555 100644 --- a/common/static/sass/_mixins.scss +++ b/common/static/sass/_mixins.scss @@ -43,6 +43,43 @@ // ==================== +// mixins - flex support +@mixin ui-flexbox() { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; +} + +// extends - justify-content right for display:flex alignment in older browsers +%ui-justify-right-flex { + -webkit-box-pack: end; + -moz-box-pack: end; + -ms-flex-pack: end; + -webkit-justify-content: end; + justify-content: flex-end; +} + +// extends - justify-content left for display:flex alignment in older browsers +%ui-justify-left-flex { + -webkit-box-pack: start; + -moz-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: flex-start; +} + +// extends - align items center for display:flex alignment in older browsers +%ui-align-center-flex { + -webkit-flex-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + align-items: center; +} + +// ==================== + // extends - UI - used for page/view-level wrappers (for centering/grids) %ui-wrapper { @include clearfix();