Commit da4b8d35 by Zia Fazal Committed by Jonathan Piacenti

changes to make things work as expected

changes to make things work as expected after rebase with master branch
having notifications
parent d4f63292
......@@ -69,10 +69,13 @@ BROKER_HEARTBEAT_CHECKRATE = 2
# Each worker should only fetch one message at a time
CELERYD_PREFETCH_MULTIPLIER = 1
if not 'SOUTH_MIGRATION_MODULES' in vars() and not 'SOUTH_MIGRATION_MODULES' in globals():
SOUTH_MIGRATION_MODULES = {}
# Skip djcelery migrations, since we don't use the database as the broker
SOUTH_MIGRATION_MODULES = {
SOUTH_MIGRATION_MODULES.update({
'djcelery': 'ignore',
}
})
# Rename the exchange and queues for each variant
......
......@@ -13,7 +13,7 @@ from openedx.core.lib.django_startup import autostartup
from edx_notifications import startup
from monkey_patch import django_utils_translation
from course_groups.scope_resolver import CourseGroupScopeResolver
from openedx.core.djangoapps.course_groups.scope_resolver import CourseGroupScopeResolver
from student.scope_resolver import CourseEnrollmentsScopeResolver, StudentEmailScopeResolver
from projects.scope_resolver import GroupProjectParticipantsScopeResolver
from edx_notifications.scopes import register_user_scope_resolver
......
......@@ -524,7 +524,6 @@ p, ul, ol, dl {
position: absolute;
top: 5px;
@include right(5px);
.edit-button,
.delete-button,
.visibility-toggle {
......
......@@ -237,6 +237,7 @@
}
.branding {
width: 20%;
@include margin-right(2%);
}
......@@ -261,6 +262,7 @@
}
.branding {
width: 20%;
@include margin-right(2%);
}
......
......@@ -207,6 +207,8 @@
position: absolute;
display: block;
top: 0;
@include right(0);
z-index: 11;
width: 35px;
height: 100%;
border: none;
......
......@@ -127,7 +127,7 @@
// ELEM: item - metadata
.item-metadata {
width: flex-grid(4, 9);
width: flex-grid(5, 9);
@include margin-right(flex-gutter());
.user-username, .user-email {
......
......@@ -152,6 +152,7 @@ div.problem {
label {
@include box-sizing(border-box);
display: inline-block;
@include float(left);
clear: both;
margin-bottom: ($baseline/2);
border: 2px solid $gray-l4;
......@@ -184,19 +185,26 @@ div.problem {
}
}
.indicator-container {
.indicator_container {
display: inline-block;
min-height: 1px;
@include float(left);
width: 25px;
height: 1px;
@include margin-right(15px);
}
fieldset {
@include box-sizing(border-box);
margin: 0px 0px $baseline;
@include padding-left($baseline);
@include border-left(1px solid #ddd);
}
input[type="radio"],
input[type="checkbox"] {
@include margin(($baseline/4) ($baseline/2) ($baseline/4) ($baseline/4));
@include float(left);
}
text {
......@@ -907,7 +915,43 @@ div.problem {
padding: lh();
border: 1px solid $gray-l3;
}
div.action {
margin-top: $baseline;
.save, .check, .show, .reset {
height: ($baseline*2);
vertical-align: middle;
font-weight: 600;
}
.save {
@extend .blue-button !optional;
}
.show {
.show-label {
font-weight: 600;
font-size: 1.0em;
}
}
.submission_feedback {
// background: #F3F3F3;
// border: 1px solid #ddd;
// border-radius: 3px;
// padding: 8px 12px;
// margin-top: ($baseline/2);
display: inline-block;
margin-top: 8px;
@include margin-left($baseline/2);
color: #666;
font-style: italic;
-webkit-font-smoothing: antialiased;
}
}
.detailed-solution {
> p:first-child {
@extend %t-strong;
......
......@@ -264,7 +264,27 @@ $sequence--border-color: #C8C8C8;
&:hover,
&:active {
}
&.prev {
@include left(-10px);
@include border-radius(35px 0 0 35px);
a {
background-image: $prev-icon;
background-position: center 15px;
}
}
&.next {
@include right(-10px);
@include border-radius(0 35px 35px 0);
a {
@include margin-left(30px);
background-image: $next-icon;
background-position: center 15px;
}
}
}
&.disabled {
cursor: normal;
......@@ -284,6 +304,79 @@ nav.sequence-bottom {
@media print {
display: none;
ul {
@include clearfix();
display: inline-block;
li {
@include float(left);
width: 50px;
height: 44px;
border: 1px solid $gray-l3;
@include linear-gradient(top, #eee, #ddd);
box-shadow: 0 1px 0 rgba(255, 255, 255, .7) inset;
&.prev, &.next {
margin-bottom: 0;
a {
background-position: center center;
background-repeat: no-repeat;
border: none;
display: block;
padding: lh(0.5) 4px;
text-indent: -9999px;
overflow: hidden;
@include transition(all .2s $ease-in-out-quad 0s);
&:hover, &:focus {
opacity: 0.5;
background-position: center 15px;
}
&.disabled {
opacity: 0.4;
}
/* &:focus {
outline: 0;
}
*/
}
}
$prev-icon: url('../images/sequence-nav/previous-icon.png');
$next-icon: url('../images/sequence-nav/next-icon.png');
&.prev {
@include border-radius(35px 0 0 35px);
a {
background-image: $prev-icon;
background-position: center 15px;
}
}
&.next {
@include border-radius(0 35px 35px 0);
@include border-left(none);
a {
background-image: $next-icon;
background-position: center 15px;
@include ltr {
background-image: $next-icon;
}
@include rtl {
background-image: $prev-icon;
}
}
}
}
}
}
}
......
// common - utilities - mixins and extends
// ====================
// Table of Contents
// Table of Contents
// * +Font Sizing - Mixin
// * +Line Height - Mixin
// * +Sizing - Mixin
......@@ -26,34 +26,34 @@
// * +Icon - Font-Awesome - Extend
// +Font Sizing - Mixin
// ====================
// ====================
@mixin font-size($sizeValue: 16){
font-size: $sizeValue + px;
font-size: ($sizeValue/10) + rem;
}
// +Line Height - Mixin
// ====================
// ====================
@mixin line-height($fontSize: auto){
line-height: ($fontSize*1.48) + px;
line-height: (($fontSize/10)*1.48) + rem;
}
// +Sizing - Mixin
// ====================
// ====================
@mixin size($width: $baseline, $height: $baseline) {
height: $height;
width: $width;
}
// +Square - Mixin
// ====================
// ====================
@mixin square($size: $baseline) {
@include size($size);
}
// +Placeholder Styling - Mixin
// ====================
// ====================
@mixin placeholder($color) {
:-moz-placeholder {
color: $color;
......@@ -67,7 +67,7 @@
}
// +Flex Support - Mixin
// ====================
// ====================
@mixin ui-flexbox() {
display: -webkit-box;
display: -moz-box;
......@@ -77,7 +77,7 @@
}
// +Flex PolyFill - Extends
// ====================
// ====================
// justify-content right for display:flex alignment in older browsers
%ui-justify-right-flex {
......@@ -107,7 +107,7 @@
// +UI - Wrapper - Extends
// ====================
// ====================
// used for page/view-level wrappers (for centering/grids)
%ui-wrapper {
@include clearfix();
......@@ -128,7 +128,7 @@
}
// +UI - Window - Extends
// ====================
// ====================
%ui-window {
@include clearfix();
border-radius: ($baseline/10);
......@@ -144,13 +144,13 @@
}
// +UI - Visual Link - Extends
// ====================
// ====================
%ui-fake-link {
cursor: pointer;
}
// +UI - Functional Disable - Extends
// ====================
// ====================
%ui-disabled {
pointer-events: none;
outline: none;
......@@ -158,7 +158,7 @@
}
// +UI - Depth Levels - Extends
// ====================
// ====================
%ui-depth0 { z-index: 0; }
%ui-depth1 { z-index: 10; }
%ui-depth2 { z-index: 100; }
......@@ -168,7 +168,7 @@
// +UI - Clear Children - Extends
// ====================
// ====================
// extends - UI - utility - first child clearing
%wipe-first-child {
......@@ -190,7 +190,7 @@
}
// +UI - Buttons - Extends
// ====================
// ====================
%ui-btn {
@include box-sizing(border-box);
@include transition(color $tmg-f2 ease-in-out 0s, border-color $tmg-f2 ease-in-out 0s, background $tmg-f2 ease-in-out 0s, box-shadow $tmg-f2 ease-in-out 0s);
......@@ -329,7 +329,7 @@
}
// +UI - Well Archetype - Extends
// ====================
// ====================
%ui-well {
box-shadow: inset 0 1px 2px 1px $shadow;
padding: ($baseline*0.75) $baseline;
......@@ -378,7 +378,7 @@
}
// +Content - No List - Extends
// ====================
// ====================
// removes list styling/spacing when using uls, ols for navigation and less content-centric cases
%cont-no-list {
list-style: none;
......@@ -393,7 +393,7 @@
}
// +Content - Hidden Image Text - Extend
// ====================
// ====================
// image-replacement hidden text
%cont-text-hide {
text-indent: 100%;
......@@ -402,7 +402,7 @@
}
// +Content - Screenreader Text - Extend
// ====================
// ====================
%cont-text-sr {
border: 0;
clip: rect(0 0 0 0);
......@@ -415,13 +415,13 @@
}
// +Content - Text Wrap - Extend
// ====================
// ====================
%cont-text-wrap {
word-wrap: break-word;
}
// +Content - Text Truncate - Extend
// ====================
// ====================
%cont-truncated {
@include box-sizing(border-box);
overflow: hidden;
......
......@@ -262,7 +262,6 @@ def create_thread(request, course_id, commentable_id):
add_courseware_context([data], course, user)
add_thread_group_name(data, course_key)
add_courseware_context([data], course)
if request.is_ajax():
return ajax_content_response(request, course_key, data)
else:
......
......@@ -19,14 +19,17 @@ import newrelic.agent
from edxmako.shortcuts import render_to_response
from courseware.courses import get_course_with_access
from courseware.tabs import EnrolledTab
from openedx.core.djangoapps.course_groups.cohorts import (
is_course_cohorted,
get_cohort_id,
get_course_cohorts,
is_commentable_cohorted,
get_cohorted_threads_privacy,
get_cohorted_commentables)
from courseware.tabs import EnrolledTab
get_cohorted_commentables,
get_cohort_by_id
)
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
from courseware.access import has_access
from xmodule.modulestore.django import modulestore
from ccx.overrides import get_current_ccx
......@@ -227,7 +230,7 @@ def inline_discussion(request, course_key, discussion_id):
try:
threads, query_params = get_threads(request, course, discussion_id, per_page=INLINE_THREADS_PER_PAGE)
except ValueError:
except (ValueError, CourseUserGroup.DoesNotExist):
return HttpResponseBadRequest("Invalid group_id")
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
......@@ -269,7 +272,7 @@ def forum_form_discussion(request, course_key):
except cc.utils.CommentClientMaintenanceError:
log.warning("Forum is in maintenance mode")
return render_to_response('discussion/maintenance.html', {})
except ValueError:
except (ValueError, CourseUserGroup.DoesNotExist):
return HttpResponseBadRequest("Invalid group_id")
with newrelic.agent.FunctionTrace(nr_transaction, "get_metadata_for_threads"):
......
# pylint: disable=C0103
""" ORGANIZATIONS API VIEWS """
from django.conf import settings
from django.contrib.auth.models import User, Group
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Sum, F, Count
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from api_manager.courseware_access import get_course_key
from organizations.models import Organization
from api_manager.users.serializers import UserSerializer, SimpleUserSerializer
from api_manager.groups.serializers import GroupSerializer
from api_manager.utils import str2bool
from gradebook.models import StudentGradebook
from student.models import CourseEnrollment
from student.roles import get_aggregate_exclusion_user_ids
from .serializers import OrganizationSerializer
class OrganizationsViewSet(viewsets.ModelViewSet):
"""
Django Rest Framework ViewSet for the Organization model.
"""
serializer_class = OrganizationSerializer
model = Organization
@action(methods=['get', ])
def metrics(self, request, pk):
"""
Provide statistical information for the specified Organization
"""
response_data = {}
grade_avg = 0
grade_complete_match_range = getattr(settings, 'GRADEBOOK_GRADE_COMPLETE_PROFORMA_MATCH_RANGE', 0.01)
org_user_grades = StudentGradebook.objects.filter(user__organizations=pk, user__is_active=True)
courses_filter = request.QUERY_PARAMS.get('courses', None)
courses = []
exclude_users = set()
if courses_filter:
upper_bound = getattr(settings, 'API_LOOKUP_UPPER_BOUND', 100)
courses_filter = courses_filter.split(",")[:upper_bound]
for course_string in courses_filter:
courses.append(get_course_key(course_string))
# fill exclude users
for course_key in courses:
exclude_users.union(get_aggregate_exclusion_user_ids(course_key))
org_user_grades = org_user_grades.filter(course_id__in=courses).exclude(user_id__in=exclude_users)
users_grade_sum = org_user_grades.aggregate(Sum('grade'))
if users_grade_sum['grade__sum']:
users_enrolled_qs = CourseEnrollment.objects.filter(user__is_active=True, is_active=True,
user__organizations=pk)\
.exclude(user_id__in=exclude_users)
if courses:
users_enrolled_qs = users_enrolled_qs.filter(course_id__in=courses)
users_enrolled = users_enrolled_qs.aggregate(Count('user', distinct=True))
total_users = users_enrolled['user__count']
if total_users:
# in order to compute avg across organization we need course of courses org has
total_courses_in_org = len(courses)
if not courses:
org_courses = users_enrolled_qs.aggregate(Count('course_id', distinct=True))
total_courses_in_org = org_courses['course_id__count']
grade_avg = float('{0:.3f}'.format(
float(users_grade_sum['grade__sum']) / total_users / total_courses_in_org
))
response_data['users_grade_average'] = grade_avg
users_grade_complete_count = org_user_grades\
.filter(proforma_grade__lte=F('grade') + grade_complete_match_range, proforma_grade__gt=0)\
.aggregate(Count('user', distinct=True))
response_data['users_grade_complete_count'] = users_grade_complete_count['user__count'] or 0
return Response(response_data, status=status.HTTP_200_OK)
@action(methods=['get', 'post'])
def users(self, request, pk):
"""
- URI: ```/api/organizations/{org_id}/users/```
- GET: Returns users in an organization
* course_id parameter should filter user by course
* include_course_counts parameter should be `true` to get user's enrollment count
* include_grades parameter should be `true` to get user's grades
* for the course given in the course_id parameter
- POST: Adds a User to an Organization
"""
if request.method == 'GET':
include_course_counts = request.QUERY_PARAMS.get('include_course_counts', None)
include_grades = request.QUERY_PARAMS.get('include_grades', None)
course_id = request.QUERY_PARAMS.get('course_id', None)
grade_complete_match_range = getattr(settings, 'GRADEBOOK_GRADE_COMPLETE_PROFORMA_MATCH_RANGE', 0.01)
course_key = None
if course_id:
course_key = get_course_key(course_id)
users = User.objects.filter(organizations=pk)
if course_key:
users = users.filter(courseenrollment__course_id__exact=course_key,
courseenrollment__is_active=True)
if str2bool(include_grades):
users = users.select_related('studentgradebook')
if str2bool(include_course_counts):
enrollments = CourseEnrollment.objects.filter(user__in=users).values('user').order_by().annotate(total=Count('user'))
enrollments_by_user = {}
for enrollment in enrollments:
enrollments_by_user[enrollment['user']] = enrollment['total']
response_data = []
if users:
for user in users:
serializer = SimpleUserSerializer(user)
user_data = serializer.data
if str2bool(include_course_counts):
user_data['course_count'] = enrollments_by_user.get(user.id, 0)
if str2bool(include_grades) and course_key:
user_grades = {'grade': 0, 'proforma_grade': 0, 'complete_status': False}
gradebook = user.studentgradebook_set.filter(course_id=course_key)
if gradebook:
user_grades['grade'] = gradebook[0].grade
user_grades['proforma_grade'] = gradebook[0].proforma_grade
user_grades['complete_status'] = True if 0 < gradebook[0].proforma_grade <= \
gradebook[0].grade + grade_complete_match_range else False
user_data.update(user_grades)
response_data.append(user_data)
return Response(response_data, status=status.HTTP_200_OK)
else:
user_id = request.DATA.get('id')
try:
user = User.objects.get(id=user_id)
except ObjectDoesNotExist:
message = 'User {} does not exist'.format(user_id)
return Response({"detail": message}, status.HTTP_400_BAD_REQUEST)
organization = self.get_object()
organization.users.add(user)
organization.save()
return Response({}, status=status.HTTP_201_CREATED)
@action(methods=['get', 'post'])
def groups(self, request, pk):
"""
Add a Group to a organization or retrieve list of groups in organization
"""
if request.method == 'GET':
group_type = request.QUERY_PARAMS.get('type', None)
groups = Group.objects.filter(organizations=pk)
if group_type:
groups = groups.filter(groupprofile__group_type=group_type)
response_data = []
if groups:
for group in groups:
serializer = GroupSerializer(group)
response_data.append(serializer.data) # pylint: disable=E1101
return Response(response_data, status=status.HTTP_200_OK)
else:
group_id = request.DATA.get('id')
try:
group = Group.objects.get(id=group_id)
except ObjectDoesNotExist:
message = 'Group {} does not exist'.format(group_id)
return Response({"detail": message}, status.HTTP_400_BAD_REQUEST)
organization = self.get_object()
organization.groups.add(group)
organization.save()
return Response({}, status=status.HTTP_201_CREATED)
"""
Run these tests @ Devstack:
rake fasttest_lms[common/djangoapps/api_manager/management/commands/tests/test_migrate_orgdata.py]
"""
from datetime import datetime
import uuid
from django.conf import settings
from django.contrib.auth.models import User
from django.test.utils import override_settings
from api_manager.management.commands import migrate_courseids
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, mixed_store_config
from projects.models import Project, Workgroup, WorkgroupReview, WorkgroupSubmission, WorkgroupSubmissionReview
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
MODULESTORE_CONFIG = mixed_store_config(settings.COMMON_TEST_DATA_ROOT, {}, include_xml=False)
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
class MigrateCourseIdsTests(ModuleStoreTestCase):
"""
Test suite for data migration script
"""
def setUp(self):
self.course = CourseFactory.create(
start=datetime(2014, 6, 16, 14, 30),
end=datetime(2015, 1, 16)
)
self.test_data = '<html>{}</html>'.format(str(uuid.uuid4()))
self.chapter = ItemFactory.create(
category="chapter",
parent_location=self.course.location,
data=self.test_data,
due=datetime(2014, 5, 16, 14, 30),
display_name="Overview"
)
self.old_style_course_id = self.course.id.to_deprecated_string()
self.new_style_course_id = unicode(self.course.id)
self.old_style_content_id = self.chapter.location.to_deprecated_string()
self.new_style_content_id = unicode(self.chapter.location)
self.course2 = CourseFactory.create(
org='TEST',
start=datetime(2014, 6, 16, 14, 30),
end=datetime(2015, 1, 16)
)
self.chapter2 = ItemFactory.create(
category="chapter",
parent_location=self.course2.location,
data=self.test_data,
due=datetime(2014, 5, 16, 14, 30),
display_name="Overview"
)
self.new_style_course_id2 = unicode(self.course2.id)
self.new_style_content_id2 = unicode(self.chapter2.location)
def test_migrate_courseids(self):
"""
Test the data migration
"""
# Set up the data to be migrated
user = User.objects.create(email='testuser@edx.org', username='testuser', password='testpassword', is_active=True)
project = Project.objects.create(course_id=self.old_style_course_id, content_id=self.old_style_content_id)
workgroup = Workgroup.objects.create(name='Test Workgroup', project=project)
workgroup_review = WorkgroupReview.objects.create(workgroup=workgroup, content_id=self.old_style_content_id)
workgroup_submission = WorkgroupSubmission.objects.create(workgroup=workgroup, user=user)
workgroup_submission_review = WorkgroupSubmissionReview.objects.create(submission=workgroup_submission, content_id=self.old_style_content_id)
user2 = User.objects.create(email='testuser2@edx.org', username='testuser2', password='testpassword2', is_active=True)
project2 = Project.objects.create(course_id=self.new_style_course_id2, content_id=self.new_style_content_id2)
workgroup2 = Workgroup.objects.create(name='Test Workgroup2', project=project2)
workgroup_review2 = WorkgroupReview.objects.create(workgroup=workgroup2, content_id=self.new_style_content_id2)
workgroup_submission2 = WorkgroupSubmission.objects.create(workgroup=workgroup2, user=user2)
workgroup_submission_review2 = WorkgroupSubmissionReview.objects.create(submission=workgroup_submission2, content_id=self.new_style_content_id2)
# Run the data migration
migrate_courseids.Command().handle()
# Confirm that the data has been properly migrated
updated_project = Project.objects.get(id=project.id)
self.assertEqual(updated_project.course_id, self.new_style_course_id)
self.assertEqual(updated_project.content_id, self.new_style_content_id)
updated_project = Project.objects.get(id=project2.id)
self.assertEqual(updated_project.course_id, self.new_style_course_id2)
self.assertEqual(updated_project.content_id, self.new_style_content_id2)
print "Project Data Migration Passed"
updated_workgroup_review = WorkgroupReview.objects.get(id=workgroup_review.id)
self.assertEqual(updated_workgroup_review.content_id, self.new_style_content_id)
updated_workgroup_review = WorkgroupReview.objects.get(id=workgroup_review2.id)
self.assertEqual(updated_workgroup_review.content_id, self.new_style_content_id2)
print "Workgroup Review Data Migration Passed"
updated_workgroup_submission_review = WorkgroupSubmissionReview.objects.get(id=workgroup_submission_review.id)
self.assertEqual(updated_workgroup_submission_review.content_id, self.new_style_content_id)
updated_workgroup_submission_review = WorkgroupSubmissionReview.objects.get(id=workgroup_submission_review2.id)
self.assertEqual(updated_workgroup_submission_review.content_id, self.new_style_content_id2)
print "Workgroup Submission Review Data Migration Passed"
......@@ -144,9 +144,6 @@ FEATURES = {
# with Shib. Feature was requested by Stanford's office of general counsel
'SHIB_DISABLE_TOS': False,
# Allows to configure the LMS to provide CORS headers to serve requests from other domains
'ENABLE_CORS_HEADERS': False,
# Toggles OAuth2 authentication provider
'ENABLE_OAUTH2_PROVIDER': False,
......
// ------------------------------
// LMS Courseware: Shared Build Compile
@import 'bourbon/bourbon';
@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages
// About: Sass compile for the LMS Courseware Elements that are shared between LTR and RTL UI. Configuration and vendor specific imports happen before this shared set of imports are compiled in the lms-course-*.scss files.
......
## Note: This Sass infrastructure is repeated in application-extend1 and application-extend2, but needed in order to address an IE9 rule limit within CSS - http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/10164546.aspx
// lms - css application architecture
// ====================
// libs and resets *do not edit*
@import 'bourbon/bourbon'; // lib - bourbon
@import 'vendor/bi-app/bi-app-ltr'; // set the layout for left to right languages
// BASE *default edX offerings*
// ====================
// base - utilities
@import 'base/reset';
@import 'base/variables';
@import 'base/mixins';
## THEMING
## -------
## Set up this file to import an edX theme library if the environment
## indicates that a theme should be used. The assumption is that the
## theme resides outside of this main edX repository, in a directory
## called themes/<theme-name>/, with its base Sass file in
## themes/<theme-name>/static/sass/_<theme-name>.scss. That one entry
## point can be used to @import in as many other things as needed.
% if env["FEATURES"].get("USE_CUSTOM_THEME", False):
// import theme's Sass overrides
@import '${env.get('THEME_NAME')}';
% endif
@import 'base/base';
// base - assets
@import 'base/font_face';
@import 'base/extends';
@import 'base/animations';
// base - starter
@import 'base/base';
// base - elements
@import 'elements/typography';
@import 'elements/controls';
// shared - course
@import 'shared/forms';
@import 'shared/footer';
@import 'shared/header';
@import 'shared/course_object';
@import 'shared/course_filter';
@import 'shared/modal';
@import 'shared/activation_messages';
@import 'shared/unsubscribe';
@import 'developer'; // used for any developer-created scss that needs further polish/refactoring
@import 'shame'; // used for any bad-form/orphaned scss
## NOTE: needed here for cascade and dependency purposes, but not a great permanent solution
......@@ -209,40 +209,3 @@
text-decoration: underline !important;
}
}
%tooltip {
position: absolute;
top: 0;
left: 0;
z-index: 99999;
padding: 0 10px;
border-radius: 3px;
background: rgba(0, 0, 0, .85);
font-size: 11px;
font-weight: 400;
line-height: 26px;
color: #fff;
pointer-events: none;
opacity: 0;
@include transition(opacity .1s linear 0s);
&:after {
content: '▾';
display: block;
position: absolute;
bottom: -14px;
left: 50%;
margin-left: -7px;
font-size: 20px;
color: rgba(0, 0, 0, .85);
}
}
%loading-animation {
position: absolute;
left: 50%;
width: 20px;
height: 20px;
margin-left: -10px;
background: url(../images/spinner.gif) no-repeat;
}
@mixin blue-button {
display: block;
height: 35px;
......
......@@ -969,6 +969,13 @@
background: $gray-l5;
border-radius: ($baseline/10);
input[type="button"].add {
@include idashbutton($blue);
position: absolute;
@include right($baseline);
}
}
.icon {
@include margin-right($baseline/4);
......
nav.course-material {
@include clearfix();
@include box-sizing(border-box);
background: none;
margin: 0px auto 0px;
padding: 0px;
width: 100%;
.inner-wrapper {
margin: 0 auto;
max-width: 1200px;
width: flex-grid(12);
}
ol.course-tabs {
@include border-top-radius(4px);
@include clearfix();
padding: 10px 0 0 0;
li {
float: left;
list-style: none;
a {
color: darken($lighter-base-font-color, 20%);
display: block;
text-align: center;
padding: 8px 13px 12px;
font-size: 14px;
font-weight: 400;
text-decoration: none;
text-shadow: 0 1px rgb(255,255,255);
&:hover, &:focus {
color: $base-font-color;
}
&.active {
color: $blue;
}
}
}
}
}
.course-content {
margin-top: ($baseline*1.5);
.courseware {
min-height: 300px;
}
}
......@@ -58,7 +58,7 @@
&:after,
&:before {
bottom: 100%;
right: 6px;
@include right(6px);
border: solid transparent;
content: " ";
height: 0;
......
......@@ -8,6 +8,8 @@
width: 31%;
border: 1px solid #aaa;
border-radius: 3px;
}
div.discussion-course, div.content-wrapper {
section.discussion {
div.forum-nav {
......@@ -224,7 +226,6 @@ div.discussion-course, div.content-wrapper {
@include font-size(14);
&:before {
@include rtl {
@include transform(scale(-1, 1)); // RTL for font awesome question mark
}
......
......@@ -47,6 +47,7 @@ body.discussion, .discussion-module, div.discussion-course {
@include right($baseline);
top: ($baseline);
@include float(right);
width: flex-grid(3,12);
}
}
......
......@@ -19,6 +19,7 @@
background: transparent;
@include float(right);
margin-top: ($baseline*2);
margin-right: flex-gutter();
width: flex-grid(3);
box-shadow: 0 0 1px $shadow-l1;
border: 1px solid $border-color-2;
......@@ -264,18 +265,23 @@
@include margin-right(flex-gutter());
width: flex-grid(3);
.cover {
@include box-sizing(border-box);
@include transition(all 0.15s linear 0s);
@include float(left);
overflow: hidden;
position: relative;
max-height: 120px;
border-radius: ($baseline/5);
border: 1px solid $dashboard-course-cover-border;
border-bottom: 4px solid $dashboard-course-cover-border;
.cover {
@include box-sizing(border-box);
@include transition(all 0.15s linear 0s);
overflow: hidden;
position: relative;
@include float(left);
height: 100%;
max-height: 100%;
width: 200px;
height: 120px;
margin: 0;
border-radius: ($baseline/10);
border: 1px solid $dashboard-course-cover-border;
border-bottom: 4px solid $dashboard-course-cover-border;
padding: ($baseline/10);
.course-image {
.course-image {
width: 100%;
}
}
......@@ -283,6 +289,12 @@
// "enrolled as" status
.sts-enrollment {
@include float(left);
.info {
@include clearfix;
@include padding(0, 10px, 0, 230px);
> hgroup {
padding: 0;
width: 100%;
position: relative;
bottom: 15px;
......@@ -297,6 +309,12 @@
position: absolute;
top: -5px;
@include right(0);
top: 0;
right: 0;
font-family: $sans-serif;
font-size: 13px;
font-style: italic;
color: $lighter-base-font-color;
}
.sts-enrollment-value {
......
section.outside-app {
@extend .container;
@include text-align(left);
@include float(left);
padding: ($baseline*4) 0;
h1 {
......
......@@ -13,6 +13,23 @@
}
}
.courses-listing {
@include clearfix();
margin: 0;
padding: 0;
list-style: none;
.courses-listing-item {
width: flex-grid(4);
@include margin-right(flex-gutter());
@include float(left);
&:nth-child(3n+3) {
@include margin-right(0);
}
}
}
.course {
background: $body-bg;
border: 1px solid $border-color-1;
......
......@@ -228,8 +228,8 @@ header.global {
height: 0px;
position: absolute;
@include transform(rotate(-45deg));
@include right(7px);
top: -5px;
@include right(12px);
top: -6px;
width: 0px;
}
......
......@@ -5,7 +5,7 @@ Test cases for scope_resolver.py
from django.test import TestCase
from student.tests.factories import UserFactory
from course_groups.scope_resolver import CourseGroupScopeResolver
from ..scope_resolver import CourseGroupScopeResolver
from .test_views import CohortFactory
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment