├── ContosoUniversity.Web ├── wwwroot │ ├── js │ │ ├── site.min.js │ │ └── site.js │ ├── favicon.ico │ ├── lib │ │ ├── bootstrap │ │ │ ├── dist │ │ │ │ ├── fonts │ │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ │ └── js │ │ │ │ │ └── npm.js │ │ │ ├── .bower.json │ │ │ └── LICENSE │ │ ├── jquery │ │ │ ├── .bower.json │ │ │ └── LICENSE.txt │ │ ├── jquery-validation │ │ │ ├── .bower.json │ │ │ └── LICENSE.md │ │ └── jquery-validation-unobtrusive │ │ │ └── .bower.json │ └── css │ │ ├── site.min.css │ │ └── site.css ├── .bowerrc ├── Views │ ├── _ViewStart.cshtml │ ├── _ViewImports.cshtml │ ├── Account │ │ ├── AccessDenied.cshtml │ │ ├── ForgotPasswordConfirmation.cshtml │ │ ├── ExternalLoginFailure.cshtml │ │ ├── Lockout.cshtml │ │ ├── ResetPasswordConfirmation.cshtml │ │ ├── ConfirmEmail.cshtml │ │ ├── SendCode.cshtml │ │ ├── ForgotPassword.cshtml │ │ ├── ExternalLoginConfirmation.cshtml │ │ ├── VerifyCode.cshtml │ │ ├── Register.cshtml │ │ └── ResetPassword.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _LoginPartial.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── Courses │ │ ├── UpdateCourseCredits.cshtml │ │ ├── Details.cshtml │ │ ├── Delete.cshtml │ │ ├── Index.cshtml │ │ ├── Edit.cshtml │ │ └── Create.cshtml │ ├── Instructors │ │ ├── Details.cshtml │ │ └── Delete.cshtml │ ├── Manage │ │ ├── AddPhoneNumber.cshtml │ │ ├── VerifyPhoneNumber.cshtml │ │ ├── ChangePassword.cshtml │ │ └── Index.cshtml │ ├── Departments │ │ ├── Details.cshtml │ │ ├── Delete.cshtml │ │ ├── Index.cshtml │ │ ├── Create.cshtml │ │ └── Edit.cshtml │ └── Students │ │ ├── Delete.cshtml │ │ ├── Details.cshtml │ │ ├── Create.cshtml │ │ ├── Edit.cshtml │ │ └── Index.cshtml ├── Resources │ └── contosoU2017_schema1.png ├── Readme.md ├── bower.json ├── Helpers │ ├── IUrlHelperAdaptor.cs │ └── UrlHelperAdaptor.cs ├── ViewModels │ ├── ManageIndexViewModel.cs │ ├── ForgotPasswordViewModel.cs │ ├── ExternalLoginConfirmationViewModel.cs │ ├── AddPhoneNumberViewModel.cs │ ├── TokenViewModel.cs │ ├── VerifyPhoneNumberViewModel.cs │ ├── SendCodeViewModel.cs │ ├── DepartmentEditViewModel.cs │ ├── DepartmentCreateViewModel.cs │ ├── RegisterViewModel.cs │ ├── LoginViewModel.cs │ ├── DepartmentDetailsViewModel.cs │ ├── VerifyCodeViewModel.cs │ ├── DepartmentBaseViewModel.cs │ ├── ResetPasswordViewModel.cs │ └── ChangePasswordViewModel.cs ├── Models │ └── SchoolViewModels │ │ ├── AssignedCourseData.cs │ │ ├── EnrollmentDateGroup.cs │ │ └── InstructorIndexData.cs ├── appsettings.json ├── Enums │ └── ManageMessage.cs ├── Pages │ ├── Contact.cshtml.cs │ ├── Contact.cshtml │ ├── About.cshtml │ ├── Index.cshtml │ └── About.cshtml.cs ├── IModelBindingHelperAdaptor.cs ├── QueryableExtensions.cs ├── bundleconfig.json ├── Properties │ └── launchSettings.json ├── DefaultModelBindingHelperAdaptor.cs ├── WebProfile.cs ├── appsettings.Development.json ├── PaginatedList.cs ├── Program.cs ├── ScaffoldingReadMe.txt └── ContosoUniversity.Web.csproj ├── ContosoUniversity.Data ├── Readme.md ├── Enums │ └── Grade.cs ├── Entities │ ├── ApplicationUser.cs │ ├── CourseAssignment.cs │ ├── OfficeAssignment.cs │ ├── BaseEntity.cs │ ├── Enrollment.cs │ ├── Student.cs │ ├── Instructor.cs │ ├── Course.cs │ ├── Person.cs │ └── Department.cs ├── appsettings.json ├── DTO │ ├── CourseAssignmentsDTO.cs │ ├── OfficeAssignmentDTO.cs │ ├── InstructorDTO.cs │ ├── StudentDTO.cs │ ├── CourseDTO.cs │ └── EnrollmentDTO.cs ├── AdminIdentityOptions.cs ├── OperatingSystem.cs ├── DbContexts │ ├── ApiContext.cs │ ├── SecureApplicationContext.cs │ ├── ApplicationContext.cs │ ├── SecureApplicatonContextFactory.cs │ ├── WebContext.cs │ ├── ApplicationContextFactory.cs │ └── DbContextConfig.cs └── ContosoUniversity.Data.csproj ├── ContosoUniversity.Spa.React ├── ClientApp │ ├── src │ │ ├── api │ │ │ ├── delay.js │ │ │ └── mockDepartmentApi.js │ │ ├── components │ │ │ ├── common │ │ │ │ ├── Layout.css │ │ │ │ ├── Footer.js │ │ │ │ ├── Layout.js │ │ │ │ ├── LoadingDots.js │ │ │ │ ├── TextInput.js │ │ │ │ ├── NumberInput.js │ │ │ │ ├── SelectInput.js │ │ │ │ └── NavMenu.js │ │ │ ├── about │ │ │ │ └── AboutPage.js │ │ │ ├── login │ │ │ │ └── LoginPage.js │ │ │ ├── forms │ │ │ │ └── form.css │ │ │ ├── selectors │ │ │ │ └── selectors.js │ │ │ ├── register │ │ │ │ └── RegisterPage.js │ │ │ ├── course │ │ │ │ ├── CourseListRow.js │ │ │ │ ├── CourseList.js │ │ │ │ ├── CoursesPage.js │ │ │ │ └── CourseForm.js │ │ │ ├── department │ │ │ │ ├── DepartmentListRow.js │ │ │ │ ├── DepartmentList.js │ │ │ │ └── DepartmentsPage.js │ │ │ └── contact │ │ │ │ └── ContactPage.js │ │ ├── reducers │ │ │ ├── initialState.js │ │ │ ├── index.js │ │ │ ├── departmentReducer.js │ │ │ ├── ajaxStatusReducer.js │ │ │ └── courseReducer.js │ │ ├── index.css │ │ ├── App.test.js │ │ ├── actions │ │ │ ├── ajaxStatusActions.js │ │ │ ├── actionTypes.js │ │ │ ├── departmentActions.js │ │ │ └── courseActions.js │ │ ├── store │ │ │ └── configureStore.js │ │ ├── index.js │ │ └── App.js │ ├── public │ │ ├── favicon.ico │ │ ├── manifest.json │ │ └── index.html │ ├── .gitignore │ └── package.json ├── package-lock.json ├── appsettings.Development.json ├── appsettings.json ├── Properties │ └── launchSettings.json ├── ReadMe.md ├── ReactApiProfile.cs ├── DTO │ └── CreateDepartmentDTO.cs ├── Program.cs └── Controllers │ ├── CoursesController.cs │ └── DepartmentController.cs ├── ContosoUniversity.Common ├── Readme.md ├── Interfaces │ ├── IUnitOfWork.cs │ ├── IDbInitializer.cs │ ├── IPersonRepository.cs │ └── IRepository.cs ├── ISmsSender.cs ├── AuthMessageSenderOptions.cs ├── IEmailSender.cs ├── SMSOptions.cs ├── OperatingSystem.cs ├── DTO │ └── DepartmentDTO.cs ├── Data │ ├── SampleData.cs │ └── ApiInitializer.cs ├── Repositories │ ├── PersonRepository.cs │ └── Repository.cs ├── ContosoUniversity.Common.csproj └── MessageServices.cs ├── ContosoUniversity.Api ├── wwwroot │ └── swagger │ │ └── ui │ │ ├── css │ │ ├── custom.css.map │ │ └── custom.css │ │ ├── swagger-ui.css.map │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── oauth2-redirect.html ├── Readme.md ├── appsettings.json ├── ApiProfile.cs ├── DTO │ └── CreateDepartmentDTO.cs ├── appsettings.Development.json ├── Properties │ └── launchSettings.json ├── Program.cs └── ContosoUniversity.Api.csproj ├── ContosoUniversity.Api.Tests ├── Readme.md ├── appsettings.Testing.json ├── ApiIntegrationTests.cs └── ContosoUniversity.Api.Tests.csproj ├── ContosoUniversity.Data.Tests ├── Readme.md └── ContosoUniversity.Data.Tests.csproj ├── ContosoUniversity.Web.Tests ├── Readme.md ├── Views │ └── ChromeViews.cs └── ContosoUniversity.Web.Tests.csproj ├── global.json ├── .travis.yml ├── ContosoUniversity.Web.IntegrationTests ├── Readme.md ├── ContosoUniversity.Web.IntegrationTests.csproj ├── StaticPagesTests.cs └── Helpers │ └── Utilities.cs ├── .vscode ├── tasks.json └── launch.json ├── LICENSE ├── ContosoUniversity.Test ├── ContosoUniversity.Test.csproj └── HttpClientExtensions.cs ├── README.md └── .gitattributes /ContosoUniversity.Web/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/Readme.md: -------------------------------------------------------------------------------- 1 | ### ContosoUniversity.Data (WIP) -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/api/delay.js: -------------------------------------------------------------------------------- 1 | export default 1000; -------------------------------------------------------------------------------- /ContosoUniversity.Web/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "wwwroot/lib" 3 | } 4 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your Javascript code. 2 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1 3 | } 4 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/Readme.md: -------------------------------------------------------------------------------- 1 | ### ContosoUniversity.Common 2 | 3 | Shared library between Api and Web projects -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/Layout.css: -------------------------------------------------------------------------------- 1 | .body-content { 2 | padding-top: 50px; 3 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Web/wwwroot/favicon.ico -------------------------------------------------------------------------------- /ContosoUniversity.Api/wwwroot/swagger/ui/css/custom.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""} -------------------------------------------------------------------------------- /ContosoUniversity.Api/wwwroot/swagger/ui/swagger-ui.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""} -------------------------------------------------------------------------------- /ContosoUniversity.Data/Enums/Grade.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Data.Enums 2 | { 3 | public enum Grade 4 | { 5 | A,B,C,D,F 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/reducers/initialState.js: -------------------------------------------------------------------------------- 1 | export default { 2 | departments: [], 3 | courses: [], 4 | ajaxCallsInProgress: 0 5 | }; -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Spa.React/ClientApp/public/favicon.ico -------------------------------------------------------------------------------- /ContosoUniversity.Web/Resources/contosoU2017_schema1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Web/Resources/contosoU2017_schema1.png -------------------------------------------------------------------------------- /ContosoUniversity.Api/wwwroot/swagger/ui/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Api/wwwroot/swagger/ui/favicon-16x16.png -------------------------------------------------------------------------------- /ContosoUniversity.Api/wwwroot/swagger/ui/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Api/wwwroot/swagger/ui/favicon-32x32.png -------------------------------------------------------------------------------- /ContosoUniversity.Common/Interfaces/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Common.Interfaces 2 | { 3 | public interface IUnitOfWork 4 | { 5 | void Commit(); 6 | } 7 | } -------------------------------------------------------------------------------- /ContosoUniversity.Common/Interfaces/IDbInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Common.Interfaces 2 | { 3 | public interface IDbInitializer 4 | { 5 | void Initialize(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Readme.md: -------------------------------------------------------------------------------- 1 | ## ContosoUniversity.Web 2 | 3 | ### Run Mvc App 4 | ``` 5 | git clone https://github.com/alimon808/contoso-university.git 6 | cd ContosoUnversity.Web 7 | dotnet run 8 | ``` -------------------------------------------------------------------------------- /ContosoUniversity.Api/Readme.md: -------------------------------------------------------------------------------- 1 | ## ContosoUniversity.Api 2 | 3 | ### Run Api with Swagger UI 4 | ``` 5 | git clone https://github.com/alimon808/contoso-university.git 6 | cd ContosoUniversity.Api 7 | dotnet run 8 | ``` -------------------------------------------------------------------------------- /ContosoUniversity.Api.Tests/Readme.md: -------------------------------------------------------------------------------- 1 | ## ContosoUniversity.Api.Tests 2 | 3 | ### Run tests 4 | ``` 5 | git clone https://github.com/alimon808/contoso-university.git 6 | cd ContosoUniversity.Api.Tests 7 | dotnet test 8 | ``` -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/ApplicationUser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | 3 | namespace ContosoUniversity.Data.Entities 4 | { 5 | public class ApplicationUser : IdentityUser 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using ContosoUniversity.Web 2 | @using ContosoUniversity.Data.Entities 3 | @using Microsoft.AspNetCore.Identity 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity2020;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | } 5 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/AccessDenied.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Access Denied"; 3 | } 4 | 5 |

@ViewData["Title"]

6 |

You are not authorized. Contact the administrator for assistance.

7 | 8 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/about/AboutPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function() { 4 | return( 5 |
6 |

About Page

7 |
8 | ); 9 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/login/LoginPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function(){ 4 | return( 5 |
6 |

Login Page

7 |
8 | ); 9 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alimon808/contoso-university/HEAD/ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/index.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 767px) { 2 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */ 3 | body { 4 | padding-top: 50px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/ForgotPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Forgot Password Confirmation"; 3 | } 4 | 5 |

@ViewData["Title"]

6 |

7 | Please check your email to reset your password. 8 |

-------------------------------------------------------------------------------- /ContosoUniversity.Common/ISmsSender.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace ContosoUniversity.Common 4 | { 5 | public interface ISmsSender 6 | { 7 | Task SendSmsAsync(string email, string message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/ExternalLoginFailure.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Login Failure"; 3 | } 4 | 5 |
6 |

@ViewData["Title"]

7 |

Unsuccessful login with service.

8 |
9 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DTO/CourseAssignmentsDTO.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Data.DTO 2 | { 3 | public class CourseAssignmentsDTO 4 | { 5 | public int InstructorID { get; set; } 6 | public int CourseID { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ContosoUniversity.Data.Tests/Readme.md: -------------------------------------------------------------------------------- 1 | ## ContosoUniversity.Data.Tests 2 | Test project for ContosoUniversity.Data. 3 | 4 | ### Run tests 5 | ``` 6 | git clone https://github.com/alimon808/contoso-university.git 7 | cd ContosoUniversity.Data.Tests 8 | dotnet test 9 | ``` -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/forms/form.css: -------------------------------------------------------------------------------- 1 | input, textarea, select { 2 | max-width: 280px; 3 | } 4 | 5 | .form-control { 6 | width: 280px !important; 7 | } 8 | 9 | .form-control-feedback { 10 | left: 265px !important; 11 | } -------------------------------------------------------------------------------- /ContosoUniversity.Common/AuthMessageSenderOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Common 2 | { 3 | public class AuthMessageSenderOptions 4 | { 5 | public string SendGridUser { get; set; } 6 | public string SendGridKey { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/IEmailSender.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace ContosoUniversity.Common 4 | { 5 | public interface IEmailSender 6 | { 7 | Task SendEmailAsync(string email, string subject, string message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asp.net", 3 | "private": true, 4 | "dependencies": { 5 | "bootstrap": "3.3.7", 6 | "jquery": "2.2.0", 7 | "jquery-validation": "1.14.0", 8 | "jquery-validation-unobtrusive": "3.2.6" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function () { 4 | return ( 5 | 8 | ); 9 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/Lockout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Locked Out"; 3 | } 4 | 5 |
6 |

Locked Out

7 |

This account has been locked out, please try again later.

8 |
9 | 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/actions/ajaxStatusActions.js: -------------------------------------------------------------------------------- 1 | import * as types from './actionTypes'; 2 | 3 | export function beginAjaxCall() { 4 | return {type: types.BEGIN_AJAX_CALL}; 5 | } 6 | 7 | export function ajaxCallError() { 8 | return {type: types.AJAX_CALL_ERROR}; 9 | } -------------------------------------------------------------------------------- /ContosoUniversity.Data/DTO/OfficeAssignmentDTO.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Data.DTO 2 | { 3 | public class OfficeAssignmentDTO 4 | { 5 | public int ID { get; set; } 6 | public int InstructorID { get; set; } 7 | public string Location { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/selectors/selectors.js: -------------------------------------------------------------------------------- 1 | export function departmentsFormattedForDropdown(departments){ 2 | return departments.map(department => { 3 | return { 4 | value: department.id, 5 | text: department.name 6 | }; 7 | }); 8 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/Helpers/IUrlHelperAdaptor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace ContosoUniversity.Web.Helpers 4 | { 5 | public interface IUrlHelperAdaptor 6 | { 7 | string Action(IUrlHelper helper, string action, string controller, object values, string protocol); 8 | } 9 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/ManageIndexViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Web.ViewModels 2 | { 3 | public class ManageIndexViewModel 4 | { 5 | public bool HasPassword { get; set; } 6 | public string PhoneNumber { get; set; } 7 | public bool TwoFactor { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /ContosoUniversity.Data/AdminIdentityOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Data 2 | { 3 | public class AdminIdentityOptions 4 | { 5 | public string Role { get; } = "Administrator"; 6 | public string UserName { get; set; } 7 | public string Password { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/SMSOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Common 2 | { 3 | public class SMSOptions 4 | { 5 | public string SMSAccountIdentification { get; set; } 6 | public string SMSAccountPassword { get; set; } 7 | public string SMSAccountFrom { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/ResetPasswordConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Reset password confirmation"; 3 | } 4 | 5 |

@ViewData["Title"]

6 | 7 |

8 | Your password has been reset. Please Click here to log in. 9 |

10 | 11 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity2017;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Warning" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/ConfirmEmail.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Confirm Email"; 3 | } 4 | 5 |

@ViewData["Title"]

6 |
7 |

8 | Thank you for confirming yoru email. Please Click here to login 9 |

10 |
11 | 12 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Models/SchoolViewModels/AssignedCourseData.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Models.SchoolViewModels 2 | { 3 | public class AssignedCourseData 4 | { 5 | public int CourseID { get; set; } 6 | public string Title { get; set; } 7 | public bool Assigned { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/ForgotPasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class ForgotPasswordViewModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ContosoUniversity.Web.Tests/Readme.md: -------------------------------------------------------------------------------- 1 | ## ContosoUniversity.Web.Test 2 | Unit test project for [ContosoUniversity.Web](https://github.com/alimon808/contoso-university/tree/master/ContosoUniversity.Web) 3 | 4 | ### Run tests 5 | ``` 6 | git clone https://github.com/alimon808/contoso-university.git 7 | cd ContosoUniversity.Web.Test 8 | dotnet test 9 | ``` -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/register/RegisterPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RegisterForm from '../forms/Register'; 3 | 4 | export default function () { 5 | return ( 6 |
7 |

Register

8 | 9 |
10 | ); 11 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ContosoUniversity": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | }, 9 | "applicationUrl": "http://localhost:5000" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/ExternalLoginConfirmationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class ExternalLoginConfirmationViewModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ 3 | "ContosoUniversity.Web", 4 | "ContosoUniversity.Web.Tests", 5 | "ContosoUniversity.Api", 6 | "ContosoUniversity.Api.Tests", 7 | "ContosoUniversity.Data", 8 | "ContosoUniversity.Data.Tests" 9 | ], 10 | "sdk": { 11 | "version": "2.1.300" 12 | } 13 | } -------------------------------------------------------------------------------- /ContosoUniversity.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity2017;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | "Logging": { 6 | "IncludeScopes": false, 7 | "LogLevel": { 8 | "Default": "Warning" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DTO/InstructorDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ContosoUniversity.Data.DTO 4 | { 5 | public class InstructorDTO 6 | { 7 | public int ID { get; set; } 8 | public string FirstMidName { get; set; } 9 | public string LastName { get; set; } 10 | public DateTime HireDate { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DTO/StudentDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ContosoUniversity.Data.DTO 4 | { 5 | public class StudentDTO 6 | { 7 | public int ID { get; set; } 8 | public string FirstMidName { get; set; } 9 | public string LastName { get; set; } 10 | public DateTime EnrollmentDate { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/CourseAssignment.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Data.Entities 2 | { 3 | public class CourseAssignment : BaseEntity 4 | { 5 | public int InstructorID { get; set; } 6 | public int CourseID { get; set; } 7 | public Instructor Instructor { get; set; } 8 | public Course Course { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import {createStore, applyMiddleware} from 'redux'; 2 | import rootReducer from '../reducers'; 3 | import thunk from 'redux-thunk'; 4 | 5 | export default function configureStore(initialState){ 6 | return createStore( 7 | rootReducer, 8 | initialState, 9 | applyMiddleware(thunk) 10 | ); 11 | } -------------------------------------------------------------------------------- /ContosoUniversity.Data/DTO/CourseDTO.cs: -------------------------------------------------------------------------------- 1 | namespace ContosoUniversity.Data.DTO 2 | { 3 | public class CourseDTO 4 | { 5 | public int ID { get; set; } 6 | public int CourseNumber { get; set; } 7 | public string Title { get; set; } 8 | public int Credits { get; set; } 9 | public int DepartmentID { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DTO/EnrollmentDTO.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Enums; 2 | 3 | namespace ContosoUniversity.Data.DTO 4 | { 5 | public class EnrollmentDTO 6 | { 7 | public int ID { get; set; } 8 | public int CourseID { get; set; } 9 | public int StudentID { get; set; } 10 | public Grade? Grade { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/AddPhoneNumberViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class AddPhoneNumberViewModel 6 | { 7 | [Required] 8 | [Phone] 9 | [Display(Name = "Phone number")] 10 | public string PhoneNumber { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity2017;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | "EnableHttps": "true", 6 | "Logging": { 7 | "IncludeScopes": false, 8 | "LogLevel": { 9 | "Default": "Warning" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | mono: none 3 | dotnet: 2.1.300 4 | 5 | install: 6 | - dotnet restore 7 | 8 | script: 9 | - dotnet build 10 | - dotnet test ContosoUniversity.Data.Tests/ContosoUniversity.Data.Tests.csproj 11 | - dotnet test ContosoUniversity.Web.Tests/ContosoUniversity.Web.Tests.csproj 12 | - dotnet test ContosoUniversity.Api.Tests/ContosoUniversity.Api.Tests.csproj -------------------------------------------------------------------------------- /ContosoUniversity.Data/OperatingSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace ContosoUniversity.Data 4 | { 5 | public static class OperatingSystem 6 | { 7 | public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 8 | public static bool IsMacOs() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); 9 | } 10 | } -------------------------------------------------------------------------------- /ContosoUniversity.Common/Interfaces/IPersonRepository.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | using System.Linq; 4 | 5 | namespace ContosoUniversity.Common.Interfaces 6 | { 7 | public interface IPersonRepository : IRepository where T : Person 8 | { 9 | IQueryable GetByLastName(string lastName); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/OperatingSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace ContosoUniversity.Common 4 | { 5 | public static class OperatingSystem 6 | { 7 | public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 8 | public static bool IsMacOs() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); 9 | } 10 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ReadMe.md: -------------------------------------------------------------------------------- 1 | ## ContosoUniversity.Spa.React 2 | Single Page Application (SPA) using React front-end framework and WebApi backend 3 | 4 | ### Prerequisites 5 | - .NET Core SDK v2.1.0 or later 6 | - Node.js v6.0 or later 7 | 8 | ### Run App 9 | ``` 10 | git clone https://github.com/alimon808/contoso-university.git 11 | cd ContosoUniversity.Spa.React 12 | dotnet run 13 | ``` -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | import courses from './courseReducer'; 3 | import departments from './departmentReducer'; 4 | import ajaxCallsInProgress from './ajaxStatusReducer'; 5 | 6 | const rootReducer = combineReducers({ 7 | courses, 8 | departments, 9 | ajaxCallsInProgress 10 | }); 11 | 12 | export default rootReducer; -------------------------------------------------------------------------------- /ContosoUniversity.Web/Models/SchoolViewModels/EnrollmentDateGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.Models.SchoolViewModels 5 | { 6 | public class EnrollmentDateGroup 7 | { 8 | [DataType(DataType.Date)] 9 | public DateTime? EnrollmentDate { get; set; } 10 | public int StudentCount { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Enums/ManageMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ContosoUniversity.Web.Enums 7 | { 8 | public enum ManageMessage 9 | { 10 | ChangePasswordSuccess, 11 | Error, 12 | AddPhoneSuccess, 13 | RemovePhoneSuccess, 14 | SetTwoFactorSuccess 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Pages/Contact.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.RazorPages; 2 | using System; 3 | 4 | namespace ContosoUniversity.Web.Pages 5 | { 6 | public class ContactModel : PageModel 7 | { 8 | public string Message { get; private set; } = "PageModel in C#"; 9 | public void OnGet() 10 | { 11 | Message += $" Server time is {DateTime.Now}"; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/actions/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const BEGIN_AJAX_CALL = 'BEGIN_AJAX_CALL'; 2 | export const AJAX_CALL_ERROR = 'AJAX_CALL_ERROR'; 3 | 4 | export const LOAD_COURSES_SUCCESS = 'LOAD_COURSES_SUCCESS'; 5 | export const CREATE_COURSE_SUCCESS = 'CREATE_COURSE_SUCCESS'; 6 | export const UPDATE_COURSE_SUCCESS = 'UPDATE_COURSE_SUCCESS'; 7 | export const LOAD_DEPARTMENTS_SUCCESS = 'LOAD_DEPARTMENTS_SUCCESS'; 8 | -------------------------------------------------------------------------------- /ContosoUniversity.Web.IntegrationTests/Readme.md: -------------------------------------------------------------------------------- 1 | ## ContosoUniversity.Web.IntegrationTests 2 | 3 | ### Run Integration Tests from command line 4 | ``` 5 | git clone https://github.com/alimon808/contoso-university.git 6 | cd ContosoUniverity.Web.IntegrationTests 7 | dotnet test 8 | ``` 9 | 10 | ### References 11 | [Integration Test in Asp.Net Core](https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1) -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/TokenViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class TokenViewModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | 11 | [Required] 12 | [DataType(DataType.Password)] 13 | public string Password { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/reducers/departmentReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from '../actions/actionTypes'; 2 | import initialState from './initialState'; 3 | 4 | export default function departmentReducer(state = initialState.departments, action){ 5 | switch(action.type) { 6 | case types.LOAD_DEPARTMENTS_SUCCESS: 7 | return action.departments; 8 | default: 9 | return state; 10 | } 11 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/VerifyPhoneNumberViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class VerifyPhoneNumberViewModel 6 | { 7 | [Required] 8 | public string Code { get; set; } 9 | 10 | [Required] 11 | [Phone] 12 | [Display(Name = "Phone number")] 13 | public string PhoneNumber { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "ContosoUniversity_Spa_React", 3 | "name": "ContosoUniversity_Spa_React", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ReactApiProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using ContosoUniversity.Spa.React.DTO; 3 | using ContosoUniversity.Common.DTO; 4 | using ContosoUniversity.Data.Entities; 5 | 6 | namespace ContosoUniversity.Spa.React 7 | { 8 | public class ReactApiProfile : Profile 9 | { 10 | public ReactApiProfile() 11 | { 12 | CreateMap().ReverseMap(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ContosoUniversity.Api/wwwroot/swagger/ui/css/custom.css: -------------------------------------------------------------------------------- 1 | .swagger-section #header { 2 | border-bottom: 1px solid #000000; 3 | font-style: normal; 4 | font-weight: 400; 5 | font-family: "Segoe UI Light","Segoe WP Light","Segoe UI","Segoe WP",Tahoma,Arial,sans-serif; 6 | background-color: black; 7 | } 8 | 9 | .swagger-section #header h1 { 10 | text-align: center; 11 | font-size: 20px; 12 | color: white; 13 | } 14 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Models/SchoolViewModels/InstructorIndexData.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Entities; 2 | using System.Collections.Generic; 3 | 4 | namespace ContosoUniversity.Models.SchoolViewModels 5 | { 6 | public class InstructorIndexData 7 | { 8 | public IEnumerable Instructors { get; set; } 9 | public IEnumerable Courses { get; set; } 10 | public IEnumerable Enrollments { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/SendCodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Rendering; 2 | using System.Collections.Generic; 3 | 4 | namespace ContosoUniversity.Web.ViewModels 5 | { 6 | public class SendCodeViewModel 7 | { 8 | public string SelectedProvider { get; set; } 9 | public ICollection Providers { get; set; } 10 | public string ReturnUrl { get; set; } 11 | public bool RememberMe { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}.btn-bracketed::before{display:inline-block;content:"[";padding-right:.5em}.btn-bracketed::after{display:inline-block;content:"]";padding-left:.5em}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /ContosoUniversity.Common/DTO/DepartmentDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.Common.DTO 5 | { 6 | public class DepartmentDTO 7 | { 8 | [Required] 9 | public int ID { get; set; } 10 | public int InstructorID { get; set; } 11 | public string Name { get; set; } 12 | public decimal Budget { get; set; } 13 | public DateTime StartDate { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/DepartmentEditViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.ViewModels 4 | { 5 | public class DepartmentEditViewModel : DepartmentBaseViewModel 6 | { 7 | [Required] 8 | public int ID { get; set; } 9 | 10 | public string RowVersion { get; set; } 11 | 12 | [Display(Name = "Administrator")] 13 | public int InstructorID { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/OfficeAssignment.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Data.Entities 4 | { 5 | public class OfficeAssignment : BaseEntity 6 | { 7 | //[Key] 8 | public int InstructorID { get; set; } 9 | 10 | [StringLength(50)] 11 | [Display(Name = "Office Location")] 12 | public string Location { get; set; } 13 | 14 | public Instructor Instructor { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /ContosoUniversity.Api/ApiProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using ContosoUniversity.Api.DTO; 3 | using ContosoUniversity.Common.DTO; 4 | using ContosoUniversity.Data.Entities; 5 | 6 | namespace ContosoUniversity.Api 7 | { 8 | public class ApiProfile : Profile 9 | { 10 | public ApiProfile() 11 | { 12 | CreateMap().ReverseMap(); 13 | CreateMap().ReverseMap(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.Data.Entities 5 | { 6 | public abstract class BaseEntity 7 | { 8 | public int ID { get; set; } 9 | public DateTime AddedDate { get; set; } = DateTime.UtcNow; 10 | public DateTime ModifiedDate { get; set; } = DateTime.UtcNow; 11 | 12 | [Timestamp] 13 | public byte[] RowVersion { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/DepartmentCreateViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.ViewModels 5 | { 6 | public class DepartmentCreateViewModel : DepartmentBaseViewModel 7 | { 8 | [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] 9 | public override DateTime StartDate { get; set; } 10 | 11 | public int InstructorID { get; set; } = 0; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ContosoUniversity.Api/DTO/CreateDepartmentDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.Api.DTO 5 | { 6 | public class CreateDepartmentDTO 7 | { 8 | [Required] 9 | public int InstructorID { get; set; } 10 | [Required] 11 | public string Name { get; set; } 12 | 13 | public decimal Budget { get; set; } = 0; 14 | public DateTime StartDate { get; set; } = DateTime.UtcNow; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/RegisterViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class RegisterViewModel 6 | { 7 | [DataType(DataType.EmailAddress)] 8 | public string Email { get; set; } 9 | 10 | [DataType(DataType.Password)] 11 | public string Password { get; set; } 12 | 13 | [DataType(DataType.Password)] 14 | public string ConfirmPassword { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/DTO/CreateDepartmentDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.Spa.React.DTO 5 | { 6 | public class CreateDepartmentDTO 7 | { 8 | [Required] 9 | public int InstructorID { get; set; } 10 | [Required] 11 | public string Name { get; set; } 12 | 13 | public decimal Budget { get; set; } = 0; 14 | public DateTime StartDate { get; set; } = DateTime.UtcNow; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class LoginViewModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | 11 | [Required] 12 | [DataType(DataType.Password)] 13 | public string Password { get; set; } 14 | 15 | [Display(Name = "Remember me?")] 16 | public bool RememberMe { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/Enrollment.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Enums; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.Data.Entities 5 | { 6 | public class Enrollment : BaseEntity 7 | { 8 | public int CourseID { get; set; } 9 | public int StudentID { get; set; } 10 | 11 | [DisplayFormat(NullDisplayText = "No Grade")] 12 | public Grade? Grade { get; set; } 13 | public Course Course { get; set; } 14 | public Student Student { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/Student.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace ContosoUniversity.Data.Entities 6 | { 7 | public class Student : Person 8 | { 9 | [Display(Name = "Enrollment Date")] 10 | [DataType(DataType.Date)] 11 | [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] 12 | public DateTime EnrollmentDate { get; set; } 13 | 14 | public ICollection Enrollments { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/DepartmentDetailsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.ViewModels 5 | { 6 | public class DepartmentDetailsViewModel : DepartmentBaseViewModel 7 | { 8 | public int ID { get; set; } 9 | 10 | public override string Name { get; set; } 11 | 12 | [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")] 13 | public override DateTime StartDate { get; set; } 14 | 15 | public int InstructorID { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/course/CourseListRow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {Link} from 'react-router-dom'; 4 | 5 | const CourseListRow = ({course}) => { 6 | return ( 7 | 8 | {course.courseNumber} 9 | {course.title} 10 | {course.credits} 11 | 12 | ); 13 | }; 14 | 15 | CourseListRow.propTypes = { 16 | course: PropTypes.object.isRequired 17 | }; 18 | 19 | export default CourseListRow; -------------------------------------------------------------------------------- /ContosoUniversity.Web/IModelBindingHelperAdaptor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace ContosoUniversity.Web 7 | { 8 | public interface IModelBindingHelperAdaptor 9 | { 10 | Task TryUpdateModelAsync(ControllerBase controller, TModel model, string prefix, params Expression>[] includeExpressions) where TModel : class; 11 | Task TryUpdateModelAsync(ControllerBase controller, TModel model) where TModel : class; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/reducers/ajaxStatusReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from '../actions/actionTypes'; 2 | import initialState from './initialState'; 3 | 4 | function actionTypeEndsInSuccess(type) { 5 | return type.substring(type.length - 8) === '_SUCCESS'; 6 | } 7 | 8 | export default function ajaxStatusReducer(state = initialState.ajaxCallsInProgress, action) { 9 | if(action.type === types.BEGIN_AJAX_CALL) { 10 | return state + 1; 11 | } else if (action.type === types.AJAX_CALL_ERROR || actionTypeEndsInSuccess(action.type)) { 12 | return state - 1; 13 | } 14 | 15 | return state; 16 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/VerifyCodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class VerifyCodeViewModel 6 | { 7 | [Required] 8 | public string Provider { get; set; } 9 | 10 | [Required] 11 | public string Code { get; set; } 12 | 13 | 14 | public string ReturnUrl { get; set; } 15 | 16 | [Display(Name = "Remember me?")] 17 | public bool RememberMe { get; set; } 18 | 19 | [Display(Name = "Remember this browser?")] 20 | public bool RememberBrowser { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/Instructor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace ContosoUniversity.Data.Entities 6 | { 7 | public class Instructor : Person 8 | { 9 | [DataType(DataType.Date)] 10 | [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] 11 | [Display(Name = "Hire Date")] 12 | public DateTime HireDate { get; set; } 13 | 14 | public ICollection CourseAssignments { get; set; } 15 | public OfficeAssignment OfficeAssignment { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/reducers/courseReducer.js: -------------------------------------------------------------------------------- 1 | import * as types from '../actions/actionTypes'; 2 | import initialState from './initialState'; 3 | 4 | export default function courseReducer(state = initialState.courses, action){ 5 | switch(action.type){ 6 | case types.LOAD_COURSES_SUCCESS: 7 | return action.courses; 8 | case types.CREATE_COURSE_SUCCESS: 9 | return [...state, Object.assign({}, action.course)]; 10 | case types.UPDATE_COURSE_SUCCESS: 11 | return [...state, Object.assign({}, action.course)]; 12 | default: 13 | return state; 14 | } 15 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/Pages/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ContosoUniversity.Web.Pages.ContactModel 3 | @{ 4 | ViewData["Title"] = "Contact"; 5 | Layout = "~/Views/Shared/_Layout.cshtml"; 6 | } 7 | 8 |

@ViewData["Title"]

9 |
@Model.Message
10 | 11 |
12 | One Microsoft Way
13 | Redmond, WA 98052-6399
14 | P: 15 | 425.555.0100 16 |
17 | 18 |
19 | Support: Support@example.com
20 | Marketing: Marketing@example.com 21 |
-------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/department/DepartmentListRow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'moment'; 4 | import Numeral from 'numeral'; 5 | 6 | const DepartmentListRow = ({department}) => { 7 | return( 8 | 9 | {department.name} 10 | {Numeral(department.budget).format('$0,0.00')} 11 | {Moment(department.startDate).format('DD/MM/YYYY')} 12 | 13 | ); 14 | }; 15 | 16 | DepartmentListRow.propTypes = { 17 | department: PropTypes.object.isRequired 18 | } 19 | 20 | export default DepartmentListRow; -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/actions/departmentActions.js: -------------------------------------------------------------------------------- 1 | import * as types from './actionTypes'; 2 | import departmentApi from '../api/mockDepartmentApi'; 3 | import {beginAjaxCall} from './ajaxStatusActions'; 4 | 5 | export function loadDepartmentsSuccess(departments) { 6 | return {type: types.LOAD_DEPARTMENTS_SUCCESS, departments}; 7 | } 8 | 9 | export function loadDepartments(){ 10 | return function(dispatch){ 11 | dispatch(beginAjaxCall()); 12 | return departmentApi.getAllDepartments().then(departments => { 13 | dispatch(loadDepartmentsSuccess(departments)); 14 | }).catch(error => { 15 | throw(error); 16 | }); 17 | } 18 | } -------------------------------------------------------------------------------- /ContosoUniversity.Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "Administrator": { 11 | "UserName": "abc@example.com", 12 | "Password": "DO-NOT-STORE-WEAK-P@ssw0rd!" 13 | }, 14 | "Authentication": { 15 | "Google": { 16 | "ClientId": "DO-NOT-STORE-HERE", 17 | "ClientSecret": "DO-NOT-STORE-HERE" 18 | }, 19 | "Tokens": { 20 | "Issuer": "https://localhost:20650/", 21 | "Audience": "http://localhost:6188/", 22 | "Key": "DO-NOT-STORE-HERE-1absa-adljkzer-zldr" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/DepartmentBaseViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace ContosoUniversity.ViewModels 5 | { 6 | public abstract class DepartmentBaseViewModel 7 | { 8 | [StringLength(50, MinimumLength = 3)] 9 | public virtual string Name { get; set; } 10 | public virtual decimal Budget { get; set; } 11 | 12 | [DataType(DataType.Date)] 13 | [Display(Name = "Start Date")] 14 | [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] 15 | public virtual DateTime StartDate { get; set; } 16 | 17 | public virtual string Administrator { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DbContexts/ApiContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace ContosoUniversity.Data.DbContexts 4 | { 5 | public class ApiContext : DbContext 6 | { 7 | public ApiContext(DbContextOptions options) : base(options) 8 | { 9 | } 10 | 11 | protected override void OnModelCreating(ModelBuilder modelBuilder) 12 | { 13 | string schema = "Contoso"; 14 | if (OperatingSystem.IsMacOs()) 15 | { 16 | schema = null; 17 | } 18 | 19 | var config = new DbContextConfig(); 20 | config.ApplicationContextConfig(modelBuilder, schema); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Query.Internal; 3 | using System.Linq; 4 | 5 | namespace ContosoUniversity 6 | { 7 | // see issue @ https://stackoverflow.com/questions/27038253/mock-asnotracking-entity-framework 8 | // workaround @ https://github.com/aspnet/EntityFramework/issues/7937 9 | public static class QueryableExtensions 10 | { 11 | public static IQueryable AsGatedNoTracking(this IQueryable source) where T : class 12 | { 13 | if (source.Provider is EntityQueryProvider) 14 | return source.AsNoTracking(); 15 | return source; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /ContosoUniversity.Api.Tests/appsettings.Testing.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "IdentityUser": { 11 | "Role": "Administrator", 12 | "UserName": "abc@example.com", 13 | "Password": "DO-NOT-STORE-HERE-P@ssw0rd!" 14 | }, 15 | "Authentication": { 16 | "Google": { 17 | "ClientId": "DO-NOT-STORE-HERE", 18 | "ClientSecret": "DO-NOT-STORE-HERE" 19 | }, 20 | "Tokens": { 21 | "Issuer": "https://localhost/", 22 | "Audience": "https://localhost/", 23 | "Key": "DO-NOT-STORE-HERE-1absa-adljkzer-zldr" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/Interfaces/IRepository.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | using System.Data.Common; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ContosoUniversity.Common.Interfaces 8 | { 9 | public interface IRepository where T : BaseEntity 10 | { 11 | IQueryable GetAll(); 12 | IQueryable Get(int id); 13 | void Add(T entity); 14 | Task AddAsync(T entity); 15 | void Update(T entity, byte[] rowVersion); 16 | void Delete(T entity); 17 | Task ExecuteSqlCommandAsync(string queryString); 18 | Task SaveChangesAsync(); 19 | DbConnection GetDbConnection(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Pages/About.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ContosoUniversity.Web.Pages.AboutModel 3 | @{ 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | ViewData["Title"] = "Student Body Statistics"; 6 | } 7 | 8 |

@ViewData["Title"].

9 | 10 | 11 | 12 | 15 | 18 | 19 | @foreach (var item in Model.StudentEnrollments) 20 | { 21 | 22 | 25 | 28 | 29 | } 30 |
13 | Enrollment Date 14 | 16 | Students 17 |
23 | @Html.DisplayFor(modelItem => item.EnrollmentDate) 24 | 26 | @item.StudentCount 27 |
31 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/contact/ContactPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function () { 4 | return ( 5 |
6 |

Contact

7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P:425.555.0100 11 |
12 |
13 | Support: Support@example.com
14 | Marketing: Marketing@example.com
15 |
16 |
17 | ); 18 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace ContosoUniversity.Spa.React 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/SendCode.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.SendCodeViewModel 2 | @{ 3 | ViewData["Title"] = "Send Code"; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 |
13 | 14 |
15 |
16 | Select Two-Factor Authentication Provider: 17 | 18 | 19 |
20 |
21 |
22 | 23 | -------------------------------------------------------------------------------- /ContosoUniversity.Data.Tests/ContosoUniversity.Data.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/Data/SampleData.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.DTO; 2 | using ContosoUniversity.Common.DTO; 3 | using System.Collections.Generic; 4 | 5 | namespace ContosoUniversity.Common.Data 6 | { 7 | public class SampleData 8 | { 9 | public List Enrollments { get; set; } 10 | public List Courses { get; set; } 11 | public List CourseAssignments { get; set; } 12 | public List Departments { get; set; } 13 | public List Students { get; set; } 14 | public List OfficeAssignments { get; set; } 15 | public List Instructors { get; set; } 16 | public bool SaveToExternalFile { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/Layout.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Col, Grid, Row } from 'react-bootstrap'; 3 | import { NavMenu } from './NavMenu'; 4 | import './Layout.css'; 5 | import Footer from './Footer'; 6 | 7 | 8 | export class Layout extends Component { 9 | displayName = Layout.name 10 | render() { 11 | return ( 12 |
13 | 14 | 15 | 16 | 17 | {this.props.children} 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 |
27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | 8 |

Development Mode

9 |

10 | Swapping to Development environment will display more detailed information about the error that occurred. 11 |

12 |

13 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 14 |

15 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/course/CourseList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types' 3 | import CourseListRow from './CourseListRow'; 4 | 5 | const CourseList = ({courses}) => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {courses.map(course => )} 17 | 18 |
NumberTitleCredits
19 | ); 20 | }; 21 | 22 | CourseList.propTypes = { 23 | courses: PropTypes.array.isRequired 24 | }; 25 | 26 | export default CourseList; -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Courses/UpdateCourseCredits.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "UpdateCourseCredits"; 3 | } 4 | 5 |

Update Course Credits

6 | @if (ViewData["RowsAffected"] == null) 7 | { 8 |
9 |
10 |

11 | Enter a number to multiply every course's credits by: @Html.TextBox("multiplier") 12 |

13 |

14 | 15 |

16 |
17 |
18 | } 19 | 20 | @if (ViewData["RowsAffected"] != null) 21 | { 22 |

23 | Number of rows updated: @ViewData["RowsAffected"] 24 |

25 | } 26 | 27 |
28 | @Html.ActionLink("Back to List", "Index") 29 |
-------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/Course.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace ContosoUniversity.Data.Entities 6 | { 7 | public class Course : BaseEntity 8 | { 9 | [Display(Name = "Number")] 10 | public int CourseNumber { get; set; } 11 | 12 | [StringLength(50, MinimumLength = 3)] 13 | public string Title { get; set; } 14 | 15 | [Range(0, 5)] 16 | public int Credits { get; set; } 17 | 18 | public int DepartmentID { get; set; } 19 | 20 | public Department Department { get; set; } 21 | public ICollection Enrollments { get; set; } 22 | public ICollection CourseAssignments { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ContosoUniversity.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "https://localhost:44352/", 7 | "sslPort": 44352 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "departments", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "ContosoUniversity.Api": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "launchUrl": "api/values", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | }, 26 | "applicationUrl": "http://localhost:6188" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/department/DepartmentList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import DepartmentListRow from './DepartmentListRow'; 4 | 5 | const DepartmentList =({departments}) => { 6 | return( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {departments.map((department) => )} 17 | 18 |
NameBudgetStart Date
19 | ); 20 | } 21 | 22 | DepartmentList.propTypes = { 23 | departments: PropTypes.array.isRequired 24 | } 25 | 26 | export default DepartmentList; -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build-web-app", 8 | "command": "dotnet build ContosoUniversity.Web", 9 | "type": "shell", 10 | "group": "build", 11 | "presentation": { 12 | "reveal": "silent" 13 | }, 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "build-api", 18 | "command": "dotnet build ContosoUniversity.Api", 19 | "type": "shell", 20 | "group": "build", 21 | "presentation": { 22 | "reveal": "silent" 23 | }, 24 | "problemMatcher": "$msCompile" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/ResetPasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class ResetPasswordViewModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | 11 | [Required] 12 | [StringLength(100, ErrorMessage ="The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 13 | [DataType(DataType.Password)] 14 | public string Password { get; set; } 15 | 16 | [DataType(DataType.Password)] 17 | [Display(Name = "Confirm Password")] 18 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 19 | public string ConfirmPassword { get; set; } 20 | 21 | public string Code { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ContosoUniversity.Api.Tests/ApiIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Tests; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using Xunit; 5 | 6 | namespace ContosoUniversity.Api.Tests 7 | { 8 | public class ApiIntegrationTests : BaseIntegrationTest 9 | { 10 | [Fact(Skip = "Move to separate project")] 11 | public async Task DepartmentApi_ReturnsArrayOfDepartmentObjects() 12 | { 13 | var url = "/Departments"; 14 | using (var th = InitTestServer()) 15 | { 16 | var client = th.CreateClient(); 17 | var response = await client.GetAsync(url); 18 | 19 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 20 | 21 | var content = await response.Content.ReadAsStringAsync(); 22 | Assert.True(content.Contains("English")); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/Person.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace ContosoUniversity.Data.Entities 6 | { 7 | public class Person : BaseEntity 8 | { 9 | [Required] 10 | [StringLength(50)] 11 | [Display(Name = "Last Name")] 12 | public string LastName { get; set; } 13 | 14 | [Required] 15 | [StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")] 16 | [Column("FirstName")] 17 | [Display(Name = "First Name")] 18 | public string FirstMidName { get; set; } 19 | 20 | [Display(Name = "Full Name")] 21 | public string FullName 22 | { 23 | get 24 | { 25 | return LastName + ", " + FirstMidName; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iis": { 6 | "applicationUrl": "http://localhost/ContosoUniversity.Web", 7 | "sslPort": 0 8 | }, 9 | "iisExpress": { 10 | "applicationUrl": "https://localhost:44368/", 11 | "sslPort": 44368 12 | } 13 | }, 14 | "profiles": { 15 | "IIS Express": { 16 | "commandName": "IISExpress", 17 | "launchBrowser": true, 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "ContosoUniversity": { 23 | "commandName": "Project", 24 | "launchBrowser": true, 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | }, 28 | "applicationUrl": "http://localhost:20645;https://localhost:20650;" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/ViewModels/ChangePasswordViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ContosoUniversity.Web.ViewModels 4 | { 5 | public class ChangePasswordViewModel 6 | { 7 | [Required] 8 | [DataType(DataType.Password)] 9 | [Display(Name = "Current Password")] 10 | public string OldPassword { get; set; } 11 | 12 | [Required] 13 | [StringLength(100, ErrorMessage ="The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] 14 | [DataType(DataType.Password)] 15 | [Display(Name = "New Password")] 16 | public string NewPassword { get; set; } 17 | 18 | [DataType(DataType.Password)] 19 | [Display(Name = "Confirm new password")] 20 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match")] 21 | public string ConfirmPassword { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/department/DepartmentsPage.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import DepartmentList from './DepartmentList'; 3 | 4 | class DepartmentsPage extends Component { 5 | constructor(props){ 6 | super(props); 7 | this.state = {departments: [], loading: true}; 8 | fetch('api/department/details') 9 | .then(response => response.json()) 10 | .then(data => { 11 | this.setState({departments: data, loading: false}); 12 | }); 13 | } 14 | 15 | render() { 16 | let contents = this.state.loading 17 | ?

Loading...

18 | : ; 19 | return ( 20 |
21 |

Departments

22 | {contents} 23 |
24 | ); 25 | } 26 | } 27 | 28 | export default DepartmentsPage; -------------------------------------------------------------------------------- /ContosoUniversity.Data/Entities/Department.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace ContosoUniversity.Data.Entities 7 | { 8 | public class Department : BaseEntity 9 | { 10 | [StringLength(50, MinimumLength = 3)] 11 | public string Name { get; set; } 12 | 13 | [DataType(DataType.Currency)] 14 | [Column(TypeName = "money")] 15 | public decimal Budget { get; set; } 16 | 17 | [DataType(DataType.Date)] 18 | [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] 19 | [Display(Name = "Start Date")] 20 | public DateTime StartDate { get; set; } 21 | 22 | public int? InstructorID { get; set; } 23 | 24 | public Instructor Administrator { get; set; } 25 | public ICollection Courses { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /ContosoUniversity.Common/Repositories/PersonRepository.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Entities; 2 | using ContosoUniversity.Common.Interfaces; 3 | using System; 4 | using System.Linq; 5 | using ContosoUniversity.Data; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | namespace ContosoUniversity.Common.Repositories 9 | { 10 | public class PersonRepository : Repository, IPersonRepository where T : Person where TContext : DbContext 11 | { 12 | public PersonRepository(TContext context) : base(context) 13 | { 14 | } 15 | 16 | public IQueryable GetByLastName(string lastName) 17 | { 18 | return base.GetAll().Where(p => p.LastName == lastName); 19 | } 20 | 21 | private void IsEntityNull(T entity) 22 | { 23 | if (entity == null) 24 | { 25 | throw new ArgumentNullException("entity"); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DbContexts/SecureApplicationContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore; 4 | using ContosoUniversity.Data.Entities; 5 | 6 | namespace ContosoUniversity.Data.DbContexts 7 | { 8 | public class SecureApplicationContext : IdentityDbContext 9 | { 10 | public SecureApplicationContext(DbContextOptions options) : base(options) 11 | { 12 | } 13 | 14 | protected override void OnModelCreating(ModelBuilder builder) 15 | { 16 | base.OnModelCreating(builder); 17 | string dbSchema = "Contoso"; 18 | if (OperatingSystem.IsMacOs()) 19 | { 20 | dbSchema = null; 21 | } 22 | 23 | var config = new DbContextConfig(); 24 | config.SecureApplicationContextConfig(builder, dbSchema); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Instructors/Details.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Instructor 2 | 3 | @{ 4 | ViewData["Title"] = "Details"; 5 | } 6 | 7 |

Details

8 | 9 |
10 |

Instructor

11 |
12 |
13 |
14 | @Html.DisplayNameFor(model => model.LastName) 15 |
16 |
17 | @Html.DisplayFor(model => model.LastName) 18 |
19 |
20 | @Html.DisplayNameFor(model => model.FirstMidName) 21 |
22 |
23 | @Html.DisplayFor(model => model.FirstMidName) 24 |
25 |
26 | @Html.DisplayNameFor(model => model.HireDate) 27 |
28 |
29 | @Html.DisplayFor(model => model.HireDate) 30 |
31 |
32 |
33 |
34 | Edit | 35 | Back to List 36 |
37 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Identity 2 | @using ContosoUniversity.Data.Entities 3 | 4 | @inject SignInManager SignInManager 5 | @inject UserManager UserManager 6 | 7 | @if (SignInManager.IsSignedIn(User)) 8 | { 9 | 19 | } 20 | else 21 | { 22 | 26 | } 27 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/DefaultModelBindingHelperAdaptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Linq.Expressions; 5 | 6 | namespace ContosoUniversity.Web 7 | { 8 | // issue with unit testing controllers that use TryUpdateModelAsync 9 | // solution @ https://stackoverflow.com/questions/39259025/asp-net-core-mvc-controller-unit-testing-when-using-tryupdatemodel 10 | public class DefaultModelBindingHelaperAdaptor : IModelBindingHelperAdaptor 11 | { 12 | public Task TryUpdateModelAsync(ControllerBase controller, TModel model, string prefix, params Expression>[] includeExpressions) where TModel : class 13 | { 14 | return controller.TryUpdateModelAsync(model, prefix, includeExpressions); 15 | } 16 | 17 | public Task TryUpdateModelAsync(ControllerBase controller, TModel model) where TModel : class 18 | { 19 | return controller.TryUpdateModelAsync(model); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/ForgotPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.ForgotPasswordViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Forgot Password"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |
9 |

Enter your email

10 |
11 |
12 |
13 | 14 |
15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 | 26 | @section Scripts{ 27 | @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } 28 | } 29 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Manage/AddPhoneNumber.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.AddPhoneNumberViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Add Phone Number"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | 9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 | 26 | @section Scripts{ 27 | @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");} 28 | } 29 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/Controllers/CoursesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using ContosoUniversity.Common.Interfaces; 6 | using ContosoUniversity.Data.Entities; 7 | using System.Linq; 8 | using ContosoUniversity.Common; 9 | using ContosoUniversity.Data.DbContexts; 10 | using ContosoUniversity.Common.DTO; 11 | using AutoMapper; 12 | 13 | namespace ContosoUniversity_Spa_React.Controllers 14 | { 15 | [Route("api/[controller]")] 16 | public class CoursesController : Controller 17 | { 18 | private readonly IRepository _coursesRepo; 19 | private readonly IMapper _mapper; 20 | 21 | public CoursesController(UnitOfWork unitOfWork, IMapper mapper) 22 | { 23 | _coursesRepo = unitOfWork.CourseRepository; 24 | _mapper = mapper; 25 | } 26 | 27 | public IEnumerable Get() 28 | { 29 | return _coursesRepo.GetAll().ToArray(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web.IntegrationTests/ContosoUniversity.Web.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/WebProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using ContosoUniversity.Data.Entities; 3 | using ContosoUniversity.ViewModels; 4 | using System.Text; 5 | 6 | namespace ContosoUniversity.Web 7 | { 8 | public class WebProfile : Profile 9 | { 10 | public WebProfile() 11 | { 12 | CreateMap() 13 | .ForMember(dest => dest.Administrator, opts => opts.MapFrom(src => src.Administrator.FullName)); 14 | CreateMap() 15 | .ForMember(dest => dest.Administrator, opts => opts.Ignore()); 16 | CreateMap() 17 | .ForMember(dest => dest.Administrator, opts => opts.MapFrom(src => src.Administrator.FullName)); 18 | CreateMap() 19 | .ForMember(dest => dest.RowVersion, opts => opts.MapFrom(src => Encoding.ASCII.GetBytes(src.RowVersion))) 20 | .ForMember(dest => dest.Administrator, opts => opts.Ignore()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contoso-university-spa-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "bootstrap": "^3.4.1", 7 | "moment": "^2.24.0", 8 | "numeral": "^2.0.6", 9 | "react": "^16.12.0", 10 | "react-bootstrap": "^0.31.5", 11 | "react-dom": "^16.12.0", 12 | "react-redux": "^5.1.2", 13 | "react-router-bootstrap": "^0.24.4", 14 | "react-router-dom": "^4.3.1", 15 | "react-scripts": "^3.3.0", 16 | "redux": "^3.7.2", 17 | "redux-thunk": "^2.3.0", 18 | "rimraf": "^2.7.1", 19 | "toastr": "2.1.4" 20 | }, 21 | "scripts": { 22 | "start": "rimraf ./build && react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test --env=jsdom", 25 | "eject": "react-scripts eject" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/index.js: -------------------------------------------------------------------------------- 1 | import 'bootstrap/dist/css/bootstrap.css'; 2 | import 'bootstrap/dist/css/bootstrap-theme.css'; 3 | import './index.css'; 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | import { BrowserRouter } from 'react-router-dom'; 7 | import App from './App'; 8 | import registerServiceWorker from './registerServiceWorker'; 9 | import {Provider} from 'react-redux'; 10 | import configureStore from './store/configureStore'; 11 | import { loadCourses } from './actions/courseActions'; 12 | import { loadDepartments } from './actions/departmentActions'; 13 | import '../node_modules/toastr/build/toastr.min.css'; 14 | 15 | const store = configureStore(); 16 | store.dispatch(loadCourses()); 17 | store.dispatch(loadDepartments()); 18 | 19 | const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href'); 20 | const rootElement = document.getElementById('root'); 21 | 22 | ReactDOM.render( 23 | 24 | 25 | 26 | 27 | , 28 | rootElement); 29 | 30 | registerServiceWorker(); 31 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/LoadingDots.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class LoadingDots extends React.Component { 5 | constructor(props){ 6 | super(props); 7 | this.state = { frame: 1}; 8 | } 9 | 10 | componentDidMount() { 11 | this.interval = setInterval(() => { 12 | this.setState({ 13 | frame: this.state.frame + 1 14 | }); 15 | }, this.props.interval); 16 | } 17 | 18 | componentWillUnmount() { 19 | clearInterval(this.interval); 20 | } 21 | 22 | render() { 23 | let dots = this.state.frame % (this.props.dots + 1); 24 | let text = ''; 25 | while(dots > 0){ 26 | text += '.'; 27 | dots--; 28 | } 29 | return {text} ; 30 | }; 31 | } 32 | 33 | LoadingDots.defaultTypes = { 34 | interval: 300, 35 | dots: 3 36 | }; 37 | 38 | LoadingDots.propTypes = { 39 | interval: PropTypes.number, 40 | dots: PropTypes.number 41 | }; 42 | 43 | export default LoadingDots; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Adrian Limon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/ContosoUniversity.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Manage/VerifyPhoneNumber.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.VerifyPhoneNumberViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Verify Phone Number"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | 9 |
10 | 11 |
@ViewData["Status"]
12 |
13 |
14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 | @section Scripts{ 29 | @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");} 30 | } 31 | -------------------------------------------------------------------------------- /ContosoUniversity.Web.Tests/Views/ChromeViews.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Tests; 2 | using OpenQA.Selenium; 3 | using OpenQA.Selenium.Chrome; 4 | using System; 5 | using System.IO; 6 | using System.Reflection; 7 | using Xunit; 8 | 9 | namespace ContosoUniversity.Web.Tests.Views 10 | { 11 | public class ChromeViews : BaseIntegrationTest 12 | { 13 | private readonly Uri _baseUrl; 14 | 15 | public ChromeViews() 16 | { 17 | _baseUrl = new Uri("https://localhost:44368/"); 18 | } 19 | 20 | [Fact(Skip = "Chrome UI")] 21 | public void HomeIndex_ShouldHaveAJumbotron_WithH1() 22 | { 23 | var home = _baseUrl; 24 | using (var driver = new ChromeDriver 25 | (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))) 26 | { 27 | driver.Navigate().GoToUrl(home); 28 | var jumbotron = driver.FindElementByClassName("jumbotron"); 29 | var h1 = jumbotron.FindElement(By.TagName("h1")); 30 | Assert.Equal("Contoso University", h1.Text); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "SMSAccountPassword": "DO-NOT-STORE-HERE", 11 | "SMSAccountIdentification": "DO-NOT-STORE-HERE", 12 | "SendGridUser": "DO-NOT-STORE-HERE", 13 | "SendGridKey": "DO-NOT-STORE-HERE", 14 | "SMSAccountFrom": "DO-NOT-STORE-HERE", 15 | "Administrator": { 16 | "UserName": "admin@example.com", 17 | "Password": "DO-NOT-STORE-Weak-P@ssw0rd!" 18 | }, 19 | "Authentication": { 20 | "Google": { 21 | "ClientId": "DO-NOT-STORE-HERE", 22 | "ClientSecret": "DO-NOT-STORE-HERE" 23 | }, 24 | "Facebook": { 25 | "AppId": "DO-NOT-STORE-HERE", 26 | "AppSecret": "DO-NOT-STORE-HERE" 27 | }, 28 | "Tokens": { 29 | "Issuer": "https://localhost:20650/", 30 | "Audience": "http://localhost:6188/", 31 | "Audiences": [ 32 | "http://localhost:20645/", 33 | "http://localhost:6188/" 34 | ], 35 | "Key": "DO-NOT-STORE-HERE-1absa-adljkzer-zldr" 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 3" 33 | }, 34 | "version": "3.3.7", 35 | "_release": "3.3.7", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.7", 39 | "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" 40 | }, 41 | "_source": "https://github.com/twbs/bootstrap.git", 42 | "_target": "v3.3.7", 43 | "_originalSource": "bootstrap", 44 | "_direct": true 45 | } -------------------------------------------------------------------------------- /ContosoUniversity.Api.Tests/ContosoUniversity.Api.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | 5 | 6 | 7 | PreserveNewest 8 | PreserveNewest 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /ContosoUniversity.Test/ContosoUniversity.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | PreserveNewest 10 | PreserveNewest 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Departments/Details.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.ViewModels.DepartmentDetailsViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Details"; 5 | } 6 | 7 |

Details

8 | 9 |
10 |

Department

11 |
12 |
13 |
14 | @Html.DisplayNameFor(model => model.Name) 15 |
16 |
17 | @Html.DisplayFor(model => model.Name) 18 |
19 |
20 | @Html.DisplayNameFor(model => model.Budget) 21 |
22 |
23 | @Html.DisplayFor(model => model.Budget) 24 |
25 |
26 | @Html.DisplayNameFor(model => model.StartDate) 27 |
28 |
29 | @Html.DisplayFor(model => model.StartDate) 30 |
31 |
32 | @Html.DisplayNameFor(model => model.Administrator) 33 |
34 |
35 | @Html.DisplayFor(model => model.Administrator) 36 |
37 |
38 |
39 |
40 | Edit | 41 | Back to List 42 |
43 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Courses/Details.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Course 2 | 3 | @{ 4 | ViewData["Title"] = "Details"; 5 | } 6 | 7 |

Details

8 | 9 |
10 |

Course

11 |
12 |
13 |
14 | @Html.DisplayNameFor(model => model.CourseNumber) 15 |
16 |
17 | @Html.DisplayFor(model => model.CourseNumber) 18 |
19 |
20 | @Html.DisplayNameFor(model => model.Title) 21 |
22 |
23 | @Html.DisplayFor(model => model.Title) 24 |
25 |
26 | @Html.DisplayNameFor(model => model.Credits) 27 |
28 |
29 | @Html.DisplayFor(model => model.Credits) 30 |
31 |
32 | @Html.DisplayNameFor(model => model.Department) 33 |
34 | @*
35 | @Html.DisplayFor(model => model.Department.DepartmentID) 36 |
*@ 37 |
38 |
39 |
40 | Edit | 41 | Back to List 42 |
43 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Set widths on the form inputs since otherwise they're 100% wide */ 14 | input, 15 | select, 16 | textarea { 17 | max-width: 280px; 18 | } 19 | 20 | /* Carousel */ 21 | .carousel-caption p { 22 | font-size: 20px; 23 | line-height: 1.4; 24 | } 25 | 26 | /* Make .svg files in the carousel display properly in older browsers */ 27 | .carousel-inner .item img[src$=".svg"] { 28 | width: 100%; 29 | } 30 | 31 | /* buttons and links extension to use brackets: [ click me ] */ 32 | .btn-bracketed::before { 33 | display: inline-block; 34 | content: "["; 35 | padding-right: 0.5em; 36 | } 37 | 38 | .btn-bracketed::after { 39 | display: inline-block; 40 | content: "]"; 41 | padding-left: 0.5em; 42 | } 43 | 44 | /* Hide/rearrange for smaller screens */ 45 | @media screen and (max-width: 767px) { 46 | /* Hide captions */ 47 | .carousel-caption { 48 | display: none; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Instructors/Delete.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Instructor 2 | 3 | @{ 4 | ViewData["Title"] = "Delete"; 5 | } 6 | 7 |

Delete

8 | 9 |

Are you sure you want to delete this?

10 |
11 |

Instructor

12 |
13 |
14 | 15 |
16 | @Html.DisplayNameFor(model => model.LastName) 17 |
18 |
19 | @Html.DisplayFor(model => model.LastName) 20 |
21 |
22 | @Html.DisplayNameFor(model => model.FirstMidName) 23 |
24 |
25 | @Html.DisplayFor(model => model.FirstMidName) 26 |
27 |
28 | @Html.DisplayNameFor(model => model.HireDate) 29 |
30 |
31 | @Html.DisplayFor(model => model.HireDate) 32 |
33 |
34 | 35 |
36 |
37 | | 38 | Back to List 39 |
40 |
41 |
42 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/TextInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const TextInput = ({name, label, placeholder, value, onChange, error}) => { 5 | let wrapperClass = 'form-group'; 6 | if(error && error.length > 0){ 7 | wrapperClass += " has-error"; 8 | } 9 | 10 | return ( 11 |
12 | 13 |
14 | 22 | {error &&
{error}
} 23 |
24 |
25 | ); 26 | } 27 | 28 | TextInput.propTypes = { 29 | name: PropTypes.string.isRequired, 30 | label: PropTypes.string.isRequired, 31 | placeholder: PropTypes.string, 32 | value: PropTypes.string, 33 | onChange: PropTypes.func.isRequired, 34 | error: PropTypes.string 35 | }; 36 | 37 | export default TextInput; -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/NumberInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const NumberInput = ({name, label, placeholder, value, onChange, error}) => { 5 | let wrapperClass = 'form-group'; 6 | if(error && error.length > 0){ 7 | wrapperClass += " has-error"; 8 | } 9 | 10 | return ( 11 |
12 | 13 |
14 | 22 | {error &&
{error}
} 23 |
24 |
25 | ); 26 | } 27 | 28 | NumberInput.propTypes = { 29 | name: PropTypes.string.isRequired, 30 | label: PropTypes.string.isRequired, 31 | placeholder: PropTypes.string, 32 | value: PropTypes.number, 33 | onChange: PropTypes.func.isRequired, 34 | error: PropTypes.string 35 | }; 36 | 37 | export default NumberInput; -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Students/Delete.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Student 2 | 3 | @{ 4 | ViewData["Title"] = "Delete"; 5 | } 6 | 7 |

Delete

8 |

@ViewData["ErrorMessage"]

9 |

Are you sure you want to delete this?

10 |
11 |

Student

12 |
13 |
14 | 15 |
16 | @Html.DisplayNameFor(model => model.LastName) 17 |
18 |
19 | @Html.DisplayFor(model => model.LastName) 20 |
21 |
22 | @Html.DisplayNameFor(model => model.FirstMidName) 23 |
24 |
25 | @Html.DisplayFor(model => model.FirstMidName) 26 |
27 |
28 | @Html.DisplayNameFor(model => model.EnrollmentDate) 29 |
30 |
31 | @Html.DisplayFor(model => model.EnrollmentDate) 32 |
33 |
34 | 35 |
36 |
37 | | 38 | Back to List 39 |
40 |
41 |
42 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DbContexts/ApplicationContext.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace ContosoUniversity.Data.DbContexts 5 | { 6 | public class ApplicationContext : DbContext 7 | { 8 | public ApplicationContext(DbContextOptions options) : base(options) 9 | { 10 | } 11 | 12 | public DbSet Courses { get; set; } 13 | public DbSet Enrollments { get; set; } 14 | public DbSet Students { get; set; } 15 | public DbSet Departments { get; set; } 16 | public DbSet Instructors { get; set; } 17 | public DbSet OfficeAssignments { get; set; } 18 | public DbSet CourseAssignments { get; set; } 19 | public DbSet People { get; set; } 20 | 21 | protected override void OnModelCreating(ModelBuilder modelBuilder) 22 | { 23 | string schema = "Contoso"; 24 | if (OperatingSystem.IsMacOs()) 25 | { 26 | schema = null; 27 | } 28 | 29 | var config = new DbContextConfig(); 30 | config.ApplicationContextConfig(modelBuilder, schema); 31 | 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/ExternalLoginConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.ExternalLoginConfirmationViewModel 2 | @{ 3 | ViewData["Title"] = "Register"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |

Associate your @ViewData["LoginProvider"] account.

8 |
9 |

Association Form

10 |
11 |
12 |

13 | You've successfully authenticated with @ViewData["LoginProvider"]. 14 | Please enter an email for this site below and click the Register button to finish loggin in. 15 |

16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 | 30 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/Controllers/DepartmentController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using ContosoUniversity.Common.Interfaces; 6 | using ContosoUniversity.Data.Entities; 7 | using System.Linq; 8 | using ContosoUniversity.Common; 9 | using ContosoUniversity.Data.DbContexts; 10 | using ContosoUniversity.Common.DTO; 11 | using AutoMapper; 12 | 13 | namespace ContosoUniversity_Spa_React.Controllers 14 | { 15 | [Route("api/[controller]")] 16 | public class DepartmentController : Controller 17 | { 18 | private readonly IRepository _departmentRepo; 19 | private readonly IMapper _mapper; 20 | 21 | public DepartmentController(UnitOfWork unitOfWork, IMapper mapper) 22 | { 23 | _departmentRepo = unitOfWork.DepartmentRepository; 24 | _mapper = mapper; 25 | } 26 | 27 | public IEnumerable Get() 28 | { 29 | return _departmentRepo.GetAll().Select(d => d.Name).ToArray(); 30 | } 31 | 32 | [HttpGet("[action]")] 33 | public IEnumerable Details() 34 | { 35 | return _departmentRepo.GetAll().Select(d => _mapper.Map(d)).ToArray(); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/actions/courseActions.js: -------------------------------------------------------------------------------- 1 | import * as types from './actionTypes'; 2 | import courseApi from '../api/mockCourseApi'; 3 | import {beginAjaxCall, ajaxCallError} from './ajaxStatusActions'; 4 | 5 | export function loadCoursesSuccess(courses) { 6 | return {type: types.LOAD_COURSES_SUCCESS, courses}; 7 | } 8 | 9 | export function createCourseSuccess(course) { 10 | return {type: types.CREATE_COURSE_SUCCESS, course}; 11 | } 12 | 13 | export function updateCourseSuccess(course) { 14 | return {type: types.UPDATE_COURSE_SUCCESS, course}; 15 | } 16 | 17 | export function loadCourses(){ 18 | return function(dispatch){ 19 | dispatch(beginAjaxCall()); 20 | return courseApi.getAllCourses().then(courses => { 21 | dispatch(loadCoursesSuccess(courses)); 22 | }).catch(error => { 23 | throw(error); 24 | }); 25 | } 26 | } 27 | 28 | export function saveCourse(course){ 29 | return function(dispatch){ 30 | dispatch(beginAjaxCall()); 31 | return courseApi.saveCourse(course).then(savedCourse => { 32 | course.id ? dispatch(updateCourseSuccess(savedCourse)) : dispatch(createCourseSuccess(savedCourse)); 33 | }).catch(error => { 34 | dispatch(ajaxCallError(error)); 35 | throw(error); 36 | }); 37 | }; 38 | } -------------------------------------------------------------------------------- /ContosoUniversity.Data/DbContexts/SecureApplicatonContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using Microsoft.Extensions.Configuration; 4 | using System; 5 | using System.IO; 6 | 7 | namespace ContosoUniversity.Data.DbContexts 8 | { 9 | public class SecureApplicationContextFactory : IDesignTimeDbContextFactory 10 | { 11 | public SecureApplicationContext CreateDbContext(string[] args) 12 | { 13 | var config = new ConfigurationBuilder() 14 | .SetBasePath(Directory.GetCurrentDirectory()) 15 | .AddJsonFile("appsettings.json") 16 | .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("Hosting:Environment")}.json", true) 17 | .AddEnvironmentVariables() 18 | .Build(); 19 | 20 | var builder = new DbContextOptionsBuilder(); 21 | 22 | if (OperatingSystem.IsMacOs()) 23 | { 24 | builder.UseSqlite("Data Source=ContosoUniversity.sqlite"); 25 | } 26 | else 27 | { 28 | builder.UseSqlServer(config.GetConnectionString("DefaultConnection")); 29 | 30 | } 31 | return new SecureApplicationContext(builder.Options); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/api/mockDepartmentApi.js: -------------------------------------------------------------------------------- 1 | import delay from './delay'; 2 | 3 | // original source: https://raw.githubusercontent.com/coryhouse/pluralsight-redux-starter/master/src/api/mockCourseApi.js 4 | // This file mocks a web API by working with the hard-coded data below. 5 | // It uses setTimeout to simulate the delay of an AJAX call. 6 | // All calls return promises. 7 | const departments = [ 8 | { 9 | "id": 1, 10 | "instructorID": 1, 11 | "name": "English", 12 | "budget": 350000.0000, 13 | "startDate": "2007-09-01T00:00:00" 14 | }, 15 | { 16 | "id": 2, 17 | "instructorID": 2, 18 | "name": "Mathematics", 19 | "budget": 100000.0000, 20 | "startDate": "2007-09-01T00:00:00" 21 | }, 22 | { 23 | "id": 3, 24 | "instructorID": 3, 25 | "name": "Engineering", 26 | "budget": 350000.0000, 27 | "startDate": "2007-09-01T00:00:00" 28 | }, 29 | { 30 | "id": 4, 31 | "instructorID": 4, 32 | "name": "Economics", 33 | "budget": 100000.0000, 34 | "startDate": "2007-09-01T00:00:00" 35 | } 36 | ]; 37 | 38 | class departmentApi { 39 | static getAllDepartments() { 40 | return new Promise((resolve, reject) => { 41 | setTimeout(() => { 42 | resolve(Object.assign([], departments)); 43 | }, delay); 44 | }); 45 | } 46 | 47 | } 48 | 49 | export default departmentApi; -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Courses/Delete.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Course 2 | 3 | @{ 4 | ViewData["Title"] = "Delete"; 5 | } 6 | 7 |

Delete

8 | 9 |

Are you sure you want to delete this?

10 |
11 |

Course

12 |
13 |
14 |
15 | @Html.DisplayNameFor(model => model.CourseNumber) 16 |
17 |
18 | @Html.DisplayFor(model => model.CourseNumber) 19 |
20 |
21 | @Html.DisplayNameFor(model => model.Title) 22 |
23 |
24 | @Html.DisplayFor(model => model.Title) 25 |
26 |
27 | @Html.DisplayNameFor(model => model.Credits) 28 |
29 |
30 | @Html.DisplayFor(model => model.Credits) 31 |
32 |
33 | @Html.DisplayNameFor(model => model.Department) 34 |
35 |
36 | @Html.DisplayFor(model => model.Department.Name) 37 |
38 |
39 | 40 |
41 |
42 | | 43 | Back to List 44 |
45 |
46 |
47 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/VerifyCode.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.VerifyCodeViewModel 2 | @{ 3 | ViewData["Title"] = "Verify"; 4 | } 5 | 6 |

@ViewData["Title"]

7 | 8 |
13 | 14 | 15 |

@ViewData["Status"]

16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 |
24 |
25 | 26 | 27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 |
35 | 36 | @section Scripts{ 37 | @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");} 38 | } 39 | 40 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/SelectInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const SelectInput = ({label, name, value, onChange, defaultOption, options, error}) => { 5 | return ( 6 |
7 | 8 |
9 | 21 | {error &&
{error}
} 22 |
23 |
24 | ); 25 | } 26 | 27 | SelectInput.propTypes = { 28 | label: PropTypes.string.isRequired, 29 | name: PropTypes.string.isRequired, 30 | value: PropTypes.number, 31 | onChange: PropTypes.func.isRequired, 32 | defaultOption: PropTypes.string, 33 | options: PropTypes.arrayOf(PropTypes.object), 34 | error: PropTypes.string, 35 | } 36 | 37 | export default SelectInput; -------------------------------------------------------------------------------- /ContosoUniversity.Web/PaginatedList.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ContosoUniversity 8 | { 9 | public class PaginatedList : List 10 | { 11 | public int PageIndex { get; private set; } 12 | public int TotalPages { get; private set; } 13 | 14 | public PaginatedList(List items, int count, int pageIndex, int pageSize) 15 | { 16 | PageIndex = pageIndex; 17 | TotalPages = (int)Math.Ceiling(count / (double)pageSize); 18 | this.AddRange(items); 19 | } 20 | 21 | public bool HasPreviousPage 22 | { 23 | get 24 | { 25 | return (PageIndex > 1); 26 | } 27 | } 28 | 29 | public bool HasNextPage 30 | { 31 | get 32 | { 33 | return (PageIndex < TotalPages); 34 | } 35 | } 36 | 37 | public static async Task> CreateAsync(IQueryable source, int pageIndex, int pageSize) 38 | { 39 | var count = await source.CountAsync(); 40 | var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(); 41 | return new PaginatedList(items, count, pageIndex, pageSize); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/course/CoursesPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import {connect} from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import {bindActionCreators} from 'redux'; 6 | import * as courseActions from '../../actions/courseActions'; 7 | import CourseList from './CourseList'; 8 | import LoadingDots from '../common/LoadingDots'; 9 | 10 | class CoursesPage extends React.Component { 11 | render() { 12 | const isLoading = this.props.loading; 13 | return ( 14 |
15 |

Courses {isLoading && }

16 | Add Course 17 | 18 |
19 | ); 20 | } 21 | } 22 | 23 | CoursesPage.propTypes = { 24 | actions: PropTypes.object.isRequired, 25 | courses: PropTypes.array.isRequired, 26 | loading: PropTypes.bool.isRequired 27 | }; 28 | 29 | function mapStateToProps(state, ownProps){ 30 | return { 31 | courses: state.courses, 32 | loading: state.ajaxCallsInProgress > 0 33 | }; 34 | } 35 | 36 | function mapDispatchToProps(dispatch){ 37 | return { 38 | actions: bindActionCreators(courseActions,dispatch) 39 | }; 40 | } 41 | 42 | export default connect(mapStateToProps, mapDispatchToProps)(CoursesPage); -------------------------------------------------------------------------------- /ContosoUniversity.Data/DbContexts/WebContext.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.DbContexts; 2 | using ContosoUniversity.Data.Entities; 3 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | namespace ContosoUniversity.Data 7 | { 8 | public class WebContext : IdentityDbContext 9 | { 10 | public WebContext(DbContextOptions options) : base(options) 11 | { 12 | } 13 | 14 | public DbSet Courses { get; set; } 15 | public DbSet Enrollments { get; set; } 16 | public DbSet Students { get; set; } 17 | public DbSet Departments { get; set; } 18 | public DbSet Instructors { get; set; } 19 | public DbSet OfficeAssignments { get; set; } 20 | public DbSet CourseAssignments { get; set; } 21 | public DbSet People { get; set; } 22 | 23 | protected override void OnModelCreating(ModelBuilder modelBuilder) 24 | { 25 | base.OnModelCreating(modelBuilder); 26 | string schema = "Contoso"; 27 | if (OperatingSystem.IsMacOs()) 28 | { 29 | schema = null; 30 | } 31 | 32 | var config = new DbContextConfig(); 33 | config.SecureApplicationContextConfig(modelBuilder, schema); 34 | config.ApplicationContextConfig(modelBuilder, schema); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ContosoUniversity.Web.Tests/ContosoUniversity.Web.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/ContosoUniversity.Data.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Helpers/UrlHelperAdaptor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace ContosoUniversity.Web.Helpers 4 | { 5 | public class UrlHelperAdaptor : IUrlHelperAdaptor 6 | { 7 | // 8 | // Summary: 9 | // Generates a URL with an absolute path for an action method, which contains the 10 | // specified action name, controller name, route values, and protocol to use. 11 | // 12 | // Parameters: 13 | // helper: 14 | // The Microsoft.AspNetCore.Mvc.IUrlHelper. 15 | // 16 | // action: 17 | // The name of the action method. 18 | // 19 | // controller: 20 | // The name of the controller. 21 | // 22 | // values: 23 | // An object that contains route values. 24 | // 25 | // protocol: 26 | // The protocol for the URL, such as "http" or "https". 27 | // 28 | // Returns: 29 | // The generated URL. 30 | public string Action(IUrlHelper helper, string action, string controller, object values, string protocol) 31 | { 32 | var url = GenerateUrl(helper, action, controller, values, protocol); 33 | return url; 34 | } 35 | 36 | protected virtual string GenerateUrl(IUrlHelper helper, string action, string controller, object values, string protocol) 37 | { 38 | return helper.Action(action, controller, values, protocol); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Departments/Delete.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Department 2 | 3 | @{ 4 | ViewData["Title"] = "Delete"; 5 | } 6 | 7 |

Delete

8 |

@ViewData["ConcurrencyErrorMessage"]

9 |

Are you sure you want to delete this?

10 |
11 |

Department

12 |
13 |
14 |
15 | @Html.DisplayNameFor(model => model.Name) 16 |
17 |
18 | @Html.DisplayFor(model => model.Name) 19 |
20 |
21 | @Html.DisplayNameFor(model => model.Budget) 22 |
23 |
24 | @Html.DisplayFor(model => model.Budget) 25 |
26 |
27 | @Html.DisplayNameFor(model => model.StartDate) 28 |
29 |
30 | @Html.DisplayFor(model => model.StartDate) 31 |
32 |
33 | @Html.DisplayNameFor(model => model.Administrator) 34 |
35 |
36 | @Html.DisplayFor(model => model.Administrator.FullName) 37 |
38 |
39 | 40 |
41 | 42 | 43 |
44 | | 45 | Back to List 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DbContexts/ApplicationContextFactory.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.DbContexts; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Design; 4 | using Microsoft.Extensions.Configuration; 5 | using System; 6 | using System.IO; 7 | 8 | namespace ContosoUniversity.Data 9 | { 10 | // modeled from https://www.benday.com/2017/02/17/ef-core-migrations-without-hard-coding-a-connection-string-using-idbcontextfactory 11 | public class ApplicationContextFactory : IDesignTimeDbContextFactory 12 | { 13 | public ApplicationContext CreateDbContext(string[] args) 14 | { 15 | var config = new ConfigurationBuilder() 16 | .SetBasePath(Directory.GetCurrentDirectory()) 17 | .AddJsonFile("appsettings.json") 18 | .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("Hosting:Environment")}.json", true) 19 | .AddEnvironmentVariables() 20 | .Build(); 21 | 22 | var builder = new DbContextOptionsBuilder(); 23 | if (OperatingSystem.IsMacOs()) 24 | { 25 | builder.UseSqlite("Data Source=ContosoUniversity.sqlite"); 26 | } 27 | else 28 | { 29 | builder.UseSqlServer(config.GetConnectionString("DefaultConnection"), x => x.MigrationsHistoryTable("Migration", "Contoso")); 30 | } 31 | return new ApplicationContext(builder.Options); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ContosoUniversity.Test/HttpClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Net.Http; 4 | using System.Reflection; 5 | using System.Threading.Tasks; 6 | 7 | namespace ContosoUniversity.Tests 8 | { 9 | public static class HttpClientExtensions 10 | { 11 | /// 12 | /// This method provides a way to post form data to an MVC controller 13 | /// 14 | /// The view model type that will be posted. 15 | /// The HttpClient that will be posting the data. 16 | /// The url of the controller 17 | /// The view model data that will be passed in the request. 18 | /// 19 | /// 20 | public async static Task PostFormDataAsync(this HttpClient client, string url, T viewModel, KeyValuePair? antiForgeryToken = null) 21 | { 22 | var list = viewModel.GetType() 23 | .GetProperties() 24 | .Select(t => new KeyValuePair(t.Name, (t.GetValue(viewModel) ?? new object()).ToString())) 25 | .ToList(); 26 | if (antiForgeryToken.HasValue) 27 | list.Add(antiForgeryToken.Value); 28 | var formData = new FormUrlEncodedContent(list); 29 | return await client.PostAsync(url, formData); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Route } from 'react-router'; 3 | import PropTypes from 'prop-types'; 4 | import { Layout } from './components/common/Layout'; 5 | import DepartmentsPage from './components/department/DepartmentsPage'; 6 | import CoursesPage from './components/course/CoursesPage'; 7 | import ManageCoursePage from './components/course/ManageCoursePage'; 8 | import AboutPage from './components/about/AboutPage'; 9 | import ContactPage from './components/contact/ContactPage'; 10 | import RegisterPage from './components/register/RegisterPage'; 11 | import LoginPage from './components/login/LoginPage'; 12 | import HomePage from './components/home/HomePage.js'; 13 | 14 | class App extends Component { 15 | displayName = App.name 16 | 17 | render() { 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | App.propTypes = { 35 | router: PropTypes.object 36 | } 37 | 38 | export default App; 39 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/Data/ApiInitializer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using ContosoUniversity.Common.Interfaces; 3 | using Microsoft.Extensions.Options; 4 | using Microsoft.AspNetCore.Hosting; 5 | using ContosoUniversity.Data.DbContexts; 6 | 7 | namespace ContosoUniversity.Common.Data 8 | { 9 | public class ApiInitializer : IDbInitializer 10 | { 11 | private readonly ApiContext _context; 12 | private readonly ILogger _logger; 13 | private readonly SampleData _data; 14 | private readonly IHostingEnvironment _environment; 15 | 16 | public ApiInitializer(ApiContext context, 17 | ILoggerFactory loggerFactory, 18 | IOptions dataOptions, 19 | IHostingEnvironment env) 20 | { 21 | _context = context; 22 | _logger = loggerFactory.CreateLogger("ApiInitializer"); 23 | _data = dataOptions.Value; 24 | _environment = env; 25 | } 26 | public void Initialize() 27 | { 28 | InitializeContext(); 29 | } 30 | 31 | private void InitializeContext() 32 | { 33 | // create database schema if it does not exist 34 | if (_context.Database.EnsureCreated()) 35 | { 36 | _logger.LogInformation("Creating database schema..."); 37 | } 38 | var unitOfWork = new UnitOfWork(_context); 39 | var seedData = new SeedData(_logger, unitOfWork, _data); 40 | seedData.Initialize(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ContosoUniversity.Web.IntegrationTests/StaticPagesTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using System.Threading.Tasks; 3 | 4 | namespace ContosoUniversity.Web.IntegrationTests 5 | { 6 | public class StaticPagesTests : IClassFixture> 7 | { 8 | private readonly CustomWebApplicationFactory _factory; 9 | public StaticPagesTests(CustomWebApplicationFactory factory) => _factory = factory; 10 | 11 | #region Razor Pages 12 | [Theory] 13 | [InlineData("/")] 14 | [InlineData("/contact")] 15 | public async Task GetPages_ReturnSuccess(string url) 16 | { 17 | var client = _factory.CreateClient(); 18 | 19 | var response = await client.GetAsync(url); 20 | response.EnsureSuccessStatusCode(); 21 | } 22 | #endregion 23 | 24 | #region Mvc Views 25 | [Theory] 26 | [InlineData("/students")] 27 | [InlineData("/students/details/1")] 28 | [InlineData("/courses")] 29 | [InlineData("/courses/details/1")] 30 | [InlineData("/instructors")] 31 | [InlineData("/instructors/details/2")] 32 | [InlineData("/departments")] 33 | [InlineData("/departments/details/1")] 34 | public async Task GetControllersIndexAndDetails_ReturnSuccess(string url) 35 | { 36 | var client = _factory.CreateClient(); 37 | 38 | var response = await client.GetAsync(url); 39 | 40 | response.EnsureSuccessStatusCode(); 41 | } 42 | #endregion 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | ViewData["Title"] = "Home Page"; 5 | } 6 | 7 |
8 |

Contoso University

9 |
10 |
11 |
12 |

13 | Contoso University is a place for learning AspNetCore and related technologies. This demo application is an amalgamation of smaller demo applications found in tutorials at AspNetCore docs. The tutorials are great at demonstrating isolated concepts, but issues surfaces when applying these concepts/techniques in a larger context. The purpose of this demo application is to apply concepts/techniques learned from those tutorial into a single domain (i.e. university). 14 |

15 |
16 |
17 |

Build it from scratch

18 |

19 | You can build the initial application by following the steps in a series of tutorials. 20 |

21 |

22 | 23 | Seee the tutorial » 24 | 25 |

26 |
27 |
28 |

Download it

29 |

You can download the completed project from Github.

30 |

31 | 32 | See project source code » 33 | 34 |

35 |
36 |
-------------------------------------------------------------------------------- /ContosoUniversity.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore; 5 | using Microsoft.Extensions.Configuration; 6 | 7 | namespace ContosoUniversity.Api 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | var host = new WebHostBuilder() 14 | .UseKestrel() 15 | .UseContentRoot(Directory.GetCurrentDirectory()) 16 | .UseIISIntegration() 17 | .ConfigureAppConfiguration(ConfigConfiguration) 18 | .UseStartup() 19 | .Build(); 20 | 21 | host.Run(); 22 | } 23 | 24 | // use by EF Tooling 25 | public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) 26 | .UseStartup() 27 | .Build(); 28 | 29 | public static void ConfigConfiguration(WebHostBuilderContext context, IConfigurationBuilder config) 30 | { 31 | config.SetBasePath(Directory.GetCurrentDirectory()) 32 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 33 | .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true); 34 | 35 | if (context.HostingEnvironment.IsDevelopment()) 36 | { 37 | config.AddJsonFile($"sampleData.json", optional: false, reloadOnChange: false); 38 | config.AddUserSecrets(); 39 | } 40 | 41 | config.AddEnvironmentVariables(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Courses/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | 3 | @{ 4 | ViewData["Title"] = "Courses"; 5 | } 6 | 7 |

Courses

8 | 9 |

10 | Create New 11 |

12 | 13 | 14 | 15 | 18 | 21 | 24 | 27 | 28 | 29 | 30 | 31 | @foreach (var item in Model) 32 | { 33 | 34 | 37 | 40 | 43 | 46 | 51 | 52 | } 53 | 54 |
16 | @Html.DisplayNameFor(model => model.CourseNumber) 17 | 19 | @Html.DisplayNameFor(model => model.Title) 20 | 22 | @Html.DisplayNameFor(model => model.Credits) 23 | 25 | @Html.DisplayNameFor(model => model.Department) 26 |
35 | @Html.DisplayFor(modelItem => item.CourseNumber) 36 | 38 | @Html.DisplayFor(modelItem => item.Title) 39 | 41 | @Html.DisplayFor(modelItem => item.Credits) 42 | 44 | @Html.DisplayFor(modelItem => item.Department.Name) 45 | 47 | Edit | 48 | Details | 49 | Delete 50 |
55 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Departments/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | 3 | @{ 4 | ViewData["Title"] = "Departments"; 5 | } 6 | 7 |

Departments

8 |

9 | Create New 10 |

11 | 12 | 13 | 14 | 17 | 20 | 23 | 26 | 27 | 28 | 29 | 30 | @foreach (var item in Model) 31 | { 32 | 33 | 36 | 39 | 42 | 45 | 50 | 51 | } 52 | 53 |
15 | @Html.DisplayNameFor(model => model.Name) 16 | 18 | @Html.DisplayNameFor(model => model.Budget) 19 | 21 | @Html.DisplayNameFor(model => model.StartDate) 22 | 24 | @Html.DisplayNameFor(model => model.Administrator) 25 |
34 | @Html.DisplayFor(modelItem => item.Name) 35 | 37 | @Html.DisplayFor(modelItem => item.Budget) 38 | 40 | @Html.DisplayFor(modelItem => item.StartDate) 41 | 43 | @Html.DisplayFor(modelItem => item.Administrator) 44 | 46 | Edit | 47 | Details | 48 | Delete 49 |
54 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/Register.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.RegisterViewModel 2 | @{ 3 | ViewData["Title"] = "Register"; 4 | } 5 | 6 |

@ViewData["Title"]

7 |
8 |

Create a new account

9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 | 23 |
24 |
25 |
26 | 27 |
28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 |
38 | 39 | @section Scripts 40 | { 41 | @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Manage/ChangePassword.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.ChangePasswordViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Chanage Password"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | 9 |
10 |

Change Password Form

11 |
12 |
13 |
14 | 15 |
16 | 17 | 18 |
19 |
20 |
21 | 22 |
23 | 24 | 25 |
26 |
27 |
28 | 29 |
30 | 31 | 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 | 41 | @section Scripts{ 42 | @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");} 43 | } 44 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Account/ResetPassword.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.ResetPasswordViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Log in"; 5 | } 6 | 7 |

@ViewData["Title"]

8 | 9 |
10 |

Reset your password

11 |
12 |
13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 | 42 | @section Scripts 43 | { 44 | @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Students/Details.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Student 2 | 3 | @{ 4 | ViewData["Title"] = "Details"; 5 | } 6 | 7 |

Details

8 | 9 |
10 |

Student

11 |
12 |
13 |
14 | @Html.DisplayNameFor(model => model.LastName) 15 |
16 |
17 | @Html.DisplayFor(model => model.LastName) 18 |
19 |
20 | @Html.DisplayNameFor(model => model.FirstMidName) 21 |
22 |
23 | @Html.DisplayFor(model => model.FirstMidName) 24 |
25 |
26 | @Html.DisplayNameFor(model => model.EnrollmentDate) 27 |
28 |
29 | @Html.DisplayFor(model => model.EnrollmentDate) 30 |
31 |
32 | @Html.DisplayNameFor(model => model.Enrollments) 33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | @foreach (var item in Model.Enrollments) 41 | { 42 | 43 | 46 | 49 | 50 | } 51 |
Course TitleGrade
44 | @Html.DisplayFor(modelItem => item.Course.Title) 45 | 47 | @Html.DisplayFor(modelItem => item.Grade) 48 |
52 |
53 |
54 |
55 |
56 | Edit | 57 | Back to List 58 |
59 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.AspNetCore; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace ContosoUniversity 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | CreateWebHostBuilder(args).Build().Run(); 14 | } 15 | 16 | // netcoreapp2.1 code-based idiom to support integration test infrastructor 17 | // https://docs.microsoft.com/en-us/aspnet/core/migration/20_21?view=aspnetcore-3.1 18 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 19 | WebHost.CreateDefaultBuilder(args) 20 | .ConfigureAppConfiguration(ConfigConfiguration) 21 | .ConfigureLogging(ConfigureLogger) 22 | .UseStartup(); 23 | 24 | // refs: 25 | // https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0 26 | public static void ConfigConfiguration(WebHostBuilderContext context, IConfigurationBuilder config) 27 | { 28 | config.SetBasePath(Directory.GetCurrentDirectory()); 29 | 30 | if (context.HostingEnvironment.IsDevelopment()) 31 | { 32 | config.AddJsonFile($"sampleData.json", optional: true, reloadOnChange: false); 33 | config.AddUserSecrets(); 34 | } 35 | 36 | config.AddEnvironmentVariables(); 37 | } 38 | 39 | static void ConfigureLogger(WebHostBuilderContext ctx, ILoggingBuilder logging) 40 | { 41 | logging.AddConfiguration(ctx.Configuration.GetSection("Logging")); 42 | logging.AddConsole(); 43 | logging.AddDebug(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 23 | Contoso University - React 24 | 25 | 26 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 | 2 | ASP.NET MVC core dependencies have been added to the project. 3 | However you may still need to do make changes to your project. 4 | 5 | 1. Suggested changes to Startup class: 6 | 1.1 Add a constructor: 7 | public IConfigurationRoot Configuration { get; } 8 | 9 | public Startup(IHostingEnvironment env) 10 | { 11 | var builder = new ConfigurationBuilder() 12 | .SetBasePath(env.ContentRootPath) 13 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 14 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 15 | .AddEnvironmentVariables(); 16 | Configuration = builder.Build(); 17 | } 18 | 1.2 Add MVC services: 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | // Add framework services. 22 | services.AddMvc(); 23 | } 24 | 25 | 1.3 Configure web app to use use Configuration and use MVC routing: 26 | 27 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 28 | { 29 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 30 | loggerFactory.AddDebug(); 31 | 32 | if (env.IsDevelopment()) 33 | { 34 | app.UseDeveloperExceptionPage(); 35 | } 36 | else 37 | { 38 | app.UseExceptionHandler("/Home/Error"); 39 | } 40 | 41 | app.UseStaticFiles(); 42 | 43 | app.UseMvc(routes => 44 | { 45 | routes.MapRoute( 46 | name: "default", 47 | template: "{controller=Home}/{action=Index}/{id?}"); 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Students/Create.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Student 2 | 3 | @{ 4 | ViewData["Title"] = "Create"; 5 | } 6 | 7 |

Create

8 | 9 |
10 |
11 |

Student

12 |
13 |
14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 | 43 |
44 | Back to List 45 |
46 | 47 | @section Scripts { 48 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 49 | } 50 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/course/CourseForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import TextInput from '../common/TextInput'; 4 | import SelectInput from '../common/SelectInput'; 5 | import NumberInput from '../common/NumberInput'; 6 | import LoadingDots from '../common/LoadingDots'; 7 | 8 | const CourseForm = ({course, allDepartments, onChange, onSave, saving, errors}) => { 9 | return ( 10 |
11 |

Manage Course {saving && }

12 | 18 | 26 | 32 | 39 | 40 | ); 41 | }; 42 | 43 | CourseForm.propTypes = { 44 | course: PropTypes.object.isRequired, 45 | allDepartments: PropTypes.array, 46 | onChange: PropTypes.func.isRequired, 47 | onSave: PropTypes.func.isRequired, 48 | saving: PropTypes.bool, 49 | errors: PropTypes.object 50 | }; 51 | 52 | export default CourseForm; -------------------------------------------------------------------------------- /ContosoUniversity.Api/ContosoUniversity.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | Debug;Release;Release-Azure 5 | 6 | 7 | 494868df-30c3-4e9e-8fea-5db093fcb63f 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | custom.css 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ContosoUniversity.Data/DbContexts/DbContextConfig.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Entities; 2 | using Microsoft.AspNetCore.Identity; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace ContosoUniversity.Data.DbContexts 6 | { 7 | public class DbContextConfig 8 | { 9 | public void ApplicationContextConfig(ModelBuilder modelBuilder, string schema = "") 10 | { 11 | modelBuilder.Entity().ToTable("Course", schema); 12 | modelBuilder.Entity().ToTable("Enrollment", schema); 13 | modelBuilder.Entity().ToTable("Student", schema); 14 | modelBuilder.Entity().ToTable("Department", schema); 15 | modelBuilder.Entity().ToTable("Instructor", schema); 16 | modelBuilder.Entity().ToTable("OfficeAssignment", schema); 17 | modelBuilder.Entity().ToTable("CourseAssignment", schema); 18 | modelBuilder.Entity().ToTable("Person", schema); 19 | modelBuilder.Entity().HasKey(c => new { c.CourseID, c.InstructorID }); 20 | } 21 | 22 | public void SecureApplicationContextConfig(ModelBuilder modelBuilder, string schema = "") 23 | { 24 | modelBuilder.Entity().ToTable("Users", schema); 25 | modelBuilder.Entity().ToTable("Role", schema); 26 | modelBuilder.Entity>().ToTable("UserClaim", schema); 27 | modelBuilder.Entity>().ToTable("UserRole", schema); 28 | modelBuilder.Entity>().ToTable("UserLogin", schema); 29 | modelBuilder.Entity>().ToTable("RoleClaim", schema); 30 | modelBuilder.Entity>().ToTable("UserToken", schema); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Students/Edit.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Student 2 | 3 | @{ 4 | ViewData["Title"] = "Edit"; 5 | } 6 | 7 |

Edit

8 | 9 |
10 |
11 |

Student

12 |
13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 | 27 |
28 |
29 |
30 | 31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 |
45 | Back to List 46 |
47 | 48 | @section Scripts { 49 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 50 | } 51 | -------------------------------------------------------------------------------- /ContosoUniversity.Spa.React/ClientApp/src/components/common/NavMenu.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { Glyphicon, Nav, Navbar, NavItem, MenuItem, NavDropdown } from 'react-bootstrap'; 4 | import { LinkContainer } from 'react-router-bootstrap'; 5 | 6 | export class NavMenu extends Component { 7 | displayName = NavMenu.name 8 | 9 | render() { 10 | return ( 11 | 12 | 13 | 14 | Contoso University 15 | 16 | 17 | 18 | 19 | 42 | 50 | 51 | 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ContosoUniversity.Web.IntegrationTests/Helpers/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ContosoUniversity.Data; 3 | using ContosoUniversity.Data.DbContexts; 4 | using ContosoUniversity.Data.Entities; 5 | 6 | namespace ContosoUniversity.Web.IntegrationTests 7 | { 8 | public static class Utilities 9 | { 10 | public static void InitializeDbForTest(ApplicationContext db) 11 | { 12 | // id = 1 13 | var student = new Student { FirstMidName = "Anna", LastName = "Garland", EnrollmentDate = DateTime.Now }; 14 | db.Students.Add(student); 15 | db.SaveChanges(); 16 | 17 | // id = 1 18 | var office = new OfficeAssignment(); 19 | office.Location = "A"; 20 | db.OfficeAssignments.Add(office); 21 | db.SaveChanges(); 22 | 23 | // id = 2 24 | var instructor = new Instructor { }; 25 | instructor.LastName = "Smith"; 26 | instructor.FirstMidName = "John"; 27 | instructor.HireDate = DateTime.Now; 28 | instructor.OfficeAssignment = office; 29 | db.Instructors.Add(instructor); 30 | db.SaveChanges(); 31 | 32 | // id = 1 33 | var department = new Department(); 34 | department.Administrator = instructor; 35 | department.Name = "Engineering"; 36 | department.Budget = 100; 37 | db.Departments.Add(department); 38 | db.SaveChanges(); 39 | 40 | // id = 1 41 | var course = new Course(); 42 | course.Department = department; 43 | course.Title = "Intro to Engineering"; 44 | course.Credits = 3; 45 | db.Courses.Add(course); 46 | db.SaveChanges(); 47 | 48 | var courseAssignment = new CourseAssignment(); 49 | courseAssignment.CourseID = 1; 50 | courseAssignment.InstructorID = 2; 51 | db.CourseAssignments.Add(courseAssignment); 52 | db.SaveChanges(); 53 | 54 | } 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /ContosoUniversity.Common/Repositories/Repository.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Data.Entities; 2 | using ContosoUniversity.Common.Interfaces; 3 | using Microsoft.EntityFrameworkCore; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.Data.Common; 7 | using ContosoUniversity.Data.DbContexts; 8 | 9 | namespace ContosoUniversity.Data 10 | { 11 | public class Repository : IRepository where T : BaseEntity where TContext : DbContext 12 | { 13 | private readonly TContext context; 14 | private DbSet entities; 15 | string errorMessage = string.Empty; 16 | 17 | public Repository() 18 | { 19 | } 20 | 21 | public Repository(TContext context) 22 | { 23 | this.context = context; 24 | entities = context.Set(); 25 | } 26 | 27 | public IQueryable Get(int id) 28 | { 29 | return entities.Where(s => s.ID == id).AsQueryable(); 30 | } 31 | 32 | public IQueryable GetAll() 33 | { 34 | return entities.AsQueryable(); 35 | } 36 | 37 | public void Add(T entity) 38 | { 39 | context.Add(entity); 40 | } 41 | 42 | public async Task AddAsync(T entity) 43 | { 44 | await entities.AddAsync(entity); 45 | } 46 | 47 | public void Delete(T entity) 48 | { 49 | entities.Remove(entity); 50 | } 51 | 52 | public void Update(T entity, byte[] rowVersion) 53 | { 54 | context.Entry(entity).Property("RowVersion").OriginalValue = rowVersion; 55 | context.Update(entity); 56 | } 57 | 58 | public async Task SaveChangesAsync() 59 | { 60 | await context.SaveChangesAsync(); 61 | } 62 | 63 | public async Task ExecuteSqlCommandAsync(string queryString) 64 | { 65 | return await context.Database.ExecuteSqlCommandAsync(queryString); 66 | } 67 | 68 | public DbConnection GetDbConnection() 69 | { 70 | return context.Database.GetDbConnection(); 71 | } 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Contoso University [![Build Status](https://travis-ci.org/alimon808/contoso-university.svg?branch=master)](https://travis-ci.org/alimon808/contoso-university) 2 | Contoso University is a place for learning AspNetCore and related technologies. This demo application is an amalgamation of smaller demo applications found in tutorials at [AspNetCore docs](https://docs.microsoft.com/en-us/aspnet/core/). The tutorials are great at demonstrating isolated concepts, but issues surfaces when applying these concepts/techniques in a larger context. The purpose of this demo application is to apply concepts/techniques learned from those tutorial into a single domain (i.e. university). 3 | 4 | ### ContosoUniversity.Web 5 | - Traditional Web App using MVC + Razor Pages 6 | - [Demo](http://contoso-university-web.adrianlimon.com) 7 | ### ContosoUniversity.Api 8 | - Traditional Rest Api 9 | - [Demo](http://contoso-university-api.adrianlimon.com/) 10 | - Generate JWT Token at http://contoso-university-web.adrianlimon.com/api/token to access secure api content. Requires registering via Web App. 11 | ### Testing 12 | - Unit Testing using [Moq](https://github.com/Moq/moq4/wiki/Quickstart) and [xUnit](https://xunit.github.io/docs/getting-started-dotnet-core) 13 | - Integration Testing using TestHost and InMemoryDatabase 14 | - UI Testing using Selenium 15 | ### Security 16 | - using Identity 2.0 17 | - Confirm Email using [SendGrid](sendgrid.com) 18 | - Confirm Phone using [Twilio](https://www.twilio.com/sms/api) 19 | - Two-Factor Authentication - [see tutorial](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/2fa) 20 | - OAuth 2 - Enable Google & Facebook logins 21 | - JWT (Json Web Token) - use to access secure API 22 | ### Technologies 23 | - [ASP.NET Core 2.0](https://blogs.msdn.microsoft.com/webdev/2017/08/14/announcing-asp-net-core-2-0/) 24 | - Asp.Net Core Mvc 2.0 / Razor 2.0 25 | - Entity Framework Core 2.0 / Identity 2.0 26 | - Moq 27 | - xUnit 28 | - Twilio 29 | - SendGrid 30 | 31 | ### Design Patterns 32 | - [Repository](https://social.technet.microsoft.com/wiki/contents/articles/36287.repository-pattern-in-asp-net-core.aspx) 33 | - [Unit Of Work](https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/advanced) 34 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Manage/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Web.ViewModels.ManageIndexViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Manage Account Settings"; 5 | } 6 | 7 |

@ViewData["Title"]

8 |

@ViewData["StatusMessage"]

9 | 10 |
11 |
12 |
13 |
Password:
14 |
15 | @if (Model.HasPassword) 16 | { 17 | Change 18 | } 19 |
20 |
Phone Number:
21 |
22 | @(Model.PhoneNumber ?? "None") 23 | @if (Model.PhoneNumber != null) 24 | { 25 |
26 | Change 27 |
28 | 29 |
30 | } 31 | else 32 | { 33 | Add 34 | } 35 | 36 |
37 | @if (Model.PhoneNumber != null) 38 | { 39 |
Two-Factor Authentication
40 |
41 | @if (Model.TwoFactor) 42 | { 43 |
44 | Enabled 45 |
46 | } 47 | else 48 | { 49 |
50 | Disabled 51 |
52 | } 53 |
54 | } 55 |
56 |
57 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Departments/Create.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.ViewModels.DepartmentCreateViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Create"; 5 | } 6 | 7 |

Create

8 | 9 |
10 |
11 |

Department

12 |
13 |
14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |
35 |
36 | 37 |
38 | 41 |
42 |
43 |
44 |
45 | 46 |
47 |
48 |
49 |
50 | 51 |
52 | Back to List 53 |
54 | 55 | @section Scripts { 56 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 57 | } 58 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "web-app", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "preLaunchTask": "build-web-app", 9 | "program": "${workspaceRoot}/ContosoUniversity.Web/bin/Debug/netcoreapp2.1/ContosoUniversity.Web.dll", 10 | "args": [], 11 | "cwd": "${workspaceRoot}/ContosoUniversity.Web", 12 | "stopAtEntry": false, 13 | "launchBrowser": { 14 | "enabled": true, 15 | "args": "${auto-detect-url}", 16 | "windows": { 17 | "command": "cmd.exe", 18 | "args": "/C start ${auto-detect-url}" 19 | }, 20 | "osx": { 21 | "command": "open" 22 | }, 23 | "linux": { 24 | "command": "xdg-open" 25 | } 26 | }, 27 | "env": { 28 | "ASPNETCORE_ENVIRONMENT": "Development" 29 | }, 30 | "sourceFileMap": { 31 | "/Views": "${workspaceRoot}/Views" 32 | } 33 | }, 34 | { 35 | "name": "api", 36 | "type": "coreclr", 37 | "request": "launch", 38 | "preLaunchTask": "build-api", 39 | "program": "${workspaceRoot}/ContosoUniversity.Api/bin/Debug/netcoreapp2.1/ContosoUniversity.Api.dll", 40 | "args": [], 41 | "cwd": "${workspaceRoot}/ContosoUniversity.Api", 42 | "stopAtEntry": false, 43 | "launchBrowser": { 44 | "enabled": true, 45 | "args": "${auto-detect-url}", 46 | "windows": { 47 | "command": "cmd.exe", 48 | "args": "/C start ${auto-detect-url}" 49 | }, 50 | "osx": { 51 | "command": "open" 52 | }, 53 | "linux": { 54 | "command": "xdg-open" 55 | } 56 | }, 57 | "env": { 58 | "ASPNETCORE_ENVIRONMENT": "Development" 59 | }, 60 | "sourceFileMap": {} 61 | } 62 | ] 63 | } -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Courses/Edit.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Course 2 | 3 | @{ 4 | ViewData["Title"] = "Edit"; 5 | } 6 | 7 |

Edit

8 | 9 |
10 |
11 |

Course

12 |
13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 |
21 |
22 |
23 | 24 |
25 | @Html.DisplayFor(model => model.CourseNumber) 26 |
27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |
35 |
36 | 37 |
38 | 41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 | 52 |
53 | Back to List 54 |
55 | 56 | @section Scripts { 57 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 58 | } 59 | -------------------------------------------------------------------------------- /ContosoUniversity.Api/wwwroot/swagger/ui/oauth2-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 61 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/ContosoUniversity.Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp2.1 4 | 5 | 6 | 494868df-30c3-4e9e-8fea-5db093fcb63f 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Courses/Create.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.Data.Entities.Course 2 | 3 | @{ 4 | ViewData["Title"] = "Create"; 5 | } 6 | 7 |

Create

8 | 9 |
10 |
11 |

Course

12 |
13 |
14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |
35 |
36 | 37 |
38 | 41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 | 52 |
53 | Back to List 54 |
55 | 56 | @section Scripts { 57 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 58 | } 59 | -------------------------------------------------------------------------------- /ContosoUniversity.Common/MessageServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using SendGrid; 3 | using SendGrid.Helpers.Mail; 4 | using System.Threading.Tasks; 5 | using Twilio; 6 | using Twilio.Rest.Api.V2010.Account; 7 | 8 | namespace ContosoUniversity.Common 9 | { 10 | public class AuthMessageSender : IEmailSender, ISmsSender 11 | { 12 | public AuthMessageSender(IOptions optionsAccessor, IOptions smsOptionsAccessor) 13 | { 14 | Options = optionsAccessor.Value; 15 | SmsOptions = smsOptionsAccessor.Value; 16 | } 17 | 18 | public AuthMessageSenderOptions Options { get; } 19 | public SMSOptions SmsOptions { get; } 20 | 21 | public Task SendEmailAsync(string email, string subject, string message) 22 | { 23 | // plug in your email service here to send an email 24 | return Execute(Options.SendGridKey, subject, message, email); 25 | } 26 | 27 | public Task Execute(string apiKey, string subject, string message, string email) 28 | { 29 | var client = new SendGridClient(apiKey); 30 | var msg = new SendGridMessage() 31 | { 32 | From = new EmailAddress("no-reply@contoso.com", "Consoto University"), 33 | Subject = subject, 34 | PlainTextContent = message, 35 | HtmlContent = message 36 | }; 37 | 38 | msg.AddTo(new EmailAddress(email)); 39 | return client.SendEmailAsync(msg); 40 | } 41 | 42 | public Task SendSmsAsync(string number, string message) 43 | { 44 | // plug in your sms service here to send a text message 45 | // your account SID from twilio.com/console 46 | var accountSid = SmsOptions.SMSAccountIdentification; 47 | // your auth token from twilio.com/console 48 | var authToken = SmsOptions.SMSAccountPassword; 49 | 50 | TwilioClient.Init(accountSid, authToken); 51 | 52 | var msg = MessageResource.Create( 53 | to: new Twilio.Types.PhoneNumber(number), 54 | from: new Twilio.Types.PhoneNumber(SmsOptions.SMSAccountFrom), 55 | body: message); 56 | 57 | return Task.FromResult(0); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Departments/Edit.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.ViewModels.DepartmentEditViewModel 2 | 3 | @{ 4 | ViewData["Title"] = "Edit"; 5 | } 6 | 7 |

Edit

8 | 9 |
10 |
11 |

Department

12 |
13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 | 27 | 28 |
29 |
30 |
31 | 32 |
33 | 34 | 35 |
36 |
37 |
38 | 39 |
40 | 43 | 44 |
45 |
46 |
47 |
48 | 49 |
50 |
51 |
52 |
53 | 54 |
55 | Back to List 56 |
57 | 58 | @section Scripts { 59 | @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} 60 | } 61 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Pages/About.cshtml.cs: -------------------------------------------------------------------------------- 1 | using ContosoUniversity.Common; 2 | using ContosoUniversity.Common.Interfaces; 3 | using ContosoUniversity.Data.DbContexts; 4 | using ContosoUniversity.Data.Entities; 5 | using ContosoUniversity.Models.SchoolViewModels; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | using System.Collections.Generic; 8 | using System.Data.Common; 9 | using System.Threading.Tasks; 10 | 11 | namespace ContosoUniversity.Web.Pages 12 | { 13 | public class AboutModel : PageModel 14 | { 15 | private readonly IRepository _studentRepo; 16 | public List StudentEnrollments; 17 | 18 | public AboutModel(UnitOfWork unitOfWork) 19 | { 20 | _studentRepo = unitOfWork.StudentRepository; 21 | } 22 | 23 | public void OnGet() 24 | { 25 | StudentEnrollments = Stats().Result; 26 | } 27 | 28 | private async Task> Stats() 29 | { 30 | List groups = new List(); 31 | var conn = _studentRepo.GetDbConnection(); 32 | try 33 | { 34 | await conn.OpenAsync(); 35 | using (var command = conn.CreateCommand()) 36 | { 37 | // todo: read from configuration 38 | var dbSchema = "Contoso."; 39 | if (ContosoUniversity.Common.OperatingSystem.IsMacOs()) 40 | { 41 | dbSchema = string.Empty; 42 | } 43 | string query = $"SELECT EnrollmentDate, COUNT(*) AS StudentCount FROM {dbSchema}Person WHERE Discriminator = 'Student' GROUP BY EnrollmentDate"; 44 | command.CommandText = query; 45 | DbDataReader reader = await command.ExecuteReaderAsync(); 46 | if (reader.HasRows) 47 | { 48 | while (await reader.ReadAsync()) 49 | { 50 | var row = new EnrollmentDateGroup 51 | { 52 | EnrollmentDate = reader.GetDateTime(0), 53 | StudentCount = reader.GetInt32(1) 54 | }; 55 | groups.Add(row); 56 | } 57 | } 58 | reader.Dispose(); 59 | } 60 | } 61 | finally 62 | { 63 | conn.Close(); 64 | } 65 | 66 | return groups; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ContosoUniversity.Web/Views/Students/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model ContosoUniversity.PaginatedList 2 | 3 | @{ 4 | ViewData["Title"] = "Index"; 5 | } 6 | 7 |

Index

8 |

9 | Create New 10 |

11 |
12 |
13 |

14 | Find by name: 15 | | 16 | Back to Full List 17 | 18 |

19 |
20 |
21 | 22 | 23 | 24 | 27 | 30 | 33 | 34 | 35 | 36 | 37 | @foreach (var item in Model) 38 | { 39 | 40 | 43 | 46 | 49 | 54 | 55 | } 56 | 57 |
25 | Last Name 26 | 28 | First Mid 29 | 31 | Enrollment Date 32 |
41 | @Html.DisplayFor(modelItem => item.LastName) 42 | 44 | @Html.DisplayFor(modelItem => item.FirstMidName) 45 | 47 | @Html.DisplayFor(modelItem => item.EnrollmentDate) 48 | 50 | Edit | 51 | Details | 52 | Delete 53 |
58 | @{ 59 | var prevDisabled = !Model.HasPreviousPage ? "disabled" : ""; 60 | var nextDisabled = !Model.HasNextPage ? "disabled" : ""; 61 | } 62 | Previous 63 | Next --------------------------------------------------------------------------------