From 9a2652739b57b6e3c79bbd62ca07d60e69d4c49b Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 17 Nov 2015 22:22:59 +0100 Subject: [PATCH] Merge from booking branch --- .gitignore | 4 + ChangeLog | 30 +- Makefile | 7 +- NpgsqlBlogProvider/ChangeLog | 35 +- NpgsqlBlogProvider/NpgsqlBlogProvider.cs | 107 +- NpgsqlBlogProvider/NpgsqlBlogProvider.csproj | 2 - NpgsqlBlogProvider/NpgsqlTagInfo.cs | 104 -- NpgsqlContentProvider/ChangeLog | 20 +- .../NpgsqlContentProvider.csproj | 1 + NpgsqlMRPProviders/ChangeLog | 9 + NpgsqlMRPProviders/NpgsqlProfileProvider.cs | 5 +- README.md | 19 + TestAPI/ChangeLog | 4 + TestAPI/TestAPI.csproj | 13 +- WebControls/ChangeLog | 11 + WebControls/WebControls.csproj | 1 + Yavsc.sln | 1 - booking/Admin/DataManager.cs | 157 ++ booking/Admin/Export.cs | 24 + booking/ApiControllers/AccountController.cs | 116 ++ booking/ApiControllers/AuthorizationDenied.cs | 55 + booking/ApiControllers/BasketController.cs | 92 ++ booking/ApiControllers/BlogsController.cs | 256 ++++ booking/ApiControllers/CalendarController.cs | 204 +++ booking/ApiControllers/CircleController.cs | 138 ++ .../ApiControllers/FrontOfficeController.cs | 195 +++ booking/ApiControllers/GCMController.cs | 33 + booking/ApiControllers/PaypalController.cs | 77 + booking/ApiControllers/WorkFlowController.cs | 182 +++ booking/ApiControllers/YavscController.cs | 71 + booking/CatExts/WebCatalogExtensions.cs | 94 ++ booking/Catalog.xml | 86 ++ booking/ChangeLog | 4 + booking/Controllers/AccountController.cs | 419 ++++++ booking/Controllers/AdminController.cs | 324 +++++ booking/Controllers/BackOfficeController.cs | 25 + booking/Controllers/BlogsController.cs | 412 ++++++ booking/Controllers/FileSystemController.cs | 67 + booking/Controllers/FrontOfficeController.cs | 263 ++++ booking/Controllers/GoogleController.cs | 367 +++++ booking/Controllers/HomeController.cs | 138 ++ booking/Controllers/ModuleController.cs | 36 + booking/Formatters/ErrorHtmlFormatter.cs | 95 ++ .../Formatters/EstimToPdfFormatter.MSAN.cs | 138 ++ booking/Formatters/EstimToPdfFormatter.cs | 134 ++ booking/Formatters/FormatterException.cs | 57 + booking/Formatters/RssFeedsFormatter.cs | 100 ++ booking/Formatters/SimpleFormatter.cs | 84 ++ booking/Formatters/TexToPdfFormatter.cs | 137 ++ booking/Global.asax | 1 + booking/Global.asax.cs | 132 ++ booking/Helpers/Google/ApiClient.cs | 78 + booking/Helpers/Google/CalendarApi.cs | 138 ++ booking/Helpers/Google/Entity.cs | 56 + booking/Helpers/Google/EntityQuery.cs | 46 + booking/Helpers/Google/MapTracks.cs | 81 ++ booking/Helpers/Google/OAuth2.cs | 250 ++++ booking/Helpers/Google/PeopleApi.cs | 69 + booking/Helpers/SimpleJsonPostMethod.cs | 110 ++ booking/Helpers/T.cs | 43 + booking/Helpers/TemplateException.cs | 15 + booking/Helpers/ThanksHelper.cs | 63 + booking/Helpers/YavscAjaxHelper.cs | 64 + booking/Helpers/YavscHelpers.cs | 345 +++++ booking/Models/App.master | 92 ++ booking/Models/AppAdmin.master | 107 ++ booking/Models/NoLogin.master | 69 + booking/RegistrationMail.txt | 9 + .../Settings/ModuleConfigurationElement.cs | 62 + .../ModuleConfigurationElementCollection.cs | 60 + .../Settings/ModulesConfigurationSection.cs | 40 + .../Settings/ThanksConfigurationCollection.cs | 41 + .../Settings/ThanksConfigurationElement.cs | 61 + .../Settings/ThanksConfigurationSection.cs | 52 + booking/ValidateAjaxAttribute.cs | 72 + booking/Views/Account/ChangePassword.aspx | 22 + .../Views/Account/ChangePasswordSuccess.aspx | 6 + booking/Views/Account/Circles.aspx | 74 + booking/Views/Account/Index.aspx | 4 + booking/Views/Account/Login.aspx | 32 + booking/Views/Account/Profile.aspx | 140 ++ booking/Views/Account/Register.ascx | 73 + booking/Views/Account/Register.aspx | 39 + .../Views/Account/RegistrationPending.aspx | 13 + booking/Views/Account/ResetPassword.aspx | 23 + booking/Views/Account/Unregister.aspx | 12 + booking/Views/Account/Validate.aspx | 2 + booking/Views/Admin/AddRole.aspx | 15 + booking/Views/Admin/AddUserToRole.ascx | 14 + booking/Views/Admin/Admin.aspx | 31 + booking/Views/Admin/BackupCreated.aspx | 7 + booking/Views/Admin/Backups.aspx | 6 + booking/Views/Admin/CreateBackup.aspx | 23 + booking/Views/Admin/Created.aspx | 12 + booking/Views/Admin/Index.aspx | 4 + booking/Views/Admin/InitDb.aspx | 23 + booking/Views/Admin/RemoveRole.aspx | 17 + booking/Views/Admin/RemoveUser.aspx | 18 + booking/Views/Admin/Restore.aspx | 37 + booking/Views/Admin/Restored.aspx | 8 + booking/Views/Admin/RoleList.aspx | 20 + booking/Views/Admin/UserList.aspx | 16 + booking/Views/Admin/UsersInRole.aspx | 21 + booking/Views/BackOffice/Index.aspx | 9 + {web => booking}/Views/Blogs/ChooseMedia.aspx | 0 booking/Views/Blogs/Edit.aspx | 243 ++++ booking/Views/Blogs/Index.aspx | 18 + .../Views/Blogs/NotAuthorized.aspx | 0 booking/Views/Blogs/PostActions.ascx | 26 + booking/Views/Blogs/RemovePost.aspx | 20 + booking/Views/Blogs/RemoveTitle.aspx | 22 + booking/Views/Blogs/TagControl.ascx | 47 + booking/Views/Blogs/TagPanel.ascx | 12 + booking/Views/Blogs/Title.aspx | 34 + booking/Views/Blogs/TitleNotFound.aspx | 5 + booking/Views/Blogs/UserPost.aspx | 41 + booking/Views/Blogs/UserPosts.aspx | 48 + booking/Views/FileSystem/Create.aspx | 20 + booking/Views/FileSystem/Delete.aspx | 3 + booking/Views/FileSystem/Details.aspx | 11 + booking/Views/FileSystem/Edit.aspx | 5 + booking/Views/FileSystem/Index.aspx | 12 + booking/Views/FrontOffice/Basket.aspx | 38 + booking/Views/FrontOffice/Brand.aspx | 31 + booking/Views/FrontOffice/Catalog.aspx | 28 + booking/Views/FrontOffice/Command.aspx | 13 + booking/Views/FrontOffice/Estimate.aspx | 357 +++++ booking/Views/FrontOffice/Estimates.aspx | 24 + booking/Views/FrontOffice/EventPub.aspx | 20 + booking/Views/FrontOffice/Index.aspx | 11 + booking/Views/FrontOffice/Product.aspx | 33 + .../Views/FrontOffice/ProductCategory.aspx | 20 + .../Views/FrontOffice/ReferenceNotFound.aspx | 5 + booking/Views/FrontOffice/Service.aspx | 31 + booking/Views/FrontOffice/Writting.ascx | 25 + booking/Views/Google/Auth.aspx | 6 + booking/Views/Google/Book.aspx | 65 + booking/Views/Google/CalAuth.aspx | 5 + booking/Views/Google/ChooseADate.aspx | 13 + booking/Views/Google/ChooseCalendar.aspx | 17 + booking/Views/Google/Index.aspx | 4 + booking/Views/Google/OtherWebException.aspx | 9 + booking/Views/Home/AssemblyInfo.aspx | 19 + booking/Views/Home/Contact.aspx | 37 + booking/Views/Home/Credits.aspx | 5 + booking/Views/Home/Index.aspx | 16 + booking/Views/Home/TagPanel.ascx | 13 + booking/Views/Web.config | 41 + booking/Views/WorkFlow/Index.aspx | 9 + booking/Views/WorkFlow/NewProject.aspx | 20 + booking/Web.config | 269 ++++ booking/WebApiConfig.cs | 66 + booking/booking.csproj | 295 ++++ booking/packages.config | 17 + booking/robots.txt | 7 + booking/templates/Estim.cs | 1033 +++++++++++++ booking/templates/Estim.tt | 197 +++ booking/templates/TexEstimInit.cs | 36 + booking/web.config | 11 + totem-banner.xxs.xcf | Bin 48261 -> 0 bytes totem.xcf | Bin 220839 -> 0 bytes web/ApiControllers/AccountController.cs | 8 + web/ApiControllers/BlogsController.cs | 47 +- web/ApiControllers/FrontOfficeController.cs | 2 +- web/ApiControllers/GCMController.cs | 6 + web/ApiControllers/YavscController.cs | 19 +- web/App_Data/Sql/instdbws.sql | 3 +- .../images/ui-bg_glass_20_555555_1x400.png | Bin 260 -> 312 bytes .../images/ui-bg_glass_40_0078a3_1x400.png | Bin 342 -> 394 bytes .../images/ui-bg_glass_40_ffc73d_1x400.png | Bin 316 -> 368 bytes .../ui-bg_gloss-wave_25_333333_500x100.png | Bin 3816 -> 3891 bytes .../ui-bg_highlight-soft_80_eeeeee_1x100.png | Bin 276 -> 332 bytes .../ui-bg_inset-soft_25_000000_1x100.png | Bin 275 -> 327 bytes .../ui-bg_inset-soft_30_f58400_1x100.png | Bin 340 -> 392 bytes .../dark/images/ui-icons_222222_256x240.png | Bin 6922 -> 6837 bytes .../dark/images/ui-icons_4b8e0b_256x240.png | Bin 4549 -> 4601 bytes .../dark/images/ui-icons_a83300_256x240.png | Bin 4549 -> 4601 bytes .../dark/images/ui-icons_cccccc_256x240.png | Bin 6975 -> 7038 bytes .../dark/images/ui-icons_ffffff_256x240.png | Bin 6299 -> 6351 bytes .../images/star-939235_1280.xxs.jpg | Bin 4116 -> 6540 bytes .../images/ui-bg_glass_20_555555_1x400.png | Bin 260 -> 312 bytes .../images/ui-bg_glass_40_0078a3_1x400.png | Bin 342 -> 394 bytes .../images/ui-bg_glass_40_ffc73d_1x400.png | Bin 316 -> 368 bytes .../ui-bg_gloss-wave_25_333333_500x100.png | Bin 3816 -> 3891 bytes .../ui-bg_highlight-soft_80_eeeeee_1x100.png | Bin 276 -> 332 bytes .../ui-bg_inset-soft_25_000000_1x100.png | Bin 275 -> 327 bytes .../ui-bg_inset-soft_30_f58400_1x100.png | Bin 340 -> 392 bytes .../images/ui-icons_222222_256x240.png | Bin 6922 -> 6837 bytes .../images/ui-icons_4b8e0b_256x240.png | Bin 4549 -> 4601 bytes .../images/ui-icons_a83300_256x240.png | Bin 4549 -> 4601 bytes .../images/ui-icons_cccccc_256x240.png | Bin 6975 -> 7038 bytes .../images/ui-icons_ffffff_256x240.png | Bin 6299 -> 6351 bytes web/App_Themes/style.css | 242 +-- web/ChangeLog | 1294 +++++++---------- web/Controllers/AccountController.cs | 1 + web/Controllers/AdminController.cs | 46 +- web/Controllers/BlogsController.cs | 25 +- web/Controllers/FrontOfficeController.cs | 64 +- web/Controllers/GoogleController.cs | 88 +- web/Controllers/HomeController.cs | 32 +- web/Formatters/FormatterException.cs | 9 +- web/Helpers/Google/OAuth2.cs | 2 + web/Models/App.master | 14 +- web/Models/AppAdmin.master | 14 +- web/Models/NoLogin.master | 19 +- web/Scripts/yavsc.circles.js | 3 - web/Scripts/yavsc.js | 12 +- web/Scripts/yavsc.tags.js | 3 +- web/Views/Account/Circles.aspx | 3 +- web/Views/Account/Profile.aspx | 43 +- web/Views/Admin/AddMemberToRole.ascx | 11 - web/Views/Admin/AddRole.aspx | 1 - web/Views/Admin/Admin.aspx | 5 +- web/Views/Admin/RoleList.aspx | 2 + web/Views/Admin/UserList.aspx | 12 +- web/Views/Admin/UsersInRole.aspx | 30 +- web/Views/BackOffice/Index.aspx | 6 +- web/Views/Blogs/Edit.aspx | 12 +- web/Views/Blogs/Index.aspx | 3 + web/Views/Blogs/PostActions.ascx | 14 +- web/Views/Blogs/TagControl.ascx | 3 +- web/Views/Blogs/TagPanel.ascx | 18 +- web/Views/Blogs/Title.aspx | 18 +- web/Views/Blogs/UserPosts.aspx | 38 +- web/Views/FrontOffice/Basket.aspx | 2 - web/Views/FrontOffice/Command.aspx | 7 +- web/Views/FrontOffice/Estimate.aspx | 151 +- web/Views/FrontOffice/EventPub.aspx | 1 - web/Views/FrontOffice/Index.aspx | 4 +- web/Views/Home/Contact.aspx | 14 +- web/Views/Home/Index.aspx | 18 +- web/Views/Home/TagPanel.ascx | 17 +- web/Views/Web.config | 4 +- web/Web.config | 498 +++---- web/Web.csproj | 105 +- web/WebDeploy.targets | 4 +- web/packages.config | 41 +- yavscModel/Blogs/BasePost.cs | 22 +- yavscModel/Blogs/BasePostInfo.cs | 15 + yavscModel/Blogs/BlogEntry.cs | 18 +- yavscModel/Blogs/BlogEntryCollection.cs | 24 +- yavscModel/Blogs/BlogHelper.cs | 35 - yavscModel/Blogs/BlogManager.cs | 52 +- yavscModel/Blogs/BlogProvider.cs | 17 +- yavscModel/Blogs/TagInfo.cs | 5 +- yavscModel/Calendar/BookQuery.cs | 86 -- yavscModel/Calendar/ICalendarManager.cs | 16 +- yavscModel/Calendar/YaEvent.cs | 3 + yavscModel/ChangeLog | 272 ++-- yavscModel/Circles/CircleBase.cs | 6 + yavscModel/Google/CalendarEventList.cs | 15 +- yavscModel/Google/CalendarListEntry.cs | 4 +- yavscModel/Google/GDate.cs | 12 + yavscModel/LocalizedText.Designer.cs | 44 +- yavscModel/LocalizedText.fr.Designer.cs | 54 +- yavscModel/LocalizedText.fr.resx | 7 +- yavscModel/LocalizedText.resx | 9 +- yavscModel/RolesAndMembers/LoginModel.cs | 4 +- .../RolesAndMembers/LostPasswordModel.cs | 11 + yavscModel/RolesAndMembers/Profile.cs | 84 +- yavscModel/RolesAndMembers/ProfileEdition.cs | 10 + .../RolesAndMembers/RegisterClientModel.cs | 8 + yavscModel/RolesAndMembers/RegisterModel.cs | 10 +- yavscModel/WorkFlow/IDataProvider.cs | 11 + yavscModel/YavscModel.csproj | 21 +- yavscclient/ChangeLog | 14 +- 266 files changed, 12833 insertions(+), 2337 deletions(-) delete mode 100644 NpgsqlBlogProvider/NpgsqlTagInfo.cs create mode 100644 booking/Admin/DataManager.cs create mode 100644 booking/Admin/Export.cs create mode 100644 booking/ApiControllers/AccountController.cs create mode 100644 booking/ApiControllers/AuthorizationDenied.cs create mode 100644 booking/ApiControllers/BasketController.cs create mode 100644 booking/ApiControllers/BlogsController.cs create mode 100644 booking/ApiControllers/CalendarController.cs create mode 100644 booking/ApiControllers/CircleController.cs create mode 100644 booking/ApiControllers/FrontOfficeController.cs create mode 100644 booking/ApiControllers/GCMController.cs create mode 100644 booking/ApiControllers/PaypalController.cs create mode 100644 booking/ApiControllers/WorkFlowController.cs create mode 100644 booking/ApiControllers/YavscController.cs create mode 100644 booking/CatExts/WebCatalogExtensions.cs create mode 100644 booking/Catalog.xml create mode 100644 booking/ChangeLog create mode 100644 booking/Controllers/AccountController.cs create mode 100644 booking/Controllers/AdminController.cs create mode 100644 booking/Controllers/BackOfficeController.cs create mode 100644 booking/Controllers/BlogsController.cs create mode 100644 booking/Controllers/FileSystemController.cs create mode 100644 booking/Controllers/FrontOfficeController.cs create mode 100644 booking/Controllers/GoogleController.cs create mode 100644 booking/Controllers/HomeController.cs create mode 100644 booking/Controllers/ModuleController.cs create mode 100644 booking/Formatters/ErrorHtmlFormatter.cs create mode 100644 booking/Formatters/EstimToPdfFormatter.MSAN.cs create mode 100644 booking/Formatters/EstimToPdfFormatter.cs create mode 100644 booking/Formatters/FormatterException.cs create mode 100644 booking/Formatters/RssFeedsFormatter.cs create mode 100644 booking/Formatters/SimpleFormatter.cs create mode 100644 booking/Formatters/TexToPdfFormatter.cs create mode 100644 booking/Global.asax create mode 100644 booking/Global.asax.cs create mode 100644 booking/Helpers/Google/ApiClient.cs create mode 100644 booking/Helpers/Google/CalendarApi.cs create mode 100644 booking/Helpers/Google/Entity.cs create mode 100644 booking/Helpers/Google/EntityQuery.cs create mode 100644 booking/Helpers/Google/MapTracks.cs create mode 100644 booking/Helpers/Google/OAuth2.cs create mode 100644 booking/Helpers/Google/PeopleApi.cs create mode 100644 booking/Helpers/SimpleJsonPostMethod.cs create mode 100644 booking/Helpers/T.cs create mode 100644 booking/Helpers/TemplateException.cs create mode 100644 booking/Helpers/ThanksHelper.cs create mode 100644 booking/Helpers/YavscAjaxHelper.cs create mode 100644 booking/Helpers/YavscHelpers.cs create mode 100644 booking/Models/App.master create mode 100644 booking/Models/AppAdmin.master create mode 100644 booking/Models/NoLogin.master create mode 100644 booking/RegistrationMail.txt create mode 100644 booking/Settings/ModuleConfigurationElement.cs create mode 100644 booking/Settings/ModuleConfigurationElementCollection.cs create mode 100644 booking/Settings/ModulesConfigurationSection.cs create mode 100644 booking/Settings/ThanksConfigurationCollection.cs create mode 100644 booking/Settings/ThanksConfigurationElement.cs create mode 100644 booking/Settings/ThanksConfigurationSection.cs create mode 100644 booking/ValidateAjaxAttribute.cs create mode 100644 booking/Views/Account/ChangePassword.aspx create mode 100644 booking/Views/Account/ChangePasswordSuccess.aspx create mode 100644 booking/Views/Account/Circles.aspx create mode 100644 booking/Views/Account/Index.aspx create mode 100644 booking/Views/Account/Login.aspx create mode 100644 booking/Views/Account/Profile.aspx create mode 100644 booking/Views/Account/Register.ascx create mode 100644 booking/Views/Account/Register.aspx create mode 100644 booking/Views/Account/RegistrationPending.aspx create mode 100644 booking/Views/Account/ResetPassword.aspx create mode 100644 booking/Views/Account/Unregister.aspx create mode 100644 booking/Views/Account/Validate.aspx create mode 100644 booking/Views/Admin/AddRole.aspx create mode 100644 booking/Views/Admin/AddUserToRole.ascx create mode 100644 booking/Views/Admin/Admin.aspx create mode 100644 booking/Views/Admin/BackupCreated.aspx create mode 100644 booking/Views/Admin/Backups.aspx create mode 100644 booking/Views/Admin/CreateBackup.aspx create mode 100644 booking/Views/Admin/Created.aspx create mode 100644 booking/Views/Admin/Index.aspx create mode 100644 booking/Views/Admin/InitDb.aspx create mode 100644 booking/Views/Admin/RemoveRole.aspx create mode 100644 booking/Views/Admin/RemoveUser.aspx create mode 100644 booking/Views/Admin/Restore.aspx create mode 100644 booking/Views/Admin/Restored.aspx create mode 100644 booking/Views/Admin/RoleList.aspx create mode 100644 booking/Views/Admin/UserList.aspx create mode 100644 booking/Views/Admin/UsersInRole.aspx create mode 100644 booking/Views/BackOffice/Index.aspx rename {web => booking}/Views/Blogs/ChooseMedia.aspx (100%) create mode 100644 booking/Views/Blogs/Edit.aspx create mode 100644 booking/Views/Blogs/Index.aspx rename {web => booking}/Views/Blogs/NotAuthorized.aspx (100%) create mode 100644 booking/Views/Blogs/PostActions.ascx create mode 100644 booking/Views/Blogs/RemovePost.aspx create mode 100644 booking/Views/Blogs/RemoveTitle.aspx create mode 100644 booking/Views/Blogs/TagControl.ascx create mode 100644 booking/Views/Blogs/TagPanel.ascx create mode 100644 booking/Views/Blogs/Title.aspx create mode 100644 booking/Views/Blogs/TitleNotFound.aspx create mode 100644 booking/Views/Blogs/UserPost.aspx create mode 100644 booking/Views/Blogs/UserPosts.aspx create mode 100644 booking/Views/FileSystem/Create.aspx create mode 100644 booking/Views/FileSystem/Delete.aspx create mode 100644 booking/Views/FileSystem/Details.aspx create mode 100644 booking/Views/FileSystem/Edit.aspx create mode 100644 booking/Views/FileSystem/Index.aspx create mode 100644 booking/Views/FrontOffice/Basket.aspx create mode 100644 booking/Views/FrontOffice/Brand.aspx create mode 100644 booking/Views/FrontOffice/Catalog.aspx create mode 100644 booking/Views/FrontOffice/Command.aspx create mode 100644 booking/Views/FrontOffice/Estimate.aspx create mode 100644 booking/Views/FrontOffice/Estimates.aspx create mode 100644 booking/Views/FrontOffice/EventPub.aspx create mode 100644 booking/Views/FrontOffice/Index.aspx create mode 100644 booking/Views/FrontOffice/Product.aspx create mode 100644 booking/Views/FrontOffice/ProductCategory.aspx create mode 100644 booking/Views/FrontOffice/ReferenceNotFound.aspx create mode 100644 booking/Views/FrontOffice/Service.aspx create mode 100644 booking/Views/FrontOffice/Writting.ascx create mode 100644 booking/Views/Google/Auth.aspx create mode 100644 booking/Views/Google/Book.aspx create mode 100644 booking/Views/Google/CalAuth.aspx create mode 100644 booking/Views/Google/ChooseADate.aspx create mode 100644 booking/Views/Google/ChooseCalendar.aspx create mode 100644 booking/Views/Google/Index.aspx create mode 100644 booking/Views/Google/OtherWebException.aspx create mode 100644 booking/Views/Home/AssemblyInfo.aspx create mode 100644 booking/Views/Home/Contact.aspx create mode 100644 booking/Views/Home/Credits.aspx create mode 100644 booking/Views/Home/Index.aspx create mode 100644 booking/Views/Home/TagPanel.ascx create mode 100644 booking/Views/Web.config create mode 100644 booking/Views/WorkFlow/Index.aspx create mode 100644 booking/Views/WorkFlow/NewProject.aspx create mode 100644 booking/Web.config create mode 100644 booking/WebApiConfig.cs create mode 100644 booking/booking.csproj create mode 100644 booking/packages.config create mode 100644 booking/robots.txt create mode 100644 booking/templates/Estim.cs create mode 100644 booking/templates/Estim.tt create mode 100644 booking/templates/TexEstimInit.cs create mode 100644 booking/web.config delete mode 100644 totem-banner.xxs.xcf delete mode 100644 totem.xcf delete mode 100644 web/Views/Admin/AddMemberToRole.ascx delete mode 100644 yavscModel/Blogs/BlogHelper.cs delete mode 100644 yavscModel/Calendar/BookQuery.cs diff --git a/.gitignore b/.gitignore index 36d54d4d..94909cfd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,11 @@ dist bower_components node_modules svg +web/Web.config.prod +web/Web.config.dev + .nuget .gitignore web/bfiles +web/App_Themes/style.totem.css diff --git a/ChangeLog b/ChangeLog index 6a9ce1d2..161629d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,30 @@ -2015-10-24 Paul Schneider +2015-11-17 Paul Schneider - * Makefile: restores the server reload after rsync call + * Makefile: adds nuget targets -2015-10-09 Paul Schneider +2015-11-14 Paul Schneider - * Makefile: reloads config after each rsync call + * Makefile: Fixes the `clean` target + + * README.md: WIP Skills + +2015-11-11 Paul Schneider + + * README.md: \n @ EOF + + * Yavsc.sln: remove the `booking` project from the solution + + * Edit.aspx: cleaning + +2015-11-08 Paul Schneider + + * README.md: Adds a TODO list + + * Yavsc.sln: do not build the MVC 5.1 project, that does not + run under mono. + + * ChangeLog: this file should not be maintained as a git + respository legacy file + + * .gitignore: this file should not be maintained under git diff --git a/Makefile b/Makefile index 2bd59221..3b37e8c7 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,6 @@ deploy: ddir build xbuild /p:Configuration=$(CONFIG) /p:SkipCopyUnchangedFiles=$(COPYUNCHANGED) /p:DeployDir=../$(LDYDESTDIR) /t:Deploy web/Web.csproj mv $(LDYDESTDIR)/Web.config $(LDYDESTDIR)/Web.config.new - rsync_% : HOST = $(HOST_$@) rsync_% : DESTDIR = $(DESTDIR_$@) @@ -44,7 +43,7 @@ build: xbuild /p:Configuration=$(CONFIG) /t:Build Yavsc.sln clean: - xbuild /t:Clean + xbuild /p:Configuration=$(CONFIG) /t:Clean Yavsc.sln find -name "StyleCop.Cache" -exec rm {} \; distclean: clean @@ -73,4 +72,8 @@ rsync_pre: rsync_prod: +nuget_restore: + for prj in ITContentProvider NpgsqlBlogProvider NpgsqlContentProvider NpgsqlMRPProviders Presta SalesCatalog TestAPI web WebControls yavscclient yavscModel; do nuget restore "$${prj}/packages.config" -SolutionDirectory . ; done +nuget_update: + for prj in ITContentProvider NpgsqlBlogProvider NpgsqlContentProvider NpgsqlMRPProviders Presta SalesCatalog TestAPI web WebControls yavscclient yavscModel; do nuget update "$${prj}/packages.config" ; done diff --git a/NpgsqlBlogProvider/ChangeLog b/NpgsqlBlogProvider/ChangeLog index 3b47b23f..073ce25d 100644 --- a/NpgsqlBlogProvider/ChangeLog +++ b/NpgsqlBlogProvider/ChangeLog @@ -1,31 +1,20 @@ -2015-11-04 Paul Schneider +2015-11-14 Paul Schneider - * NpgsqlBlogProvider.cs: Implements the note + * NpgsqlBlogProvider.cs: Bill ranking, and delivering hidden + content to owner -2015-10-19 Paul Schneider +2015-11-11 Paul Schneider - * NpgsqlTagInfo.cs: Fixes the photo retreival + * NpgsqlBlogProvider.cs: refactoring -2015-10-17 Paul Schneider +2015-11-06 Paul Schneider - * NpgsqlTagInfo.cs: - * NpgsqlBlogProvider.cs: + * NpgsqlBlogProvider.cs: refactorisation, to render Tags and + Circles on each entry type. + Fixes the `Find` Method using the `MatchTag` search flag -2015-10-17 Paul Schneider + * NpgsqlBlogProvider.csproj: cleanning - * NpgsqlTagInfo.cs: - * NpgsqlBlogProvider.cs: - * NpgsqlBlogProvider.csproj: - -2015-10-17 Paul Schneider - - * NpgsqlBlogProvider.cs: - -2015-10-13 Paul Schneider - - * NpgsqlBlogProvider.cs: implements the tag methods on db - -2015-10-10 Paul Schneider - - * NpgsqlBlogProvider.cs: + * ChangeLog: + * NpgsqlTagInfo.cs: diff --git a/NpgsqlBlogProvider/NpgsqlBlogProvider.cs b/NpgsqlBlogProvider/NpgsqlBlogProvider.cs index fd3c179f..53f9fb78 100644 --- a/NpgsqlBlogProvider/NpgsqlBlogProvider.cs +++ b/NpgsqlBlogProvider/NpgsqlBlogProvider.cs @@ -5,7 +5,6 @@ using Npgsql; using System.Collections.Generic; using Yavsc.Model.Blogs; using Yavsc.Model.Circles; -using System.Web.Mvc; using NpgsqlTypes; using System.Linq; @@ -22,34 +21,29 @@ namespace Npgsql.Web.Blog #region implemented abstract members of BlogProvider + public override void UpdatePost (BlogEntry be) + { + // TODO Tags and rate should not be ignored + UpdatePost (be.Id, be.Title, be.Content, be.Visible, be.AllowedCircles); + } + /// /// Note the specified postid and note. /// /// Postid. - /// Note. - public override void Note (long postid, int note) + /// rate. + public override void Rate (long postid, int rate) { using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { - cmd.CommandText = "update blogs set note = :note where _id = :pid)"; - cmd.Parameters.AddWithValue ("note", note); + cmd.CommandText = "update blog set rate = :rate where _id = :pid"; + cmd.Parameters.AddWithValue ("rate", rate); cmd.Parameters.AddWithValue ("pid", postid); cnx.Open (); cmd.ExecuteNonQuery (); cnx.Close (); } } - - /// - /// Gets the tag info. - /// - /// The tag info. - /// Tagname. - public override TagInfo GetTagInfo (string tagname) - { - return new NpgsqlTagInfo (connectionString, tagname); - } - /// /// Tag the specified post by identifier /// using the given tag. @@ -153,11 +147,13 @@ namespace Npgsql.Web.Blog cmd.CommandText = "update blog set modified=:now," + " title = :title," + - " bcontent=:content, " + + " bcontent = :content, " + " visible = :visible " + "where _id = :id"; cmd.Parameters.AddWithValue ("now", now); cmd.Parameters.AddWithValue ("title", title); + if (content == null) + content = ""; cmd.Parameters.AddWithValue ("content", content); cmd.Parameters.AddWithValue ("visible", visible); cmd.Parameters.AddWithValue ("id", postid); @@ -295,7 +291,8 @@ namespace Npgsql.Web.Blog BlogEntry be = null; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { - cmd.CommandText = "select username, title, bcontent, modified, posted, visible, photo from blog " + + cmd.CommandText = "select username, title, bcontent, modified, " + + "posted, visible, photo, rate from blog " + "where applicationname = :appname and _id = :id"; cmd.Parameters.AddWithValue ("appname", applicationName); cmd.Parameters.AddWithValue ("id", postid); @@ -309,6 +306,7 @@ namespace Npgsql.Web.Blog be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Visible = rdr.GetBoolean (rdr.GetOrdinal ("visible")); + be.Rate = rdr.GetInt32 (rdr.GetOrdinal ("rate")); int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) be.Photo = rdr.GetString (oph); @@ -351,8 +349,8 @@ namespace Npgsql.Web.Blog UUTBlogEntryCollection bec = new UUTBlogEntryCollection (username, title); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { using (NpgsqlCommand cmd = cnx.CreateCommand ()) { - cmd.CommandText = "select _id,bcontent,modified,posted,visible,photo from blog " + - "where applicationname = :appname and username = :username and title = :title"; + cmd.CommandText = "select _id,bcontent,modified,posted,visible,photo,rate from blog " + + "where applicationname = :appname and username = :username and title = :title order by rate desc"; cmd.Parameters.AddWithValue ("appname", NpgsqlDbType.Varchar, applicationName); cmd.Parameters.AddWithValue ("username", NpgsqlDbType.Varchar, username); cmd.Parameters.AddWithValue ("title", NpgsqlDbType.Varchar, title); @@ -367,12 +365,14 @@ namespace Npgsql.Web.Blog be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Visible = rdr.GetBoolean (rdr.GetOrdinal ("visible")); + be.Rate = rdr.GetInt32 (rdr.GetOrdinal ("rate")); be.Id = rdr.GetInt64 (rdr.GetOrdinal ("_id")); { int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) be.Photo = rdr.GetString (oph); } + bec.Add (be); } rdr.Close (); @@ -380,30 +380,13 @@ namespace Npgsql.Web.Blog } if (bec.Count != 0) { - using (NpgsqlCommand cmdtags = cnx.CreateCommand ()) { - long pid = 0; - cmdtags.CommandText = "select tag.name from tag,tagged where tag._id = tagged.tagid and tagged.postid = :postid"; - cmdtags.Parameters.AddWithValue ("postid", NpgsqlTypes.NpgsqlDbType.Bigint, pid); - cmdtags.Prepare (); - foreach (BlogEntry be in bec) { - List tags = new List (); - cmdtags.Parameters ["postid"].Value = be.Id; - using (NpgsqlDataReader rdrt = cmdtags.ExecuteReader ()) { - while (rdrt.Read ()) { - tags.Add (rdrt.GetString (0)); - } - } - be.Tags = tags.ToArray (); - } - } - if (bec != null) - Populate (bec); + Populate (bec); } } return bec; } - private void SetCirclesOn (BlogEntry be) + private void SetCirclesOn (BasePost be) { List circles = new List (); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) @@ -510,7 +493,7 @@ namespace Npgsql.Web.Blog } } - private void SetTagsOn (BlogEntry be) + private void SetTagsOn (BasePost be) { List tags = new List (); using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) @@ -534,7 +517,7 @@ namespace Npgsql.Web.Blog Populate (be); } - private void Populate (BlogEntry be) + private void Populate (BasePost be) { SetTagsOn (be); SetCirclesOn (be); @@ -556,7 +539,7 @@ namespace Npgsql.Web.Blog if (title == null) throw new ArgumentNullException ("title"); if (content == null) - throw new ArgumentNullException ("content"); + content = ""; using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "insert into blog (title,bcontent,modified,posted,visible,username,applicationname)" + @@ -589,9 +572,13 @@ namespace Npgsql.Web.Blog using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) { cnx.Open (); using (NpgsqlCommand cmd = cnx.CreateCommand ()) { - cmd.CommandText = "update blog set photo = :photo where _id = :pid"; + if (photo == null) + cmd.CommandText = "update blog set photo = NULL where _id = :pid"; + else { + cmd.CommandText = "update blog set photo = :photo where _id = :pid"; + cmd.Parameters.AddWithValue ("photo", photo); + } cmd.Parameters.AddWithValue ("pid", pid); - cmd.Parameters.AddWithValue ("photo", photo); cmd.ExecuteNonQuery (); } cnx.Close (); @@ -607,6 +594,7 @@ namespace Npgsql.Web.Blog cmd.Parameters.AddWithValue ("pid", pid); cmd.ExecuteNonQuery (); } + if (circles != null) if (circles.Length > 0) using (NpgsqlCommand cmd = cnx.CreateCommand ()) { cmd.CommandText = "insert into blog_access (post_id,circle_id) values (:pid,:cid)"; @@ -640,7 +628,7 @@ namespace Npgsql.Web.Blog using (NpgsqlCommand cmd = cnx.CreateCommand ()) { if (readersName != null) { cmd.CommandText = "select _id, title,bcontent, modified," + - "posted,username,visible,photo " + + "posted,username,visible,photo,rate " + "from blog b left outer join " + "(select count(*)>0 acc, a.post_id pid " + "from blog_access a," + @@ -652,29 +640,29 @@ namespace Npgsql.Web.Blog cmd.Parameters.AddWithValue ("uname", readersName); } else { cmd.CommandText = "select _id, title,bcontent,modified," + - "posted,username,visible,photo " + + "posted,username,visible,photo,rate " + "from blog b left outer join " + "(select count(*)>0 acc, a.post_id pid " + "from blog_access a" + " group by a.post_id) ma on (ma.pid = b._id)" + " where " + - " ma.acc IS NULL and " + + " ( ma.acc IS NULL and " + " b.Visible IS TRUE and " + - " applicationname = :appname"; + " applicationname = :appname ) "; } cmd.Parameters.AddWithValue ("appname", applicationName); if (pattern != null) { if ((searchflags & FindBlogEntryFlags.MatchTag) > 0) { cmd.CommandText += - "AND EXISTS (SELECT tag._id FROM public.tag, public.tagged WHERE " + - "public.tag._id = public.tagged.tagid " + - "AND public.tagged.postid = a.post_id " + - "public.tag.name like :tagname) "; + " AND EXISTS (SELECT tag._id FROM tag, tagged \n" + + " WHERE tag._id = tagged.tagid \n" + + " AND tagged.postid = b._id \n" + + " AND tag.name like :tagname) \n"; cmd.Parameters.AddWithValue ("tagname", pattern); } if ((searchflags & FindBlogEntryFlags.MatchContent) > 0) { - cmd.CommandText += " and bcontent like :bcontent"; + cmd.CommandText += " and bcontent like :bcontent \n"; cmd.Parameters.AddWithValue ("bcontent", pattern); } if ((searchflags & FindBlogEntryFlags.MatchTitle) > 0) { @@ -682,15 +670,16 @@ namespace Npgsql.Web.Blog cmd.Parameters.AddWithValue ("title", pattern); } if ((searchflags & FindBlogEntryFlags.MatchUserName) > 0) { - cmd.CommandText += " and username like :username"; + cmd.CommandText += " and username like :username \n"; cmd.Parameters.AddWithValue ("username", pattern); } } - if ((searchflags & FindBlogEntryFlags.MatchInvisible) == 0) { - cmd.CommandText += " and visible = true"; + if (((searchflags & FindBlogEntryFlags.MatchInvisible) == 0) && + (readersName == null) ) { + cmd.CommandText += " and visible = true \n"; } - cmd.CommandText += " order by posted desc"; + cmd.CommandText += " order by rate desc"; cnx.Open (); using (NpgsqlDataReader rdr = cmd.ExecuteReader ()) { // pageIndex became one based @@ -706,6 +695,7 @@ namespace Npgsql.Web.Blog be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Visible = rdr.GetBoolean (rdr.GetOrdinal ("visible")); + be.Rate = rdr.GetInt32 (rdr.GetOrdinal ("rate")); { int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) @@ -779,11 +769,14 @@ namespace Npgsql.Web.Blog be.Posted = rdr.GetDateTime (rdr.GetOrdinal ("posted")); be.Modified = rdr.GetDateTime (rdr.GetOrdinal ("modified")); be.Visible = true; // because of sql code used + be.Rate = rdr.GetInt32(rdr.GetOrdinal("rate")); { int oph = rdr.GetOrdinal ("photo"); if (!rdr.IsDBNull (oph)) be.Photo = rdr.GetString (oph); } + SetTagsOn (be); + SetCirclesOn (be); c.Add (be); } totalRecords++; diff --git a/NpgsqlBlogProvider/NpgsqlBlogProvider.csproj b/NpgsqlBlogProvider/NpgsqlBlogProvider.csproj index 22b1b709..82c92432 100644 --- a/NpgsqlBlogProvider/NpgsqlBlogProvider.csproj +++ b/NpgsqlBlogProvider/NpgsqlBlogProvider.csproj @@ -34,7 +34,6 @@ - @@ -44,7 +43,6 @@ - ..\packages\Npgsql.3.0.3\lib\net45\Npgsql.dll diff --git a/NpgsqlBlogProvider/NpgsqlTagInfo.cs b/NpgsqlBlogProvider/NpgsqlTagInfo.cs deleted file mode 100644 index 6685b582..00000000 --- a/NpgsqlBlogProvider/NpgsqlTagInfo.cs +++ /dev/null @@ -1,104 +0,0 @@ -// -// NpgsqlTagInfo.cs -// -// Author: -// Paul Schneider -// -// Copyright (c) 2015 GNU GPL -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see . -using System; -using Yavsc.Model.Blogs; -using System.Collections.Generic; - -namespace Npgsql.Web.Blog -{ - /// - /// Npgsql tag info. - /// - public class NpgsqlTagInfo: TagInfo - { - /// - /// Initializes a new instance of the class. - /// - /// Connection string. - /// Tagname. - public NpgsqlTagInfo (string connectionString, string tagname): base(tagname) - { - titles = new List(); - using (NpgsqlConnection cnx = new NpgsqlConnection (connectionString)) - using (NpgsqlCommand cmd = cnx.CreateCommand ()) { - cmd.CommandText = - "SELECT \n" + - " blog.username, \n" + - " blog.posted, \n" + - " blog.modified, \n" + - " blog.title, \n" + - " blog.bcontent, \n" + - " blog.visible, \n" + - " blog._id, \n" + - " blog.photo, \n" + - " tag.name\n" + - "FROM \n" + - " public.blog, \n" + - " public.tagged, \n" + - " public.tag\n" + - "WHERE \n" + - " tagged.postid = blog._id AND \n" + - " tag._id = tagged.tagid AND \n" + - " blog.visible = TRUE AND \n" + - " public.tag.name = :name"; - cmd.Parameters.AddWithValue ("name", tagname); - cnx.Open (); - using (NpgsqlDataReader rdr = cmd.ExecuteReader ()) { - while (rdr.Read ()) { - bool truncated; - int oph = rdr.GetOrdinal ("photo"); - string photo = null; - if (!rdr.IsDBNull (oph)) - photo = rdr.GetString (oph); - var pi = new BasePostInfo { - Title = rdr.GetString(3), - Author = rdr.GetString (0), - Id = rdr.GetInt64 (6), - Intro = MarkdownHelper.MarkdownIntro ( - rdr.GetString (4), - out truncated), - Visible = rdr.GetBoolean(5), - Photo = photo, - Modified = rdr.GetDateTime(2), - Posted = rdr.GetDateTime(1) - }; - titles.Add (pi); - } - } - } - } - - List titles; - - #region implemented abstract members of TagInfo - /// - /// Gets the titles. - /// - /// The titles. - public override System.Collections.Generic.IEnumerable Titles { - get { - return titles; - } - } - #endregion - } -} - diff --git a/NpgsqlContentProvider/ChangeLog b/NpgsqlContentProvider/ChangeLog index 925ef31f..c7cae7ea 100644 --- a/NpgsqlContentProvider/ChangeLog +++ b/NpgsqlContentProvider/ChangeLog @@ -1,9 +1,19 @@ -2015-10-30 Paul Schneider +2015-11-17 Paul Schneider - * NpgsqlContentProvider.cs: refactoring: a dedicated name - space for the catalog + * NpgsqlSkillProvider.cs: User's skills profile object now + implements an `Id` property, + in order to be rated, perhaps ranked in a near future. It's + implemented as the legay profile id. -2015-10-28 Paul Schneider +2015-11-17 Paul Schneider - * NpgsqlCircleProvider.cs: Restores circles edition + * NpgsqlSkillProvider.cs: implements a skill provider + + * NpgsqlContentProvider.csproj: Imports the + `NpgsqlSkillProvider` class + +2015-11-08 Paul Schneider + + * ChangeLog: this file should not be maintained as a git + respository legacy file diff --git a/NpgsqlContentProvider/NpgsqlContentProvider.csproj b/NpgsqlContentProvider/NpgsqlContentProvider.csproj index 591692b4..9b443b14 100644 --- a/NpgsqlContentProvider/NpgsqlContentProvider.csproj +++ b/NpgsqlContentProvider/NpgsqlContentProvider.csproj @@ -48,6 +48,7 @@ + diff --git a/NpgsqlMRPProviders/ChangeLog b/NpgsqlMRPProviders/ChangeLog index b9136c47..62c1c1c4 100644 --- a/NpgsqlMRPProviders/ChangeLog +++ b/NpgsqlMRPProviders/ChangeLog @@ -1,3 +1,12 @@ +2015-11-11 Paul Schneider + + * NpgsqlProfileProvider.cs: return the default value in the + targeted Type, in case of DBNull + +2015-11-06 Paul Schneider + + * NpgsqlProfileProvider.cs: nicer code + 2015-11-04 Paul Schneider * NpgsqlProfileProvider.cs: diff --git a/NpgsqlMRPProviders/NpgsqlProfileProvider.cs b/NpgsqlMRPProviders/NpgsqlProfileProvider.cs index b0e45456..b0a53d25 100644 --- a/NpgsqlMRPProviders/NpgsqlProfileProvider.cs +++ b/NpgsqlMRPProviders/NpgsqlProfileProvider.cs @@ -206,7 +206,7 @@ namespace Npgsql.Web cmd.CommandText = "SELECT * from profiledata, profiles where " + "profiledata.uniqueid = profiles.uniqueid " + "and profiles.username = @username " + - "and profiles.applicationname = @appname"; + "and profiles.applicationname = @appname "; cmd.Parameters.AddWithValue ("@username", username); cmd.Parameters.AddWithValue ("@appname", applicationName); cnx.Open (); @@ -216,7 +216,8 @@ namespace Npgsql.Web foreach (SettingsProperty p in collection) { SettingsPropertyValue v = new SettingsPropertyValue (p); int o = r.GetOrdinal (p.Name.ToLower ()); - v.PropertyValue = r.GetValue (o); + var obj = r.GetValue (o); + v.PropertyValue = (obj is DBNull) ? GetDefaultValue (p) : obj; c.Add (v); } } else { diff --git a/README.md b/README.md index 7c897da6..6076279d 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,22 @@ yavsc ===== [doc-fr](http://yavsc.pschneider.fr/Blogs/UserPost/paul/Documentation) + +# TODO FIRST + +1) Implement a Skills provider + +2) Create an `UserCardControl` + with quick access for users to his chat and the circle membership, and for admins to his roles, a blogentry count, and a link to the booking system + +3) Api refatoring: + + Concerning the blog entry edition, we only need Two methods: + + * ```long PostFile(long id)```, + used for creation when the given id is 0, in which case, the entry id created is returned. + Otherwise, used for import in the post spécified by its id, in which case, 0 is returned. + * `long Post(BlogEntry be)`, used to create or update a given or not + blog entry content. the returned value is the entry id at creation, or 0. + +4) UI themes diff --git a/TestAPI/ChangeLog b/TestAPI/ChangeLog index 1ffa62c9..9c11fcf8 100644 --- a/TestAPI/ChangeLog +++ b/TestAPI/ChangeLog @@ -1,3 +1,7 @@ +2015-11-14 Paul Schneider + + * TestAPI.csproj: nothing to view + 2015-11-04 Paul Schneider * test-domain-TestAPI.config: diff --git a/TestAPI/TestAPI.csproj b/TestAPI/TestAPI.csproj index 0668c673..a3b0eeef 100644 --- a/TestAPI/TestAPI.csproj +++ b/TestAPI/TestAPI.csproj @@ -79,6 +79,11 @@ nunit + + + nuget-core + + @@ -96,10 +101,6 @@ {68F5B80A-616E-4C3C-91A0-828AA40000BD} YavscModel - - {77044C92-D2F1-45BD-80DD-AA25B311B027} - Web - {C6E9E91B-97D3-48D9-8AA7-05356929E162} NpgsqlBlogProvider @@ -116,6 +117,10 @@ {90BF2234-7252-4CD5-B2A4-17501B19279B} SalesCatalog + + {77044C92-D2F1-45BD-80DD-AA25B311B027} + Web + diff --git a/WebControls/ChangeLog b/WebControls/ChangeLog index 0289448f..ab0aaaf5 100644 --- a/WebControls/ChangeLog +++ b/WebControls/ChangeLog @@ -1,3 +1,14 @@ +2015-11-17 Paul Schneider + + * RateControl.cs: refactorization + +2015-11-14 Paul Schneider + + * RateControl.cs: Uses its data model, and moves to the + general name space. + + * WebControls.csproj: Implements a server side rating control + 2015-10-13 Paul Schneider * ResultPages.cs: A multi-pages result meta info when one page diff --git a/WebControls/WebControls.csproj b/WebControls/WebControls.csproj index c4d0721b..9a4b4a0c 100644 --- a/WebControls/WebControls.csproj +++ b/WebControls/WebControls.csproj @@ -47,6 +47,7 @@ + diff --git a/Yavsc.sln b/Yavsc.sln index 92b16c03..c958fc27 100644 --- a/Yavsc.sln +++ b/Yavsc.sln @@ -27,7 +27,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject LICENSE = LICENSE Makefile = Makefile - noavatar.xcf = noavatar.xcf README.md = README.md EndProjectSection EndProject diff --git a/booking/Admin/DataManager.cs b/booking/Admin/DataManager.cs new file mode 100644 index 00000000..33b8a71d --- /dev/null +++ b/booking/Admin/DataManager.cs @@ -0,0 +1,157 @@ +using System; +using System.Diagnostics; +using System.IO; +using Yavsc.Model.Admin; +using Npgsql.Web.Blog; +using System.Resources; +using System.Reflection; + +namespace Yavsc.Admin +{ + /// + /// Data manager. + /// + public class DataManager + { + DataAccess da; + /// + /// Initializes a new instance of the class. + /// + /// Datac. + public DataManager (DataAccess datac) + { + da = datac; + } + /// + /// Creates the backup. + /// + /// The backup. + public Export CreateBackup () + { + Environment.SetEnvironmentVariable("PGPASSWORD", da.Password); + string fileName = da.BackupPrefix + "-" + DateTime.Now.ToString ("yyyyMMddhhmmss")+".tar"; + FileInfo ofi = new FileInfo (fileName); + Export e = new Export (); + e.FileName = ofi.FullName; + /* + Exec ("pg_dump", string.Format ( + "-wb -Z3 -f {0} -Ft -h {1} -U {2} -p {3} {4}", + fileName, da.Host, da.Dbuser, da.Port, da.Dbname ),e); + */ + Exec ("pg_dump", string.Format ( + "-f {0} -Ft -h {1} -U {2} -p {3} {4}", + fileName, da.Host, da.DbUser, da.Port, da.DbName ),e); + return e; + } + + private void Exec(string name, string args, TaskOutput output) + { + ProcessStartInfo Pinfo = + new ProcessStartInfo (name,args); + Pinfo.RedirectStandardError = true; + Pinfo.RedirectStandardOutput = true; + Pinfo.CreateNoWindow = true; + Pinfo.UseShellExecute = false; + using (Process p = new Process ()) { + p.EnableRaisingEvents = true; + p.StartInfo = Pinfo; + p.Start (); + p.WaitForExit (); + output.Error = p.StandardError.ReadToEnd (); + output.Message = p.StandardOutput.ReadToEnd (); + output.ExitCode = p.ExitCode; + p.Close (); + } + } + /// + /// Restore the specified fileName and dataOnly. + /// + /// File name. + /// If set to true data only. + public TaskOutput Restore (string fileName, bool dataOnly) + { + Environment.SetEnvironmentVariable("PGPASSWORD", da.Password); + var t = new TaskOutput (); + Exec ("pg_restore", (dataOnly?"-a ":"")+string.Format ( + "-1 -Ft -O -h {0} -U {1} -p {2} -d {3} {4}", + da.Host, da.DbUser, da.Port, da.DbName, fileName ),t); + /* + Exec ("pg_restore", (dataOnly?"-a ":"")+string.Format ( + "-1 -w -Fd -O -h {0} -U {1} -p {2} -d {3} {4}", + da.Host, da.Dbuser, da.Port, da.Dbname, fileName ),t); + */ + return t; + } + /// + /// Creates the db. + /// + /// The db. + public TaskOutput CreateDb () + { + TaskOutput res = new TaskOutput (); + + string sql; + try { + Assembly a = System.Reflection.Assembly.GetExecutingAssembly(); + using (Stream sqlStream = a.GetManifestResourceStream("Yavsc.instdbws.sql")) + { + try { using (StreamReader srdr = new StreamReader (sqlStream)) { + sql = srdr.ReadToEnd (); + using (var cnx = new Npgsql.NpgsqlConnection (da.ConnectionString)) { + using (var cmd = cnx.CreateCommand ()) { + cmd.CommandText = sql; + cnx.Open(); + cmd.ExecuteNonQuery(); + cnx.Close(); + } + } + } } catch (Exception exg) { + res.ExitCode = 1; + res.Error = + string.Format ("Exception of type {0} occred retrieving the script", + exg.GetType ().Name); + res.Message = exg.Message; + } + } + } + catch (Exception ex) { + res.ExitCode = 1; + res.Error = + string.Format ("Exception of type {0} occured during the script execution", + ex.GetType ().Name); + res.Message = ex.Message; + } + + return res; + } + /// + /// Tags the backup. + /// + /// The backup. + /// Filename. + /// Tags. + public Export TagBackup (string filename, string [] tags) + { + /* FileInfo fi = new FileInfo (filename); + using (FileStream s = fi.OpenWrite ()) { + + } */ + throw new NotImplementedException (); + } + /// + /// Tags the restore. + /// + /// The restore. + /// File name. + public TaskOutput TagRestore (string fileName) + { + Environment.SetEnvironmentVariable ("PGPASSWORD", da.Password); + var t = new TaskOutput (); + Exec ("pg_restore", string.Format ( + "-a -w -Fd -O -h {0} -U {1} -p {2} -d {3} {4}", + da.Host, da.DbUser, da.Port, da.DbName, fileName ),t); + return t; + } + } +} + diff --git a/booking/Admin/Export.cs b/booking/Admin/Export.cs new file mode 100644 index 00000000..e2120f43 --- /dev/null +++ b/booking/Admin/Export.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel; + +namespace Yavsc.Model.Admin +{ + /// + /// Export. + /// + public class Export: TaskOutput + { + /// + /// Initializes a new instance of the class. + /// + public Export () + { + } + /// + /// Gets or sets the name of the file. + /// + /// The name of the file. + public string FileName { get; set; } + } +} + diff --git a/booking/ApiControllers/AccountController.cs b/booking/ApiControllers/AccountController.cs new file mode 100644 index 00000000..397113b0 --- /dev/null +++ b/booking/ApiControllers/AccountController.cs @@ -0,0 +1,116 @@ +// +// AccountController.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 GNU GPL +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Web.Http; +using System.Net.Http; +using Yavsc.Model.RolesAndMembers; +using System.Web.Security; +using System.Web.Profile; +using Yavsc.Helpers; +using System.Collections.Specialized; + +namespace Yavsc.ApiControllers +{ + /// + /// Account controller. + /// + public class AccountController : YavscController + { + + /// + /// Register the specified model. + /// + /// Model. + [Authorize ()] + [ValidateAjaxAttribute] + public HttpResponseMessage Register ([FromBody] RegisterClientModel model) + { + + if (ModelState.IsValid) { + if (model.IsApprouved) + if (!Roles.IsUserInRole ("Admin")) + if (!Roles.IsUserInRole ("FrontOffice")) { + ModelState.AddModelError ("Register", + "Since you're not member of Admin or FrontOffice groups, " + + "you cannot ask for a pre-approuved registration"); + return DefaultResponse (); + } + MembershipCreateStatus mcs; + var user = Membership.CreateUser ( + model.UserName, + model.Password, + model.Email, + model.Question, + model.Answer, + model.IsApprouved, + out mcs); + switch (mcs) { + case MembershipCreateStatus.DuplicateEmail: + ModelState.AddModelError ("Email", "Cette adresse e-mail correspond " + + "à un compte utilisateur existant"); + break; + case MembershipCreateStatus.DuplicateUserName: + ModelState.AddModelError ("UserName", "Ce nom d'utilisateur est " + + "déjà enregistré"); + break; + case MembershipCreateStatus.Success: + if (!model.IsApprouved) + Url.SendActivationMessage (user); + ProfileBase prtu = ProfileBase.Create (model.UserName); + prtu.SetPropertyValue ("Name", model.Name); + prtu.SetPropertyValue ("Address", model.Address); + prtu.SetPropertyValue ("CityAndState", model.CityAndState); + prtu.SetPropertyValue ("Mobile", model.Mobile); + prtu.SetPropertyValue ("Phone", model.Phone); + prtu.SetPropertyValue ("ZipCode", model.ZipCode); + break; + default: + break; + } + } + return DefaultResponse (); + } + + /// + /// Resets the password. + /// + /// Model. + [ValidateAjax] + public void ResetPassword (LostPasswordModel model) + { + StringDictionary errors; + MembershipUser user; + YavscHelpers.ValidatePasswordReset (model, out errors, out user); + foreach (string key in errors.Keys) + ModelState.AddModelError (key, errors [key]); + if (user != null && ModelState.IsValid) + Url.SendActivationMessage (user); + } + + [ValidateAjax] + [Authorize(Roles="Admin")] + public void AddUserToRole(UserRole model) + { + Roles.AddUserToRole (model.UserName, model.Role); + } + + } +} diff --git a/booking/ApiControllers/AuthorizationDenied.cs b/booking/ApiControllers/AuthorizationDenied.cs new file mode 100644 index 00000000..2193e6aa --- /dev/null +++ b/booking/ApiControllers/AuthorizationDenied.cs @@ -0,0 +1,55 @@ +// +// AuthorizationDenied.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 GNU GPL +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Web.Http; +using System.Web.Profile; +using System.Web.Security; +using Yavsc.Formatters; +using Yavsc.Helpers; +using Yavsc.Model; +using Yavsc.Model.FrontOffice; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Model.WorkFlow; +using System.IO; + +namespace Yavsc.ApiControllers +{ + + /// + /// Authorization denied. + /// + public class AuthorizationDenied : HttpRequestException { + + /// + /// Initializes a new instance of the Yavsc.ApiControllers.AuthorizationDenied class. + /// + /// Message. + public AuthorizationDenied(string msg) : base(msg) + { + } + } + +} diff --git a/booking/ApiControllers/BasketController.cs b/booking/ApiControllers/BasketController.cs new file mode 100644 index 00000000..b31cc91d --- /dev/null +++ b/booking/ApiControllers/BasketController.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Security; +using System.Web.Http; +using Yavsc.Model.WorkFlow; +using System.Collections.Specialized; +using Yavsc.Model.FrontOffice; + +namespace Yavsc.ApiControllers +{ + /// + /// Basket controller. + /// Maintains a collection of articles + /// qualified with name value pairs + /// + public class BasketController : ApiController + { + /// + /// The wfmgr. + /// + protected WorkFlowManager wfmgr = null; + + /// + /// Initialize the specified controllerContext. + /// + /// Controller context. + protected override void Initialize (System.Web.Http.Controllers.HttpControllerContext controllerContext) + { + base.Initialize (controllerContext); + wfmgr = new WorkFlowManager (); + } + + /// + /// Gets the current basket, creates a new one, if it doesn't exist. + /// + /// The current basket. + protected CommandSet CurrentBasket { + get { + CommandSet b = wfmgr.GetCommands (Membership.GetUser ().UserName); + if (b == null) b = new CommandSet (); + return b; + } + } + + /// + /// Create the specified basket item using specified command parameters. + /// + /// Command parameters. + [Authorize] + public long Create(NameValueCollection cmdParams) + { + // HttpContext.Current.Request.Files + Command cmd = new Command(cmdParams, HttpContext.Current.Request.Files); + CurrentBasket.Add (cmd); + return cmd.Id; + } + + /// + /// Read the specified basket item. + /// + /// Itemid. + [Authorize] + Command Read(long itemid){ + return CurrentBasket[itemid]; + } + + /// + /// Update the specified item parameter using the specified value. + /// + /// Item identifier. + /// Parameter name. + /// Value. + [Authorize] + public void UpdateParam(long itemid, string param, string value) + { + CurrentBasket [itemid].Parameters [param] = value; + } + + /// + /// Delete the specified item. + /// + /// Item identifier. + [Authorize] + public void Delete(long itemid) + { + CurrentBasket.Remove (itemid); + } + + } +} \ No newline at end of file diff --git a/booking/ApiControllers/BlogsController.cs b/booking/ApiControllers/BlogsController.cs new file mode 100644 index 00000000..82ad69f4 --- /dev/null +++ b/booking/ApiControllers/BlogsController.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Security; +using System.Web.Http; +using Npgsql.Web.Blog; +using Yavsc.Model.Blogs; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using System.Diagnostics; +using Yavsc.Formatters; +using Yavsc.Model; + +namespace Yavsc.ApiControllers +{ + /// + /// Blogs API controller. + /// + public class BlogsController : YavscController + { + /// + /// Tag the specified model. + /// + /// Model. + [Authorize, + AcceptVerbs ("POST")] + public void Tag (PostTag model) { + if (ModelState.IsValid) { + BlogManager.GetForEditing (model.PostId); + BlogManager.Tag (model.PostId, model.Tag); + } + } + static string [] officalTags = new string[] { "Artistes", "Accueil", "Événements", "Mentions légales", "Admin", "Web" } ; + /// + /// Tags the specified pattern. + /// + /// Pattern. + [ValidateAjaxAttribute] + public IEnumerable Tags(string pattern) + { + return officalTags; + } + + /// + /// Untag the specified model. + /// + /// Model. + [Authorize, + AcceptVerbs ("POST")] + public void Untag (PostTag model) { + if (ModelState.IsValid) { + BlogManager.GetForEditing (model.PostId); + BlogManager.Untag (model.PostId, model.Tag); + } + } + + /// + /// Removes the post. + /// + /// User. + /// Title. + [Authorize, ValidateAjaxAttribute, HttpPost] + public void RemoveTitle(string user, string title) { + if (Membership.GetUser ().UserName != user) + if (!Roles.IsUserInRole("Admin")) + throw new AuthorizationDenied (user); + BlogManager.RemoveTitle (user, title); + } + + /// + /// Removes the tag. + /// + /// Tagid. + [Authorize, ValidateAjaxAttribute, HttpPost] + public void RemoveTag([FromBody] long tagid) { + + throw new NotImplementedException (); + } + + /// + /// The allowed media types. + /// + protected string[] allowedMediaTypes = { + "text/plain", + "text/x-tex", + "text/html", + "image/png", + "image/gif", + "image/jpeg", + "image/x-xcf", + "application/pdf", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + }; + + /// + /// Posts the file. + /// + /// The file. + [Authorize, HttpPost] + public async Task PostFile(long id) { + if (!(Request.Content.Headers.ContentType.MediaType=="multipart/form-data")) + throw new HttpRequestException ("not a multipart/form-data request"); + BlogEntry be = BlogManager.GetPost (id); + if (be.Author != Membership.GetUser ().UserName) + throw new AuthorizationDenied ("b"+id); + string root = HttpContext.Current.Server.MapPath("~/bfiles/"+id); + DirectoryInfo di = new DirectoryInfo (root); + if (!di.Exists) di.Create (); + + var provider = new MultipartFormDataStreamProvider(root); + try + { + // Read the form data. + await Request.Content.ReadAsMultipartAsync(provider) ; + var invalidChars = Path.GetInvalidFileNameChars(); + foreach (var f in provider.FileData) { + string filename = f.LocalFileName; + string orgname = f.Headers.ContentDisposition.FileName; + Trace.WriteLine(filename); + string nicename = HttpUtility.UrlDecode(orgname) ; + if (orgname.StartsWith("\"") && orgname.EndsWith("\"") && orgname.Length > 2) + nicename = orgname.Substring(1,orgname.Length-2); + nicename = new string (nicename.Where( x=> !invalidChars.Contains(x)).ToArray()); + nicename = nicename.Replace(' ','_'); + var dest = Path.Combine(root,nicename); + var fi = new FileInfo(dest); + if (fi.Exists) fi.Delete(); + File.Move(filename, fi.FullName); + } + + return Request.CreateResponse(HttpStatusCode.OK); + } + catch (System.Exception e) + { + return Request.CreateResponse(HttpStatusCode.InternalServerError, e); + } + } + + /// + /// Create the specified blog entry. + /// + /// Bp. + [Authorize, HttpPost] + public long Create (BasePost bp) + { + return BlogManager.Post (User.Identity.Name, bp.Title, "", bp.Visible, null); + } + + [Authorize, HttpPost] + public void Note (long id, int note) + { + if (note < 0 || note > 100) + throw new ArgumentException ("0<=note<=100"); + BlogManager.Note (id, note); + } + /// + /// Searchs the file. + /// + /// The file. + /// Postid. + /// Terms. + [HttpGet] + public async Task SearchFile(long id, string terms) { + throw new NotImplementedException (); + } + + /// + /// Sets the photo. + /// + /// Identifier. + /// Photo. + [Authorize, HttpPost, ValidateAjaxAttribute] + public void SetPhoto(long id, [FromBody] string photo) + { + BlogManager.Provider.UpdatePostPhoto (id, photo); + } + + /// + /// Import the specified id. + /// + /// Identifier. + [Authorize, HttpPost, ValidateAjaxAttribute] + public async Task Import(long id) { + if (!(Request.Content.Headers.ContentType.MediaType=="multipart/form-data")) + throw new HttpRequestException ("not a multipart/form-data request"); + BlogEntry be = BlogManager.GetPost (id); + if (be.Author != Membership.GetUser ().UserName) + throw new AuthorizationDenied ("post: "+id); + string root = HttpContext.Current.Server.MapPath("~/bfiles/"+id); + DirectoryInfo di = new DirectoryInfo (root); + if (!di.Exists) di.Create (); + var provider = new MultipartFormDataStreamProvider(root); + try + { + // Read the form data. + //IEnumerable data = + await Request.Content.ReadAsMultipartAsync(provider) ; + + var invalidChars = Path.GetInvalidFileNameChars(); + List bodies = new List(); + + foreach (var f in provider.FileData) { + string filename = f.LocalFileName; + + string nicename= f.Headers.ContentDisposition.FileName; + var filtered = new string (nicename.Where( x=> !invalidChars.Contains(x)).ToArray()); + + FileInfo fi = new FileInfo(filtered); + FileInfo fo = new FileInfo(filtered+".md"); + FileInfo fp = new FileInfo (Path.Combine(root,filename)); + if (fi.Exists) fi.Delete(); + fp.MoveTo(fi.FullName); + // TODO Get the mime type + using (Process p = new Process ()) { + p.StartInfo.WorkingDirectory = root; + p.StartInfo = new ProcessStartInfo (); + p.StartInfo.UseShellExecute = false; + p.StartInfo.FileName = "/usr/bin/pandoc"; + p.StartInfo.Arguments = + string.Format (" -o '{0}' -t markdown '{1}'", + fo.FullName, + fi.FullName); + p.StartInfo.RedirectStandardError = true; + p.StartInfo.RedirectStandardOutput = true; + p.Start (); + p.WaitForExit (); + if (p.ExitCode != 0) { + return Request.CreateResponse (HttpStatusCode.InternalServerError, + "# Import failed with exit code: " + p.ExitCode + "---\n" + + LocalizedText.ImportException + "---\n" + + p.StandardError.ReadToEnd() + "---\n" + + p.StandardOutput.ReadToEnd() + ); + } + } + bodies.Add(fo.OpenText().ReadToEnd()); + + + fi.Delete(); + fo.Delete(); + } + + return Request.CreateResponse(HttpStatusCode.OK,string.Join("\n---\n",bodies),new SimpleFormatter("text/plain")); + + } + catch (System.Exception e) + { + return Request.CreateResponse(HttpStatusCode.InternalServerError, e); + } + } + } +} + diff --git a/booking/ApiControllers/CalendarController.cs b/booking/ApiControllers/CalendarController.cs new file mode 100644 index 00000000..686c7730 --- /dev/null +++ b/booking/ApiControllers/CalendarController.cs @@ -0,0 +1,204 @@ +// +// CalendarController.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Web.Http; +using Yavsc.Model.RolesAndMembers; +using System.Web.Security; +using Yavsc.Model.Google; +using Yavsc.Helpers; +using System.Web.Profile; +using Yavsc.Model.Circles; +using Yavsc.Model.Calendar; +using System.Web.Http.Routing; + + +namespace Yavsc.ApiControllers +{ + /// + /// Night flash controller. + /// + public class CalendarController: ApiController + { + YaEvent[] getTestList() + { + return new YaEvent[] { + new YaEvent () { + Description = "Test Descr", + Title = "Night club special bubble party", + Location = new Position () { + Longitude = 0, + Latitude = 0 + } + }, + new YaEvent () { + Title = "Test2", + Photo = "http://bla/im.png", + Location = new Position () { + Longitude = 0, + Latitude = 0 + } + }, + new YaEvent () { + Description = "Test Descr", + Title = "Night club special bubble party", + Location = new Position () { + Longitude = 0, + Latitude = 0 + } + }, + new YaEvent () { + Title = "Test2", + Photo = "http://bla/im.png", + Location = new Position () { + Longitude = 0, + Latitude = 0 + } + } + }; + } + + /// + /// List events according the specified search arguments. + /// + /// Arguments. + [ValidateAjaxAttribute] + [HttpGet] + public YaEvent[] List ([FromUri] PositionAndKeyphrase args) + { + return getTestList(); + } + + /// + /// Provider the specified ProviderId. + /// + /// Provider identifier. + [HttpGet] + public ProviderPublicInfo ProviderInfo ([FromUri] string ProviderId) + { + return new ProviderPublicInfo () { + DisplayName = "Yavsc clubing", + WebPage = "http://yavsc.pschneider.fr/", + Calendar = new Schedule () { + Period = Periodicity.ThreeM, + WeekDays = new OpenDay[] { new OpenDay () { Day = WeekDay.Saturday, + Start = new TimeSpan(18,00,00), + End = new TimeSpan(2,00,00) + } }, + Validity = new Period[] { new Period() { + Start = new DateTime(2015,5,29), + End = new DateTime(2015,5,30)} } + }, + Description = "Yavsc Entertainment Production, Yet another private party", + LogoImgLocator = "http://yavsc.pschneider.fr/favicon.png", + Location = new Position () { Longitude = 0, Latitude = 0 }, + LocationType = "Salle des fêtes" + }; + } + + /// + /// Posts the image. + /// + /// The image. + /// NF prov identifier. + public string PostImage([FromUri] string NFProvId) + { + return null; + } + + /// + /// Posts the event. + /// + /// The event identifier. + /// Ev. + public int PostEvent ([FromBody] ProvidedEvent ev) + { + throw new NotImplementedException(); + + } + + /// + /// Registers with push notifications enabled. + /// + /// Model. + [ValidateAjax] + public void RegisterWithPushNotifications(GCMRegisterModel model) + { + if (ModelState.IsValid) { + MembershipCreateStatus mcs; + var user = Membership.CreateUser ( + model.UserName, + model.Password, + model.Email, + null, + null, + false, + out mcs); + switch (mcs) { + case MembershipCreateStatus.DuplicateEmail: + ModelState.AddModelError ("Email", "Cette adresse e-mail correspond " + + "à un compte utilisateur existant"); + break; + case MembershipCreateStatus.DuplicateUserName: + ModelState.AddModelError ("Author", "Ce nom d'utilisateur est " + + "déjà enregistré"); + break; + case MembershipCreateStatus.Success: + Url.SendActivationMessage (user); + // TODO set registration id + throw new NotImplementedException (); + } + } + } + + /// + /// Sets the registration identifier. + /// + /// Registration identifier. + [Authorize] + public void SetRegistrationId(string registrationId) + { + // TODO set registration id + setRegistrationId (Membership.GetUser ().UserName, registrationId); + } + + private void setRegistrationId(string username, string regid) { + ProfileBase pr = ProfileBase.Create(username); + pr.SetPropertyValue ("gregid", regid); + } + + /// + /// Notifies the event. + /// + /// Evpub. + public MessageWithPayloadResponse NotifyEvent(EventPub evpub) { + SimpleJsonPostMethod,MessageWithPayloadResponse> r = + new SimpleJsonPostMethod,MessageWithPayloadResponse>( + "https://gcm-http.googleapis.com/gcm/send"); + using (r) { + var msg = new MessageWithPayload () { data = new YaEvent[] { (YaEvent)evpub } }; + msg.to = string.Join (" ", Circle.Union (evpub.Circles)); + return r.Invoke (msg); + } + } + } +} + + diff --git a/booking/ApiControllers/CircleController.cs b/booking/ApiControllers/CircleController.cs new file mode 100644 index 00000000..da7f7092 --- /dev/null +++ b/booking/ApiControllers/CircleController.cs @@ -0,0 +1,138 @@ +// +// CircleController.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 GNU GPL +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Web.Http; +using Yavsc.Model.RolesAndMembers; +using System.Collections.Generic; +using Yavsc.Model.Circles; +using System.Web.Security; +using System.Collections.Specialized; +using Yavsc.Model; + +namespace Yavsc.ApiControllers +{ + + /// + /// Circle controller. + /// + public class CircleController : ApiController + { + + /// + /// Create the specified circle. + /// + /// Model. + [Authorize, + AcceptVerbs ("POST")] + public long Create(Circle model) + { + string user = Membership.GetUser ().UserName; + return CircleManager.DefaultProvider.Create (user, model.Title, model.Members); + } + + /// + /// Add the specified users to the circle. + /// + /// Circle Identifier. + /// username. + [Authorize, + AcceptVerbs ("POST")] + public void Add(long id, string username) + { + checkIsOwner (CircleManager.DefaultProvider.Get (id)); + CircleManager.DefaultProvider.AddMember (id, username); + } + + + /// + /// Delete the circle specified by id. + /// + /// Identifier. + [Authorize, + AcceptVerbs ("GET")] + public void Delete(long id) + { + checkIsOwner (CircleManager.DefaultProvider.Get (id)); + CircleManager.DefaultProvider.Delete (id); + } + + + /// + /// Removes the user from circle. + /// + /// Identifier. + /// Username. + [Authorize, + AcceptVerbs ("GET")] + public void RemoveUserFromCircle(long id, string username) + { + checkIsOwner (CircleManager.DefaultProvider.Get(id)); + CircleManager.DefaultProvider.RemoveMembership (id,username); + } + + private void checkIsOwner(CircleBase c) + { + string user = Membership.GetUser ().UserName; + if (c.Owner != user) + throw new AccessViolationException ("You're not owner of this circle"); + } + + /// + /// Get the circle specified id. + /// + /// Identifier. + [Authorize, + AcceptVerbs ("GET")] + public Circle Get(long id) + { + var c = CircleManager.DefaultProvider.GetMembers (id); + checkIsOwner (c); + return c; + } + + /// + /// List the circles + /// + [Authorize, + AcceptVerbs ("GET")] + public IEnumerable List() + { + string user = Membership.GetUser ().UserName; + return CircleManager.DefaultProvider.List (user); + } + + /// + /// List the circles + /// + [Authorize, + AcceptVerbs ("POST")] + public void Update(CircleBase circle) + { + string user = Membership.GetUser ().UserName; + CircleBase current = CircleManager.DefaultProvider.Get (circle.Id); + if (current.Owner != user) + throw new AuthorizationDenied ("Your not owner of circle at id "+circle.Id); + CircleManager.DefaultProvider.UpdateCircle (circle); + } + + } +} + diff --git a/booking/ApiControllers/FrontOfficeController.cs b/booking/ApiControllers/FrontOfficeController.cs new file mode 100644 index 00000000..a2079981 --- /dev/null +++ b/booking/ApiControllers/FrontOfficeController.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Web.Http; +using System.Web.Profile; +using System.Web.Security; +using Yavsc.Formatters; +using Yavsc.Helpers; +using Yavsc.Model; +using Yavsc.Model.FrontOffice; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Model.WorkFlow; +using System.IO; +using Yavsc.Model.FrontOffice.Catalog; + +namespace Yavsc.ApiControllers +{ + + + /// + /// Front office controller. + /// + public class FrontOfficeController : ApiController + { + /// + /// The wfmgr. + /// + protected WorkFlowManager wfmgr = null; + + /// + /// Initialize the specified controllerContext. + /// + /// Controller context. + protected override void Initialize (System.Web.Http.Controllers.HttpControllerContext controllerContext) + { + base.Initialize (controllerContext); + wfmgr = new WorkFlowManager (); + } + + /// + /// Catalog this instance. + /// + [AcceptVerbs ("GET")] + public Catalog Catalog () + { + Catalog c = CatalogManager.GetCatalog (); + return c; + } + + /// + /// Gets the product categorie. + /// + /// The product categorie. + /// Brand name. + /// Prod categorie. + [AcceptVerbs ("GET")] + public ProductCategory GetProductCategorie (string brandName, string prodCategorie) + { + return CatalogManager.GetCatalog ().GetBrand (brandName).GetProductCategory (prodCategorie); + } + + + /// + /// Gets the estimate. + /// + /// The estimate. + /// Estimate Id. + [Authorize] + [HttpGet] + public Estimate GetEstimate (long id) + { + Estimate est = wfmgr.ContentProvider.Get (id); + string username = Membership.GetUser ().UserName; + if (est.Client != username) + if (!Roles.IsUserInRole("Admin")) + if (!Roles.IsUserInRole("FrontOffice")) + throw new AuthorizationDenied ( + string.Format ( + "Auth denied to eid {1} for:{2}", + id, username)); + return est; + } + + /// + /// Gets the estim tex. + /// + /// The estim tex. + /// Estimate id. + [AcceptVerbs ("GET")] + public HttpResponseMessage EstimateToTex (long id) + { + string texest = estimateToTex (id); + if (texest == null) + throw new InvalidOperationException ( + "Not an estimate"); + HttpResponseMessage result = new HttpResponseMessage () { + Content = new ObjectContent (typeof(string), + texest, + new SimpleFormatter ("text/x-tex")) + }; + result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue ("attachment") { + FileName = "estimate-" + id.ToString () + ".tex" + }; + return result; + } + + private string estimateToTex (long estimid) + { + Yavsc.templates.Estim tmpe = new Yavsc.templates.Estim (); + Estimate e = wfmgr.GetEstimate (estimid); + tmpe.Session = new Dictionary (); + tmpe.Session.Add ("estim", e); + Profile prpro = new Profile (ProfileBase.Create (e.Responsible)); + if (!prpro.HasBankAccount) + throw new TemplateException ("NotBankable:" + e.Responsible); + if (!prpro.HasPostalAddress) + throw new TemplateException ("NoPostalAddress:" + e.Responsible); + + Profile prcli = new Profile (ProfileBase.Create (e.Client)); + if (!prcli.IsBillable) + throw new TemplateException ("NotBillable:" + e.Client); + + + tmpe.Session.Add ("from", prpro); + tmpe.Session.Add ("to", prcli); + tmpe.Session.Add ("efrom", Membership.GetUser (e.Responsible).Email); + tmpe.Session.Add ("eto", Membership.GetUser (e.Client).Email); + tmpe.Init (); + return tmpe.TransformText (); + } + + /// + /// Gets the estimate in pdf format from tex generation. + /// + /// The to pdf. + /// Estimid. + [AcceptVerbs("GET")] + public HttpResponseMessage EstimateToPdf (long id) + { + string texest = null; + try { + texest = estimateToTex (id); + } catch (TemplateException ex) { + return new HttpResponseMessage (HttpStatusCode.OK) { Content = + new ObjectContent (typeof(string), + ex.Message, new ErrorHtmlFormatter (HttpStatusCode.NotAcceptable, + LocalizedText.DocTemplateException + )) + }; + } catch (Exception ex) { + return new HttpResponseMessage (HttpStatusCode.OK) { Content = + new ObjectContent (typeof(string), + ex.Message, new ErrorHtmlFormatter (HttpStatusCode.InternalServerError, + LocalizedText.DocTemplateException)) + }; + } + if (texest == null) + return new HttpResponseMessage (HttpStatusCode.OK) { Content = + new ObjectContent (typeof(string), "Not an estimation id:" + id, + new ErrorHtmlFormatter (HttpStatusCode.NotFound, + LocalizedText.Estimate_not_found)) + }; + + var memPdf = new MemoryStream (); + try { + new TexToPdfFormatter ().WriteToStream ( + typeof(string), texest, memPdf,null); + } + catch (FormatterException ex) { + return new HttpResponseMessage (HttpStatusCode.OK) { Content = + new ObjectContent (typeof(string), ex.Message+"\n\n"+ex.Output+"\n\n"+ex.Error, + new ErrorHtmlFormatter (HttpStatusCode.InternalServerError, + LocalizedText.InternalServerError)) + }; + } + + var result = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new ByteArrayContent(memPdf.GetBuffer()) + }; + + result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue ("attachment") { + FileName = String.Format ( + "Estimation-{0}.pdf", + id) + }; + + return result; + } + + } +} + diff --git a/booking/ApiControllers/GCMController.cs b/booking/ApiControllers/GCMController.cs new file mode 100644 index 00000000..69e992b8 --- /dev/null +++ b/booking/ApiControllers/GCMController.cs @@ -0,0 +1,33 @@ +// +// GCMController.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 GNU GPL +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Web.Http; + +namespace Yavsc.ApiControllers +{ + public class GCMController : ApiController + { + public GCMController () + { + } + } +} + diff --git a/booking/ApiControllers/PaypalController.cs b/booking/ApiControllers/PaypalController.cs new file mode 100644 index 00000000..be5953c7 --- /dev/null +++ b/booking/ApiControllers/PaypalController.cs @@ -0,0 +1,77 @@ +// +// PaypalApiController.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Web.Http; + + +using PayPal; +using System.Collections.Generic; +using PayPal.OpenIdConnect; +using PayPal.Manager; +using PayPal.PayPalAPIInterfaceService; +using PayPal.PayPalAPIInterfaceService.Model; + +namespace Yavsc.ApiControllers +{ + /// + /// Paypal API controller. + /// + public class PaypalController: ApiController + { + PayPalAPIInterfaceServiceService service = null; + /// + /// Initialize the specified controllerContext. + /// + /// Controller context. + protected override void Initialize (System.Web.Http.Controllers.HttpControllerContext controllerContext) + { + base.Initialize (controllerContext); + // Get the config properties from PayPal.Api.ConfigManager + // Create the Classic SDK service instance to use. + service = new PayPalAPIInterfaceServiceService(ConfigManager.Instance.GetProperties()); + } + /// + /// Search the specified str. + /// + /// str. + public BMCreateButtonResponseType Create(string str) + { + BMCreateButtonRequestType btcrerqu = new BMCreateButtonRequestType (); + BMCreateButtonReq btcrerq = new BMCreateButtonReq (); + btcrerq.BMCreateButtonRequest = btcrerqu; + BMCreateButtonResponseType btcrere = service.BMCreateButton (btcrerq); + return btcrere; + } + /// + /// Search the specified str. + /// + /// String. + public BMButtonSearchResponseType Search(string str) + { + BMButtonSearchReq req = new BMButtonSearchReq (); + req.BMButtonSearchRequest = new BMButtonSearchRequestType (); + + return service.BMButtonSearch (req); + } + } + +} + diff --git a/booking/ApiControllers/WorkFlowController.cs b/booking/ApiControllers/WorkFlowController.cs new file mode 100644 index 00000000..ca0cf435 --- /dev/null +++ b/booking/ApiControllers/WorkFlowController.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Web; +using System.Web.Security; +using Yavsc; +using Yavsc.Model.WorkFlow; +using System.Web.Http; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Helpers; +using Yavsc.Model; +using System.Web.Http.Controllers; + +namespace Yavsc.ApiControllers +{ + /// + /// Work flow controller. + /// + public class WorkFlowController : ApiController + { + string adminRoleName="Admin"; + /// + /// The wfmgr. + /// + protected WorkFlowManager wfmgr = null; + /// + /// Initialize the specified controllerContext. + /// + /// Controller context. + protected override void Initialize (HttpControllerContext controllerContext) + { + // TODO move it in a module initialization + base.Initialize (controllerContext); + if (!Roles.RoleExists (adminRoleName)) { + Roles.CreateRole (adminRoleName); + } + wfmgr = new WorkFlowManager (); + } + + /// + /// Creates the estimate. + /// + /// The estimate. + /// Title. + /// Client. + /// Description. + [HttpGet] + [Authorize] + public Estimate CreateEstimate (string title,string client,string description) + { + return wfmgr.CreateEstimate ( + Membership.GetUser().UserName,client,title,description); + } + + + + /// + /// Register the specified userModel. + /// + /// User model. + [HttpGet] + [ValidateAjax] + [Authorize(Roles="Admin,FrontOffice")] + public void Register([FromBody] RegisterModel userModel) + { + if (ModelState.IsValid) { + MembershipCreateStatus mcs; + var user = Membership.CreateUser ( + userModel.UserName, + userModel.Password, + userModel.Email, + null, + null, + userModel.IsApprouved, + out mcs); + switch (mcs) { + case MembershipCreateStatus.DuplicateEmail: + ModelState.AddModelError ("Email", + string.Format(LocalizedText.DuplicateEmail,userModel.UserName) ); + return ; + case MembershipCreateStatus.DuplicateUserName: + ModelState.AddModelError ("Author", + string.Format(LocalizedText.DuplicateUserName,userModel.Email)); + return ; + case MembershipCreateStatus.Success: + if (!userModel.IsApprouved) + + Url.SendActivationMessage (user); + return; + default: + throw new InvalidOperationException (string.Format("Unexpected user creation code :{0}",mcs)); + } + } + } + + /// + /// Drops the writting. + /// + /// Wrid. + [HttpGet] + [Authorize] + public void DropWritting(long wrid) + { + wfmgr.DropWritting (wrid); + } + + /// + /// Drops the estimate. + /// + /// Estid. + [HttpGet] + [Authorize] + public void DropEstimate(long estid) + { + string username = Membership.GetUser().UserName; + Estimate e = wfmgr.GetEstimate (estid); + if (e == null) + throw new InvalidOperationException("not an estimate id:"+estid); + if (username != e.Responsible + && !Roles.IsUserInRole ("FrontOffice")) + throw new UnauthorizedAccessException ("You're not allowed to drop this estimate"); + + wfmgr.DropEstimate (estid); + } + + /// + /// Index this instance. + /// + [HttpGet] + [Authorize] + public object Index() + { + // TODO inform user on its roles and alerts + string username = Membership.GetUser ().UserName; + return new { test=string.Format("Hello {0}!",username) }; + } + + /// + /// Updates the writting. + /// + /// The writting. + /// Wr. + [Authorize] + [AcceptVerbs("POST")] + [ValidateAjax] + public HttpResponseMessage UpdateWritting([FromBody] Writting wr) + { + wfmgr.UpdateWritting (wr); + return Request.CreateResponse (System.Net.HttpStatusCode.OK,"WrittingUpdated:"+wr.Id); + } + + /// + /// Adds the specified imputation to the given estimation by estimation id. + /// + /// Estimation identifier + /// Imputation to add + [AcceptVerbs("POST")] + [Authorize] + [ValidateAjax] + public HttpResponseMessage Write ([FromUri] long estid, [FromBody] Writting wr) { + if (estid <= 0) { + ModelState.AddModelError ("EstimationId", "Spécifier un identifiant d'estimation valide"); + return Request.CreateResponse (System.Net.HttpStatusCode.BadRequest, + ValidateAjaxAttribute.GetErrorModelObject (ModelState)); + } + try { + return Request.CreateResponse(System.Net.HttpStatusCode.OK, + wfmgr.Write(estid, wr.Description, + wr.UnitaryCost, wr.Count, wr.ProductReference)); + } + catch (Exception ex) { + return Request.CreateResponse ( + System.Net.HttpStatusCode.InternalServerError, + "Internal server error:" + ex.Message + "\n" + ex.StackTrace); + + } + } + } + + +} diff --git a/booking/ApiControllers/YavscController.cs b/booking/ApiControllers/YavscController.cs new file mode 100644 index 00000000..76b9b471 --- /dev/null +++ b/booking/ApiControllers/YavscController.cs @@ -0,0 +1,71 @@ +// +// YavscApiController.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 GNU GPL +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Web.Http; +using System.Net.Http; +using System.Web.Profile; + +namespace Yavsc.ApiControllers +{ + /// + /// Yavsc controller. + /// + public class YavscController : ApiController + { + /// + /// Initializes a new instance of the class. + /// + public YavscController () + { + } + /// + /// Auth. + /// + public class Auth { + public string Id { get; set; } + } + /// + /// Allows the cookies. + /// + /// Model. + public void AllowCookies (Auth model) + { + // TODO check Auth when existing + if (model.Id != null) { + ProfileBase pr = ProfileBase.Create (model.Id); + pr.SetPropertyValue ("allowcookies", true); + pr.Save (); + } + } + /// + /// Defaults the response. + /// + /// The response. + protected HttpResponseMessage DefaultResponse() + { + return ModelState.IsValid ? + Request.CreateResponse (System.Net.HttpStatusCode.OK) : + Request.CreateResponse (System.Net.HttpStatusCode.BadRequest, + ValidateAjaxAttribute.GetErrorModelObject (ModelState)); + } + } +} + diff --git a/booking/CatExts/WebCatalogExtensions.cs b/booking/CatExts/WebCatalogExtensions.cs new file mode 100644 index 00000000..24f0b583 --- /dev/null +++ b/booking/CatExts/WebCatalogExtensions.cs @@ -0,0 +1,94 @@ +using System; +using System.Web; +using System.Text; +using System.Web.Mvc; +using System.Web.Routing; +using Yavsc.Model.FrontOffice; +using System.Web.Mvc.Html; +using Yavsc.Model.FrontOffice.Catalog; + +namespace Yavsc.CatExts +{ + /// + /// Web catalog extensions. + /// + public static class WebCatalogExtensions + { + /// + /// Commands the form. + /// + /// The form. + /// Helper. + /// Position. + /// Atc. + public static string CommandForm(this HtmlHelper helper, Product pos,string atc="Add to backet") { + StringBuilder sb = new StringBuilder (); + sb.Append (helper.ValidationSummary ()); + TagBuilder ft = new TagBuilder ("form"); + ft.Attributes.Add("action","/FrontOffice/Command"); + ft.Attributes.Add("method","post"); + ft.Attributes.Add("enctype","multipart/form-data"); + TagBuilder fieldset = new TagBuilder ("fieldset"); + + TagBuilder legend = new TagBuilder ("legend"); + legend.SetInnerText (pos.Name); + TagBuilder para = new TagBuilder ("p"); + + StringBuilder sbfc = new StringBuilder (); + if (pos.CommandForm != null) + foreach (FormElement e in pos.CommandForm.Items) { + sbfc.Append (e.ToHtml ()); + sbfc.Append ("
\n"); + } + sbfc.Append ( + string.Format( + "
\n", + atc + )); + + sbfc.Append (helper.Hidden ("ref", pos.Reference)); + para.InnerHtml = sbfc.ToString (); + fieldset.InnerHtml = legend.ToString ()+"\n"+para.ToString (); + ft.InnerHtml = fieldset.ToString (); + sb.Append (ft.ToString ()); + return sb.ToString (); + } + /// + /// Commands the form. + /// + /// The form. + /// Helper. + /// Position. + /// Atc. + public static string CommandForm(this HtmlHelper helper, Product pos,string atc="Add to backet") { + StringBuilder sb = new StringBuilder (); + sb.Append (helper.ValidationSummary ()); + TagBuilder ft = new TagBuilder ("form"); + ft.Attributes.Add("action","/FrontOffice/Command"); + ft.Attributes.Add("method","post"); + ft.Attributes.Add("enctype","multipart/form-data"); + TagBuilder fieldset = new TagBuilder ("fieldset"); + + TagBuilder legend = new TagBuilder ("legend"); + legend.SetInnerText (pos.Name); + TagBuilder para = new TagBuilder ("p"); + + StringBuilder sbfc = new StringBuilder (); + if (pos.CommandForm != null) + foreach (FormElement e in pos.CommandForm.Items) { + sbfc.Append (e.ToHtml ()); + sbfc.Append ("
\n"); + } + sbfc.Append ( + string.Format( + "
\n",atc)); + sbfc.Append (helper.Hidden ("ref", pos.Reference)); + para.InnerHtml = sbfc.ToString (); + fieldset.InnerHtml = legend.ToString ()+"\n"+para.ToString (); + ft.InnerHtml = fieldset.ToString (); + sb.Append (ft.ToString ()); + return sb.ToString (); + } + } +} + diff --git a/booking/Catalog.xml b/booking/Catalog.xml new file mode 100644 index 00000000..80fd456d --- /dev/null +++ b/booking/Catalog.xml @@ -0,0 +1,86 @@ + + + + + psc + Votre logiciel, efficace, sûr, et sur mesure + + /App_Themes/images/logoDev.png + + + + + + Conseil, conception logicielle + ccl + + + Aide au choix technologiques + tout un art + conseil + + + Conceptualisation de projet + Consolidation d'un niveau logique de projet, spécifications détaillées + concept + + + + + Développement et maintenance + ntic + + + Développement + Votre appli développée en cycles courts + dev + + + Maintenance + Correction des anomalies, réalisation des évolutions, prévision des besoins + main + + + + + + /Commande + + + Entrez un commentaire : + + + comment + Commentaire + + + Choisissez le type d'intervention souhaité: + + + ad + + + + + 0 + + + testarray[] + xxxxxxxxxx + + + testarray[] + + + + + + + + \ No newline at end of file diff --git a/booking/ChangeLog b/booking/ChangeLog new file mode 100644 index 00000000..aeb736a6 --- /dev/null +++ b/booking/ChangeLog @@ -0,0 +1,4 @@ +2015-11-08 Paul Schneider + + * booking.csproj: Removes the system.Core reference + diff --git a/booking/Controllers/AccountController.cs b/booking/Controllers/AccountController.cs new file mode 100644 index 00000000..53ed2338 --- /dev/null +++ b/booking/Controllers/AccountController.cs @@ -0,0 +1,419 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Mail; +using System.Web; +using System.Web.Configuration; +using System.Web.Profile; +using System.Web.Security; +using Yavsc; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Helpers; +using System.Web.Mvc; +using Yavsc.Model.Circles; +using System.Collections.Specialized; +using System.Text; +using System.Net; +using System.Configuration; +using Yavsc.Model; + +namespace Yavsc.Controllers +{ + /// + /// Account controller. + /// + public class AccountController : Controller + { + /// + /// Avatar the specified user. + /// + /// User. + [AcceptVerbs (HttpVerbs.Get)] + public ActionResult Avatar (string id) + { + string avatarLocation = Url.AvatarUrl (id); + WebRequest wr = WebRequest.Create (avatarLocation); + FileContentResult res; + using (WebResponse resp = wr.GetResponse ()) { + using (Stream str = resp.GetResponseStream ()) { + byte[] content = new byte[str.Length]; + str.Read (content, 0, (int)str.Length); + res = File (content, resp.ContentType); + wr.Abort (); + return res; + } + } + } + + + /// + /// Index this instance. + /// + public ActionResult Index () + { + return View (); + } + + // TODO [ValidateAntiForgeryToken] + /// + /// Does login. + /// + /// The login. + /// Model. + /// Return URL. + [HttpPost,ValidateAntiForgeryToken] + public ActionResult Login (LoginModel model, string returnUrl) + { + if (ModelState.IsValid) { + if (Membership.ValidateUser (model.UserName, model.Password)) { + FormsAuthentication.SetAuthCookie (model.UserName, model.RememberMe); + if (returnUrl != null) + return Redirect (returnUrl); + else + return View ("Index"); + } else { + ModelState.AddModelError ("UserName", "The user name or password provided is incorrect."); + } + } + ViewData ["returnUrl"] = returnUrl; + return View (model); + } + + + /// + /// Login the specified returnUrl. + /// + /// Return URL. + [HttpGet] + public ActionResult Login (string returnUrl) + { + ViewData ["returnUrl"] = returnUrl; + return View (); + } + + /// + /// Gets the registration form. + /// + /// The register. + /// Model. + /// Return URL. + public ActionResult GetRegister(RegisterViewModel model, string returnUrl) + { + ViewData ["returnUrl"] = returnUrl; + return View ("Register",model); + } + + /// + /// Register the specified model and returnUrl. + /// + /// Model. + /// Return URL. + [HttpPost] + public ActionResult Register (RegisterViewModel model, string returnUrl) + { + ViewData ["returnUrl"] = returnUrl; + + if (ModelState.IsValid) { + if (model.ConfirmPassword != model.Password) { + ModelState.AddModelError ("ConfirmPassword", "Veuillez confirmer votre mot de passe"); + return View (model); + } + + MembershipCreateStatus mcs; + var user = Membership.CreateUser ( + model.UserName, + model.Password, + model.Email, + null, + null, + false, + out mcs); + switch (mcs) { + case MembershipCreateStatus.DuplicateEmail: + ModelState.AddModelError ("Email", "Cette adresse e-mail correspond " + + "à un compte utilisateur existant"); + return View (model); + case MembershipCreateStatus.DuplicateUserName: + ModelState.AddModelError ("UserName", "Ce nom d'utilisateur est " + + "déjà enregistré"); + return View (model); + case MembershipCreateStatus.Success: + Url.SendActivationMessage (user); + ViewData ["username"] = user.UserName; + ViewData ["email"] = user.Email; + return View ("RegistrationPending"); + default: + ViewData ["Error"] = "Une erreur inattendue s'est produite" + + "a l'enregistrement de votre compte utilisateur" + + string.Format ("({0}).", mcs.ToString ()) + + "Veuillez pardonner la gêne" + + "occasionnée"; + return View (model); + } + + } + return View (model); + } + + /// + /// Changes the password success. + /// + /// The password success. + public ActionResult ChangePasswordSuccess () + { + return View (); + } + + + /// + /// Changes the password. + /// + /// The password. + [HttpGet] + [Authorize] + public ActionResult ChangePassword () + { + return View (); + } + + /// + /// Unregister the specified id and confirmed. + /// + /// Identifier. + /// If set to true confirmed. + [Authorize] + public ActionResult Unregister (string id, bool confirmed = false) + { + ViewData ["UserName"] = id; + if (!confirmed) + return View (); + string logged = this.User.Identity.Name; + if (logged != id) + if (!Roles.IsUserInRole ("Admin")) + throw new Exception ("Unregister another user"); + + Membership.DeleteUser ( + Membership.GetUser ().UserName); + return RedirectToAction ("Index", "Home"); + } + + + /// + /// Changes the password. + /// + /// The password. + /// Model. + [Authorize] + [HttpPost] + public ActionResult ChangePassword (ChangePasswordModel model) + { + if (ModelState.IsValid) { + + // ChangePassword will throw an exception rather + // than return false in certain failure scenarios. + bool changePasswordSucceeded = false; + try { + MembershipUserCollection users = + Membership.FindUsersByName (model.Username); + if (users.Count > 0) { + MembershipUser user = Membership.GetUser (model.Username, true); + + changePasswordSucceeded = user.ChangePassword (model.OldPassword, model.NewPassword); + } else { + changePasswordSucceeded = false; + ModelState.AddModelError ("Username", "The user name not found."); + } + } catch (Exception ex) { + ViewData ["Error"] = ex.ToString (); + } + + if (changePasswordSucceeded) { + return RedirectToAction ("ChangePasswordSuccess"); + } else { + ModelState.AddModelError ("Password", "The current password is incorrect or the new password is invalid."); + } + } + + // If we got this far, something failed, redisplay form + return View (model); + } + + + /// + /// Profile the specified id. + /// + /// Identifier. + [Authorize] + [HttpGet] + public ActionResult Profile (string id) + { + if (id == null) + id = Membership.GetUser ().UserName; + ViewData ["UserName"] = id; + ProfileEdition model = new ProfileEdition (ProfileBase.Create (id)); + model.RememberMe = FormsAuthentication.GetAuthCookie (id, true) == null; + return View (model); + } + + + + /// + /// Profile the specified id, model and AvatarFile. + /// + /// Identifier. + /// Model. + /// Avatar file. + [Authorize] + [HttpPost] + public ActionResult Profile (string id, ProfileEdition model, HttpPostedFileBase AvatarFile) + { + string logdu = User.Identity.Name; + if (string.IsNullOrWhiteSpace (id)) { + if (string.IsNullOrWhiteSpace (model.UserName)) { + model.UserName = logdu; + return View (model); + } else { + id = logdu; + } + } + ViewData ["UserName"] = id; + bool editsTheUserName = model.NewUserName!=null&&(string.Compare(id,model.NewUserName)!=0); + // Checks authorisation + if (logdu!=id) + if (!Roles.IsUserInRole ("Admin")) + if (!Roles.IsUserInRole ("FrontOffice")) + throw new UnauthorizedAccessException ("Your are not authorized to modify this profile"); + // checks availability of a new username + if (editsTheUserName) + if (!UserManager.IsAvailable (model.NewUserName)) + ModelState.AddModelError ("UserName", + string.Format ( + LocalizedText.DuplicateUserName, + model.NewUserName + )); + if (AvatarFile != null) { + // if said valid, move as avatar file + // else invalidate the model + if (AvatarFile.ContentType == "image/png") { + string avdir = Server.MapPath (YavscHelpers.AvatarDir); + var di = new DirectoryInfo (avdir); + if (!di.Exists) + di.Create (); + string avpath = Path.Combine (avdir, id + ".png"); + AvatarFile.SaveAs (avpath); + model.avatar = Url.Content( YavscHelpers.AvatarDir + "/" + id + ".png"); + } else + ModelState.AddModelError ("Avatar", + string.Format ("Image type {0} is not supported (suported formats : {1})", + AvatarFile.ContentType, "image/png")); + } + if (ModelState.IsValid) { + ProfileBase prf = ProfileBase .Create (id); + prf.SetPropertyValue ("Name", model.Name); + prf.SetPropertyValue ("BlogVisible", model.BlogVisible); + prf.SetPropertyValue ("BlogTitle", model.BlogTitle); + if (AvatarFile != null) { + prf.SetPropertyValue ("Avatar", model.avatar); + } else { + var av = prf.GetPropertyValue ("Avatar"); + if (av != null) + model.avatar = av as string; + } + prf.SetPropertyValue ("Address", model.Address); + prf.SetPropertyValue ("CityAndState", model.CityAndState); + prf.SetPropertyValue ("Country", model.Country); + prf.SetPropertyValue ("ZipCode", model.ZipCode); + prf.SetPropertyValue ("WebSite", model.WebSite); + prf.SetPropertyValue ("Name", model.Name); + prf.SetPropertyValue ("Phone", model.Phone); + prf.SetPropertyValue ("Mobile", model.Mobile); + prf.SetPropertyValue ("BankCode", model.BankCode); + prf.SetPropertyValue ("IBAN", model.IBAN); + prf.SetPropertyValue ("BIC", model.BIC); + prf.SetPropertyValue ("WicketCode", model.WicketCode); + prf.SetPropertyValue ("AccountNumber", model.AccountNumber); + prf.SetPropertyValue ("BankedKey", model.BankedKey); + prf.SetPropertyValue ("gcalid", model.GoogleCalendar); + prf.Save (); + + if (editsTheUserName) { + UserManager.ChangeName (id, model.NewUserName); + FormsAuthentication.SetAuthCookie (model.NewUserName, model.RememberMe); + model.UserName = model.NewUserName; + } + YavscHelpers.Notify(ViewData, "Profile enregistré"+((editsTheUserName)?", nom public inclu.":"")); + } + return View (model); + } + /// + /// Circles this instance. + /// + [Authorize] + public ActionResult Circles () + { + string user = Membership.GetUser ().UserName; + ViewData["Circles"] = CircleManager.DefaultProvider.List (user); + return View (); + } + + /// + /// Logout the specified returnUrl. + /// + /// Return URL. + [Authorize] + public ActionResult Logout (string returnUrl) + { + FormsAuthentication.SignOut (); + return Redirect (returnUrl); + } + + /// + /// Losts the password. + /// + /// The password. + /// Model. + public ActionResult ResetPassword(LostPasswordModel model) + { + if (Request.HttpMethod == "POST") { + StringDictionary errors; + MembershipUser user; + YavscHelpers.ValidatePasswordReset (model, out errors, out user); + foreach (string key in errors.Keys) + ModelState.AddModelError (key, errors [key]); + + if (user != null && ModelState.IsValid) + Url.SendActivationMessage (user); + } + return View (model); + } + + /// + /// Validate the specified id and key. + /// + /// Identifier. + /// Key. + [HttpGet] + public ActionResult Validate (string id, string key) + { + MembershipUser u = Membership.GetUser (id, false); + if (u == null) { + YavscHelpers.Notify( ViewData, + string.Format ("Cet utilisateur n'existe pas ({0})", id)); + } else if (u.ProviderUserKey.ToString () == key) { + if (u.IsApproved) { + YavscHelpers.Notify( ViewData, + string.Format ("Votre compte ({0}) est déjà validé.", id)); + } else { + u.IsApproved = true; + Membership.UpdateUser (u); + YavscHelpers.Notify( ViewData, + string.Format ("La création de votre compte ({0}) est validée.", id)); + } + } else + YavscHelpers.Notify( ViewData, "La clé utilisée pour valider ce compte est incorrecte" ); + return View (); + } + + } +} diff --git a/booking/Controllers/AdminController.cs b/booking/Controllers/AdminController.cs new file mode 100644 index 00000000..ab8175c1 --- /dev/null +++ b/booking/Controllers/AdminController.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.Security; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Model.Admin; +using Yavsc.Admin; +using System.IO; +using Yavsc.Model; +using Yavsc.Helpers; + +namespace Yavsc.Controllers +{ + /// + /// Admin controller. + /// Only Admin members should be allowed to use it. + /// + public class AdminController : Controller + { + /// + /// Index this instance. + /// + public ActionResult Index() + { + // FIXME do this in a new installation script. + if (!Roles.RoleExists (_adminRoleName)) { + Roles.CreateRole (_adminRoleName); + YavscHelpers.Notify (ViewData, _adminRoleName + " " + LocalizedText.role_created); + } + return View (); + } + /// + /// Inits the db. + /// In order this action succeds, + /// there must not exist any administrator, + /// nor Admin group. + /// + /// The db. + /// Datac. + /// Do init. + public ActionResult InitDb(DataAccess datac, string doInit) + { + if (doInit=="on") { + if (ModelState.IsValid) { + datac.BackupPrefix = Server.MapPath (datac.BackupPrefix); + DataManager mgr = new DataManager (datac); + TaskOutput tcdb = mgr.CreateDb (); + ViewData ["DbName"] = datac.DbName; + ViewData ["DbUser"] = datac.DbUser; + ViewData ["Host"] = datac.Host; + ViewData ["Port"] = datac.Port; + return View ("Created", tcdb); + } + } + return View (); + } + /// + /// Backups the specified model. + /// + /// Model. + [Authorize(Roles="Admin")] + public ActionResult Backups(DataAccess model) + { + return View (model); + } + /// + /// Creates the backup. + /// + /// The backup. + /// Datac. + [Authorize(Roles="Admin")] + public ActionResult CreateBackup(DataAccess datac) + { + if (datac != null) { + if (ModelState.IsValid) { + if (string.IsNullOrEmpty (datac.Password)) + ModelState.AddModelError ("Password", "Invalid passord"); + datac.BackupPrefix = Server.MapPath (datac.BackupPrefix); + DataManager ex = new DataManager (datac); + Export e = ex.CreateBackup (); + if (e.ExitCode > 0) + ModelState.AddModelError ("Password", "Operation Failed"); + return View ("BackupCreated", e); + } + } else { + datac = new DataAccess (); + } + return View (datac); + } + /// + /// Creates the user backup. + /// + /// The user backup. + /// Datac. + /// Username. + [Authorize(Roles="Admin")] + public ActionResult CreateUserBackup(DataAccess datac,string username) + { + throw new NotImplementedException(); + } + /// + /// Upgrade the specified datac. + /// + /// Datac. + [Authorize(Roles="Admin")] + public ActionResult Upgrade(DataAccess datac) { + throw new NotImplementedException(); + } + /// + /// Restore the specified datac, backupName and dataOnly. + /// + /// Datac. + /// Backup name. + /// If set to true data only. + [Authorize(Roles="Admin")] + public ActionResult Restore(DataAccess datac,string backupName,bool dataOnly=true) + { + ViewData ["BackupName"] = backupName; + if (ModelState.IsValid) { + // TODO BETTER + datac.BackupPrefix = Server.MapPath (datac.BackupPrefix); + DataManager mgr = new DataManager (datac); + ViewData ["BackupName"] = backupName; + ViewData ["DataOnly"] = dataOnly; + + TaskOutput t = mgr.Restore ( + Path.Combine(new FileInfo(datac.BackupPrefix).DirectoryName, + backupName),dataOnly); + return View ("Restored", t); + } + BuildBackupList (datac); + return View (datac); + } + private void BuildBackupList(DataAccess datac) + { + // build ViewData ["Backups"]; + string bckd=Server.MapPath (datac.BackupPrefix); + DirectoryInfo di = new DirectoryInfo (new FileInfo(bckd).DirectoryName); + List bks = new List (); + foreach (FileInfo ti in di.GetFiles("*.tar")) + bks.Add (ti.Name); + ViewData ["Backups"] = bks.ToArray (); + } + /// + /// Removes from role. + /// + /// The from role. + /// Username. + /// Rolename. + /// Return URL. + [Authorize(Roles="Admin")] + public ActionResult RemoveFromRole(string username, string rolename, string returnUrl) + { + Roles.RemoveUserFromRole(username,rolename); + return Redirect(returnUrl); + } + /// + /// Removes the user. + /// + /// The user. + /// Username. + /// Submitbutton. + [Authorize(Roles="Admin")] + public ActionResult RemoveUser (string username, string submitbutton) + { + ViewData ["usertoremove"] = username; + if (submitbutton == "Supprimer") { + Membership.DeleteUser (username); + YavscHelpers.Notify(ViewData, string.Format("utilisateur \"{0}\" supprimé",username)); + ViewData ["usertoremove"] = null; + } + return View (); + } + /// + /// Removes the role. + /// + /// The role. + /// Rolename. + /// Submitbutton. + [Authorize(Roles="Admin")] + public ActionResult RemoveRole (string rolename, string submitbutton) + { + if (submitbutton == "Supprimer") + { + Roles.DeleteRole(rolename); + } + return RedirectToAction("RoleList"); + } + /// + /// Removes the role query. + /// + /// The role query. + /// Rolename. + [Authorize(Roles="Admin")] + public ActionResult RemoveRoleQuery(string rolename) + { + ViewData["roletoremove"] = rolename; + return View (); + } + /// + /// Removes the user query. + /// + /// The user query. + /// Username. + [Authorize(Roles="Admin")] + public ActionResult RemoveUserQuery(string username) + { + ViewData["usertoremove"] = username; + return UserList(); + } + + + //TODO no more than pageSize results per page + /// + /// User list. + /// + /// The list. + [Authorize()] + public ActionResult UserList () + { + MembershipUserCollection c = Membership.GetAllUsers (); + return View (c); + } + [Authorize()] + public ActionResult UsersInRole (string rolename) + { + if (rolename == null) + rolename = "Admin"; + ViewData ["RoleName"] = rolename; + ViewData ["Roles"] = Roles.GetAllRoles (); + ViewData ["UsersInRole"] = Roles.GetUsersInRole (rolename); + return View (); + } + + [Authorize()] + public ActionResult UserRoles (string username) + { + ViewData ["AllRoles"] = Roles.GetAllRoles (); + if (username == null) + username = User.Identity.Name; + ViewData ["UserName"] = username; + ViewData ["UsersRoles"] = Roles.GetRolesForUser (username); + return View (); + } + /// + /// a form to add a role + /// + /// The role. + [Authorize(Roles="Admin"),HttpGet] + public ActionResult AddRole () + { + return View (); + } + + /// + /// Add a new role. + /// + /// The add role. + /// Rolename. + [Authorize(Roles="Admin"),HttpPost] + public ActionResult AddRole (string rolename) + { + Roles.CreateRole(rolename); + YavscHelpers.Notify(ViewData, LocalizedText.role_created+ " : "+rolename); + return View (); + } + + /// + /// Shows the roles list. + /// + /// The list. + [Authorize()] + public ActionResult RoleList () + { + return View (Roles.GetAllRoles ()); + } + + private const string _adminRoleName = "Admin"; + + /// + /// Assing the Admin role to the specified user in model. + /// + /// Model. + [Authorize()] + public ActionResult Admin (NewAdminModel model) + { + // ASSERT (Roles.RoleExists (adminRoleName)) + string [] admins = Roles.GetUsersInRole (_adminRoleName); + string currentUser = Membership.GetUser ().UserName; + List users = new List (); + foreach (MembershipUser u in Membership.GetAllUsers ()) { + var i = new SelectListItem (); + i.Text = string.Format ("{0} <{1}>", u.UserName, u.Email); + i.Value = u.UserName; + users.Add (i); + } + ViewData ["admins"] = admins; + ViewData ["useritems"] = users; + if (ModelState.IsValid) { + Roles.AddUserToRole (model.UserName, _adminRoleName); + YavscHelpers.Notify(ViewData, model.UserName + " "+LocalizedText.was_added_to_the_role+" '" + _adminRoleName + "'"); + } else { + if (admins.Length > 0) { + if (! admins.Contains (Membership.GetUser ().UserName)) { + ModelState.Remove("UserName"); + ModelState.AddModelError("UserName",LocalizedText.younotadmin+"!"); + return View ("Index"); + } + } else { + // No admin, gives the Admin Role to the current user + Roles.AddUserToRole (currentUser, _adminRoleName); + admins = new string[] { currentUser }; + YavscHelpers.Notify(ViewData, string.Format ( + LocalizedText.was_added_to_the_empty_role, + currentUser, _adminRoleName)); + } + } + return View (model); + } + } +} + diff --git a/booking/Controllers/BackOfficeController.cs b/booking/Controllers/BackOfficeController.cs new file mode 100644 index 00000000..882a61a2 --- /dev/null +++ b/booking/Controllers/BackOfficeController.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using Yavsc.Admin; + + +namespace Yavsc.Controllers +{ + /// + /// Back office controller. + /// + public class BackOfficeController : Controller + { + /// + /// Index this instance. + /// + [Authorize(Roles="Admin,Providers")] + public ActionResult Index() + { + return View (); + } + } +} diff --git a/booking/Controllers/BlogsController.cs b/booking/Controllers/BlogsController.cs new file mode 100644 index 00000000..2306ce3c --- /dev/null +++ b/booking/Controllers/BlogsController.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Net.Mime; +using System.Runtime.Serialization.Formatters.Binary; +using System.Web; +using System.Web.Configuration; +using System.Web.Profile; +using System.Web.Security; +using Npgsql.Web.Blog; +using Yavsc; +using Yavsc.Model; +using Yavsc.Model.Blogs; +using Yavsc.ApiControllers; +using Yavsc.Model.RolesAndMembers; +using System.Net; +using System.Web.Mvc; +using Yavsc.Model.Circles; +using Yavsc.Helpers; + +namespace Yavsc.Controllers +{ + /// + /// Blogs controller. + /// + public class BlogsController : Controller + { + private string sitename = + WebConfigurationManager.AppSettings ["Name"]; + + /// + /// Index the specified title, pageIndex and pageSize. + /// + /// Title. + /// Page index. + /// Page size. + public ActionResult Index (string title, int pageIndex = 0, int pageSize = 10) + { + if (title != null) + return Title (title, pageIndex, pageSize); + + return BlogList (pageIndex, pageSize); + } + /// + /// Chooses the media. + /// + /// The media. + /// Identifier. + public ActionResult ChooseMedia(long postid) + { + return View (); + } + + /// + /// Blogs the list. + /// + /// The list. + /// Page index. + /// Page size. + public ActionResult BlogList (int pageIndex = 0, int pageSize = 10) + { + int totalRecords; + var bs = BlogManager.LastPosts (pageIndex, pageSize, out totalRecords); + ViewData ["ResultCount"] = totalRecords; + ViewData ["PageSize"] = pageSize; + ViewData ["PageIndex"] = pageIndex; + var bec = new BlogEntryCollection (bs); + return View ("Index", bec ); + } + + + /// + /// Title the specified title, pageIndex and pageSize. + /// + /// Title. + /// Page index. + /// Page size. + /// + [HttpGet] + public ActionResult Title (string title, int pageIndex = 0, int pageSize = 10) + { + int recordCount; + MembershipUser u = Membership.GetUser (); + string username = u == null ? null : u.UserName; + FindBlogEntryFlags sf = FindBlogEntryFlags.MatchTitle; + BlogEntryCollection c = + BlogManager.FindPost (username, title, sf, pageIndex, pageSize, out recordCount); + var utc = new UTBlogEntryCollection (title); + utc.AddRange (c); + ViewData ["RecordCount"] = recordCount; + ViewData ["PageIndex"] = pageIndex; + ViewData ["PageSize"] = pageSize; + return View ("Title", utc); + } + + /// + /// Users the posts. + /// + /// The posts. + /// User. + /// Page index. + /// Page size. + [HttpGet] + public ActionResult UserPosts (string user, string title=null, int pageIndex = 0, int pageSize = 10) + { + if (title != null) return UserPost (user, title, pageIndex, pageSize); + int recordcount=0; + MembershipUser u = Membership.GetUser (); + FindBlogEntryFlags sf = FindBlogEntryFlags.MatchUserName; + ViewData ["SiteName"] = sitename; + ViewData ["BlogUser"] = user; + string readersName = null; + ViewData ["PageIndex"] = pageIndex; + ViewData ["pageSize"] = pageSize; + // displays invisible items when the logged user is also the author + if (u != null) { + if (u.UserName == user || Roles.IsUserInRole ("Admin")) + sf |= FindBlogEntryFlags.MatchInvisible; + readersName = u.UserName; + if (user == null) + user = u.UserName; + } + // find entries + BlogEntryCollection c = + BlogManager.FindPost (readersName, user, sf, pageIndex, pageSize, out recordcount); + // Get author's meta data + var pr = ProfileBase.Create (user); + if (pr != null) { + Profile bupr = new Profile (pr); + ViewData ["BlogUserProfile"] = bupr; + + // Inform of listing meta data + ViewData ["BlogTitle"] = bupr.BlogTitle; + ViewData ["Avatar"] = bupr.avatar; + } + ViewData ["RecordCount"] = recordcount; + UUBlogEntryCollection uuc = new UUBlogEntryCollection (user, c); + return View ("UserPosts", uuc); + } + + /// + /// Removes the comment. + /// + /// The comment. + /// Cmtid. + [Authorize(Roles="Blogger")] + public ActionResult RemoveComment (long cmtid) + { + long postid = BlogManager.RemoveComment (cmtid); + return GetPost (postid); + } + + /// + /// Gets the post. + /// + /// The post. + /// Postid. + public ActionResult GetPost (long postid) + { + ViewData ["id"] = postid; + BlogEntry e = BlogManager.GetForReading (postid); + UUTBlogEntryCollection c = new UUTBlogEntryCollection (e.Author,e.Title); + c.Add (e); + ViewData ["user"] = c.Author; + ViewData ["title"] = c.Title; + Profile pr = new Profile (ProfileBase.Create (c.Author)); + if (pr == null) + // the owner's profile must exist + // in order to publish its bills + return View ("NotAuthorized"); + ViewData ["BlogUserProfile"] = pr; + ViewData ["Avatar"] = pr.avatar; + ViewData ["BlogTitle"] = pr.BlogTitle; + return View ("UserPost",c); + } + + /// + /// Users the post. + /// Assume that : + /// * bec.Count > O + /// * bec.All(x=>x.Author == bec[0].Author) ; + /// + /// The post. + /// Bec. + private ActionResult UserPost (UUTBlogEntryCollection bec) + { + if (ModelState.IsValid) + if (bec.Count > 0) { + Profile pr = new Profile (ProfileBase.Create (bec.Author)); + if (pr == null) + // the owner's profile must exist + // in order to publish its bills + // This should'nt occur, as long as + // a profile must exist for each one of + // existing user record in data base + // and each post is deleted with user deletion + // a post => an author => a profile + throw new Exception("Unexpected error retreiving author's profile"); + ViewData ["BlogUserProfile"] = pr; + ViewData ["Avatar"] = pr.avatar; + ViewData ["BlogTitle"] = pr.BlogTitle; + MembershipUser u = Membership.GetUser (); + + ViewData ["Author"] = bec.Author; + if (!pr.BlogVisible) { + // only deliver to admins or owner + if (u == null) + return View ("NotAuthorized"); + else { + if (u.UserName != bec.Author) + if (!Roles.IsUserInRole (u.UserName, "Admin")) + return View ("NotAuthorized"); + } + } + if (u == null || (u.UserName != bec.Author) && !Roles.IsUserInRole (u.UserName, "Admin")) { + // Filer on allowed posts + BlogEntryCollection filtered = bec.FilterFor((u == null)?null : u.UserName); + UUTBlogEntryCollection nbec = new UUTBlogEntryCollection (bec.Author, bec.Title); + nbec.AddRange (filtered); + View ("UserPost",nbec); + } + } + return View ("UserPost",bec); + } + + /// + /// Users the post. + /// + /// The post. + /// User. + /// Title. + /// Page index. + /// Page size. + public ActionResult UserPost (string user, string title, int pageIndex = 0, int pageSize = 10) + { + ViewData ["user"] = user; + ViewData ["title"] = title; + ViewData ["PageIndex"] = pageIndex; + ViewData ["pageSize"] = pageSize; + var pb = ProfileBase.Create (user); + if (pb == null) + // the owner's profile must exist + // in order to publish its bills + return View ("NotAuthorized"); + Profile pr = new Profile (pb); + ViewData ["BlogUserProfile"] = pr; + ViewData ["Avatar"] = pr.avatar; + ViewData ["BlogTitle"] = pr.BlogTitle; + UUTBlogEntryCollection c = new UUTBlogEntryCollection (user, title); + c.AddRange ( BlogManager.FilterOnReadAccess (BlogManager.GetPost (user, title))); + return View ("UserPost",c); + } + + /// + /// Post the specified title. + /// + /// Title. + [Authorize(Roles="Blogger")] + public ActionResult Post (string title) + { + string un = Membership.GetUser ().UserName; + if (String.IsNullOrEmpty (title)) + title = ""; + ViewData ["SiteName"] = sitename; + ViewData ["Author"] = un; + ViewData ["AllowedCircles"] = CircleManager.DefaultProvider.List (un) + .Select (x => new SelectListItem { + Value = x.Id.ToString(), + Text = x.Title + }); + + return View ("Edit", new BlogEntry { Title = title, Author = un }); + } + + /// + /// Validates the edit. + /// + /// The edit. + /// Model. + [Authorize(Roles="Blogger")] + public ActionResult ValidateEdit (BlogEntry model) + { + ViewData ["SiteName"] = sitename; + ViewData ["Author"] = Membership.GetUser ().UserName; + if (ModelState.IsValid) { + if (model.Id != 0) { + // ensures rights to update + BlogManager.GetForEditing (model.Id, true); + BlogManager.UpdatePost (model.Id, model.Title, model.Content, model.Visible, model.AllowedCircles); + + } + else + model.Id = BlogManager.Post (model.Author, model.Title, model.Content, model.Visible, model.AllowedCircles); + if (model.Photo != null) + BlogManager.UpdatePostPhoto (model.Id, model.Photo); + return RedirectToAction ("Title", new { title = model.Title }); + } + ViewData ["AllowedCircles"] = + CircleManager.DefaultProvider.List ( + Membership.GetUser ().UserName).Select (x => new SelectListItem { + Value = x.Id.ToString(), + Text = x.Title, + Selected = model.AllowedCircles.Contains (x.Id) + }); + return View ("Edit", model); + } + + /// + /// Edit the specified bill + /// + /// Identifier. + [Authorize(Roles="Blogger")] + public ActionResult Edit (long postid) + { + + BlogEntry e = BlogManager.GetForEditing (postid); + string user = Membership.GetUser ().UserName; + Profile pr = new Profile (ProfileBase.Create(e.Author)); + ViewData ["BlogTitle"] = pr.BlogTitle; + ViewData ["LOGIN"] = user; + ViewData ["Id"] = postid; + // Populates the circles combo items + + if (e.AllowedCircles == null) + e.AllowedCircles = new long[0]; + + ViewData ["AllowedCircles"] = + CircleManager.DefaultProvider.List ( + Membership.GetUser ().UserName).Select (x => new SelectListItem { + Value = x.Id.ToString(), + Text = x.Title, + Selected = e.AllowedCircles.Contains (x.Id) + }); + return View (e); + } + + /// + /// Comment the specified model. + /// + /// Model. + [Authorize] + public ActionResult Comment (Comment model) + { + string username = Membership.GetUser ().UserName; + ViewData ["SiteName"] = sitename; + if (ModelState.IsValid) { + BlogManager.Comment (username, model.PostId, model.CommentText, model.Visible); + return GetPost (model.PostId); + } + return GetPost (model.PostId); + } + + /// + /// Remove the specified blog entry, by its author and title, + /// using returnUrl as the URL to return to, + /// and confirm as a proof you really know what you do. + /// + /// Title. + /// User. + /// Return URL. + /// If set to true confirm. + [Authorize(Roles="Blogger")] + public ActionResult RemoveTitle (string user, string title, string returnUrl, bool confirm = false) + { + if (returnUrl == null) + if (Request.UrlReferrer != null) + returnUrl = Request.UrlReferrer.AbsoluteUri; + ViewData ["returnUrl"] = returnUrl; + ViewData ["Author"] = user; + ViewData ["Title"] = title; + + if (Membership.GetUser ().UserName != user) + if (!Roles.IsUserInRole("Admin")) + throw new AuthorizationDenied (user); + if (!confirm) + return View ("RemoveTitle"); + BlogManager.RemoveTitle (user, title); + if (returnUrl == null) + RedirectToAction ("Index", new { user = user }); + return Redirect (returnUrl); + } + + /// + /// Removes the post. + /// + /// The post. + /// Identifier. + /// Return URL. + /// If set to true confirm. + [Authorize(Roles="Blogger")] + public ActionResult RemovePost (long postid, string returnUrl, bool confirm = false) + { + // ensures the access control + BlogEntry e = BlogManager.GetForEditing (postid); + if (e == null) + return new HttpNotFoundResult ("post id "+postid.ToString()); + ViewData ["id"] = postid; + ViewData ["returnUrl"] = string.IsNullOrWhiteSpace(returnUrl)? + Request.UrlReferrer.AbsoluteUri.ToString(): returnUrl; + // TODO: cleaner way to disallow deletion + if (!confirm) + return View ("RemovePost",e); + BlogManager.RemovePost (postid); + if (string.IsNullOrWhiteSpace(returnUrl)) + return RedirectToAction ("Index"); + return Redirect (returnUrl); + } + } +} + diff --git a/booking/Controllers/FileSystemController.cs b/booking/Controllers/FileSystemController.cs new file mode 100644 index 00000000..2dac4b11 --- /dev/null +++ b/booking/Controllers/FileSystemController.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.IO; +using System.Web.Security; +using System.Text.RegularExpressions; +using Yavsc.Model.FileSystem; + +namespace Yavsc.Controllers +{ + /// + /// File system controller. + /// + + public class FileSystemController : Controller + { + + /// + /// Initialize the specified requestContext. + /// + /// Request context. + [Authorize] + protected override void Initialize (System.Web.Routing.RequestContext requestContext) + { + base.Initialize (requestContext); + } + + /// + /// Index this instance. + /// + [Authorize] + public ActionResult Index (string user, string filename) + { + WebFileSystemManager fsmgr = new WebFileSystemManager (); + var files = fsmgr.GetFiles (user,filename); + return View (files); + } + + /// + /// Post the specified id. + /// + /// Identifier. + public ActionResult Post (string id) + { + return View (); + } + + /// + /// Details the specified user and filename. + /// + /// User. + /// Filename. + public ActionResult Details (string user, string filename) + { + WebFileSystemManager fsmgr = new WebFileSystemManager (); + FileInfo fi = fsmgr.FileInfo (filename); + + ViewData ["filename"] = filename; + // TODO : ensure that we use the default port for + // the used sheme + ViewData ["url"] = Url.Content("~/users/"+user+"/"+filename); + return View (fi); + } + } +} \ No newline at end of file diff --git a/booking/Controllers/FrontOfficeController.cs b/booking/Controllers/FrontOfficeController.cs new file mode 100644 index 00000000..1886e892 --- /dev/null +++ b/booking/Controllers/FrontOfficeController.cs @@ -0,0 +1,263 @@ +using System; +using Yavsc; +using System.Web.Mvc; +using System.Web; +using System.Text.RegularExpressions; +using System.IO; +using Yavsc.Controllers; +using System.Collections.Generic; +using Yavsc.Model; +using Yavsc.Model.WorkFlow; +using System.Web.Security; +using System.Threading; +using Yavsc.Model.FrontOffice; +using Yavsc.Model.FileSystem; +using Yavsc.Model.Calendar; +using System.Configuration; +using Yavsc.Helpers; +using Yavsc.Model.FrontOffice.Catalog; + +namespace Yavsc.Controllers +{ + /// + /// Front office controller. + /// Access granted to all + /// + public class FrontOfficeController : Controller + { + /// + /// The wfmgr. + /// + protected WorkFlowManager wfmgr = null; + + /// + /// Initialize the specified requestContext. + /// + /// Request context. + protected override void Initialize (System.Web.Routing.RequestContext requestContext) + { + base.Initialize (requestContext); + wfmgr = new WorkFlowManager (); + } + + /// + /// Index this instance. + /// + public ActionResult Index () + { + return View (); + } + /// + /// Pub the Event + /// + /// The pub. + /// Model. + public ActionResult EventPub (EventPub model) + { + return View (model); + } + /// + /// Estimates this instance. + /// + [Authorize] + public ActionResult Estimates (string client) + { + var u = Membership.GetUser (); + if (u == null) // There was no redirection to any login page + throw new ConfigurationErrorsException ("no redirection to any login page"); + + string username = u.UserName; + Estimate [] estims = wfmgr.GetUserEstimates (username); + ViewData ["UserName"] = username; + ViewData ["ResponsibleCount"] = + Array.FindAll ( + estims, + x => x.Responsible == username).Length; + ViewData ["ClientCount"] = + Array.FindAll ( + estims, + x => x.Client == username).Length; + return View (estims); + } + + + /// + /// Estimate the specified id. + /// + /// Identifier. + public ActionResult Get (long id) + { + Estimate f = wfmgr.GetEstimate (id); + if (f == null) { + ModelState.AddModelError ("Id", "Wrong Id"); + return View (new Estimate () { Id=id } ); + } + return View (f); + } + + /// + /// Estimate the specified model and submit. + /// + /// Model. + /// Submit. + [Authorize] + public ActionResult Estimate (Estimate model, string submit) + { + string username = Membership.GetUser().UserName; + // Obsolete, set in master page + ViewData ["WebApiBase"] = Url.Content(Yavsc.WebApiConfig.UrlPrefixRelative); + ViewData ["WABASEWF"] = ViewData ["WebApiBase"] + "/WorkFlow"; + if (submit == null) { + if (model.Id > 0) { + Estimate f = wfmgr.GetEstimate (model.Id); + if (f == null) { + ModelState.AddModelError ("Id", "Wrong Id"); + return View (model); + } + model = f; + ModelState.Clear (); + if (username != model.Responsible + && username != model.Client + && !Roles.IsUserInRole ("FrontOffice")) + throw new UnauthorizedAccessException ("You're not allowed to view this estimate"); + } else if (model.Id == 0) { + if (string.IsNullOrWhiteSpace(model.Responsible)) + model.Responsible = username; + } + } else { + + if (model.Id == 0) // if (submit == "Create") + if (string.IsNullOrWhiteSpace (model.Responsible)) + model.Responsible = username; + if (username != model.Responsible + && !Roles.IsUserInRole ("FrontOffice")) + throw new UnauthorizedAccessException ("You're not allowed to modify this estimate"); + + if (ModelState.IsValid) { + if (model.Id == 0) + model = wfmgr.CreateEstimate ( + username, + model.Client, model.Title, model.Description); + else { + wfmgr.UpdateEstimate (model); + model = wfmgr.GetEstimate (model.Id); + } + } + } + return View (model); + } + + /// + /// Catalog this instance. + /// + [AcceptVerbs ("GET")] + public ActionResult Catalog () + { + return View ( + CatalogManager.GetCatalog () + ); + } + + /// + /// Catalog this instance. + /// + [AcceptVerbs ("GET")] + public ActionResult Brand (string id) + { + Catalog c = CatalogManager.GetCatalog (); + ViewData ["BrandName"] = id; + return View (c.GetBrand (id)); + } + + /// + /// get the product category + /// + /// The category object. + /// Brand id. + /// Product category Id. + [AcceptVerbs ("GET")] + public ActionResult ProductCategory (string brandid, string pcid) + { + ViewData ["BrandId"] = brandid; + ViewData ["ProductCategoryId"] = pcid; + + var cat = CatalogManager.GetCatalog (); + if (cat == null) + throw new Exception ("No catalog"); + var brand = cat.GetBrand (brandid); + if (brand == null) + throw new Exception ("Not a brand id: "+brandid); + var pcat = brand.GetProductCategory (pcid); + if (pcat == null) + throw new Exception ("Not a product category id in this brand: " + pcid); + return View (pcat); + } + + /// + /// Product the specified id, pc and pref. + /// + /// Identifier. + /// Pc. + /// Preference. + [AcceptVerbs ("GET")] + public ActionResult Product (string id, string pc, string pref) + { + Product p = null; + ViewData ["BrandName"] = id; + ViewData ["ProdCatRef"] = pc; + ViewData ["ProdRef"] = pref; + Catalog cat = CatalogManager.GetCatalog (); + if (cat == null) { + YavscHelpers.Notify(ViewData, "Catalog introuvable"); + ViewData ["RefType"] = "Catalog"; + return View ("ReferenceNotFound"); + } + Brand b = cat.GetBrand (id); + if (b == null) { + ViewData ["RefType"] = "Brand"; + return View ("ReferenceNotFound"); + } + ProductCategory pcat = b.GetProductCategory (pc); + if (pcat == null) { + ViewData ["RefType"] = "ProductCategory"; + return View ("ReferenceNotFound"); + } + ViewData ["ProdCatName"] = pcat.Name; + p = pcat.GetProduct (pref); + if (p.CommandForm == null) + p.CommandForm = b.DefaultForm; + + return View ((p is Service) ? "Service" : "Product", p); + } + + /// + /// Basket this instance. + /// + [Authorize] + public ActionResult Basket () + { + return View (wfmgr.GetCommands (Membership.GetUser ().UserName)); + } + + /// + /// Command the specified collection. + /// + /// Collection. + [HttpPost] + [Authorize] + public ActionResult Command (FormCollection collection) + { + try { + // Add specified product command to the basket, + // saves it in db + new Command(collection,HttpContext.Request.Files); + YavscHelpers.Notify(ViewData, LocalizedText.Item_added_to_basket); + return View (collection); + } catch (Exception e) { + YavscHelpers.Notify(ViewData,"Exception:" + e.Message); + return View (collection); + } + } + + } +} diff --git a/booking/Controllers/GoogleController.cs b/booking/Controllers/GoogleController.cs new file mode 100644 index 00000000..332016d0 --- /dev/null +++ b/booking/Controllers/GoogleController.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using System.Web.Profile; +using System.Web.Security; +using Newtonsoft.Json; +using Yavsc.Model; +using Yavsc.Model.Google; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Helpers.Google; +using Yavsc.Model.Calendar; +using Yavsc.Helpers; + +namespace Yavsc.Controllers +{ + /// + /// Google controller. + /// + public class GoogleController : Controller + { + /// + /// Index this instance. + /// + public ActionResult Index() + { + return View (); + } + + private string SetSessionSate () + { + string state = "security_token"; + Random rand = new Random (); + for (int l = 0; l < 32; l++) { + int r = rand.Next (62); + char c; + if (r < 10) { + c = (char)('0' + r); + } else if (r < 36) { + r -= 10; + c = (char) ('a' + r); + } else { + r -= 36; + c = (char) ('A' + r); + } + state += c; + } + Session ["state"] = state; + return state; + } + + private string AuthGRU { + get { + return Request.Url.Scheme + "://" + + Request.Url.Authority + "/Google/Auth"; + } + } + + private string CalendarGRU { + get { + return Request.Url.Scheme + "://" + + Request.Url.Authority + "/Google/CalAuth"; + } + } + /// + /// Login the specified returnUrl. + /// + /// Return URL. + public void Login (string returnUrl) + { + if (string.IsNullOrWhiteSpace (returnUrl)) + returnUrl = "/"; + Session ["returnUrl"] = returnUrl; + OAuth2 oa = new OAuth2 (AuthGRU,clientId,clientSecret); + oa.Login (Response, SetSessionSate ()); + } + private string clientId = ConfigurationManager.AppSettings ["GOOGLE_CLIENT_ID"]; + private string clientSecret = ConfigurationManager.AppSettings ["GOOGLE_CLIENT_SECRET"]; + private string clientApiKey = ConfigurationManager.AppSettings ["GOOGLE_API_KEY"]; + /// + /// Gets the cal auth. + /// + /// Return URL. + public void GetCalAuth (string returnUrl) + { + if (string.IsNullOrWhiteSpace (returnUrl)) + returnUrl = "/"; + Session ["returnUrl"] = returnUrl; + OAuth2 oa = new OAuth2 (CalendarGRU,clientId,clientSecret); + oa.GetCalendarScope (Response, SetSessionSate ()); + } + + /// + /// Called after the Google authorizations screen, + /// we assume that Session contains a redirectUrl entry + /// + /// The auth. + [HttpGet] + [Authorize] + public ActionResult CalAuth () + { + string msg; + OAuth2 oa = new OAuth2 (CalendarGRU,clientId,clientSecret); + + AuthToken gat = oa.GetToken (Request, (string) Session ["state"], out msg); + if (gat == null) { + YavscHelpers.Notify(ViewData, msg); + return View ("Auth"); + } + SaveToken (HttpContext.Profile,gat); + HttpContext.Profile.SetPropertyValue ("gcalapi", true); + string returnUrl = (string) Session ["returnUrl"]; + Session ["returnUrl"] = null; + return Redirect (returnUrl); + } + + /// + /// Saves the token. + /// This calls the Profile.Save() method. + /// It should be called immediatly after getting the token from Google, in + /// order to save a descent value as expiration date. + /// + /// Gat. + private void SaveToken (ProfileBase pr, AuthToken gat) + { + pr.SetPropertyValue ("gtoken", gat.access_token); + if (gat.refresh_token != null) + pr.SetPropertyValue ("grefreshtoken", gat.refresh_token); + pr.SetPropertyValue ("gtokentype", gat.token_type); + pr.SetPropertyValue ("gtokenexpir", DateTime.Now.AddSeconds (gat.expires_in)); + pr.Save (); + } + + /// + /// Auth this instance. + /// + [HttpGet] + public ActionResult Auth () + { + string msg; + OAuth2 oa = new OAuth2 (AuthGRU,clientId,clientSecret); + AuthToken gat = oa.GetToken (Request, (string)Session ["state"], out msg); + if (gat == null) { + YavscHelpers.Notify(ViewData, msg); + return View (); + } + string returnUrl = (string)Session ["returnUrl"]; + SignIn regmod = new SignIn (); + + People me = PeopleApi.GetMe (gat); + // TODO use me.id to retreive an existing user + string accEmail = me.emails.Where (x => x.type == "account").First ().value; + MembershipUserCollection mbrs = Membership.FindUsersByEmail (accEmail); + if (mbrs.Count == 1) { + // TODO check the google id + // just set this user as logged on + foreach (MembershipUser u in mbrs) { + string username = u.UserName; + FormsAuthentication.SetAuthCookie (username, true); + /* var upr = ProfileBase.Create (username); + SaveToken (upr,gat); */ + } + Session ["returnUrl"] = null; + return Redirect (returnUrl); + } + // else create the account + regmod.Email = accEmail; + regmod.UserName = me.displayName; + Session ["me"] = me; + Session ["GoogleAuthToken"] = gat; + return Auth (regmod); + } + + /// + /// Creates an account using the Google authentification. + /// + /// Regmod. + [HttpPost] + public ActionResult Auth (SignIn regmod) + { + if (ModelState.IsValid) { + if (Membership.GetUser (regmod.UserName) != null) { + ModelState.AddModelError ("UserName", "This user name already is in use"); + return View (); + } + string returnUrl = (string) Session ["returnUrl"]; + AuthToken gat = (AuthToken) Session ["GoogleAuthToken"]; + People me = (People)Session ["me"]; + if (gat == null || me == null) + throw new InvalidDataException (); + + Random rand = new Random (); + string passwd = rand.Next (100000).ToString () + rand.Next (100000).ToString (); + + MembershipCreateStatus mcs; + Membership.CreateUser ( + regmod.UserName, + passwd, + regmod.Email, + null, + null, + true, + out mcs); + switch (mcs) { + case MembershipCreateStatus.DuplicateEmail: + ModelState.AddModelError ("Email", "Cette adresse e-mail correspond " + + "à un compte utilisateur existant"); + return View (regmod); + case MembershipCreateStatus.DuplicateUserName: + ModelState.AddModelError ("UserName", "Ce nom d'utilisateur est " + + "déjà enregistré"); + return View (regmod); + case MembershipCreateStatus.Success: + Membership.ValidateUser (regmod.UserName, passwd); + FormsAuthentication.SetAuthCookie (regmod.UserName, true); + + HttpContext.Profile.Initialize (regmod.UserName, true); + HttpContext.Profile.SetPropertyValue ("Name", me.displayName); + // TODO use image + if (me.image != null) { + HttpContext.Profile.SetPropertyValue ("Avatar", me.image.url); + } + if (me.placesLived != null) { + People.Place pplace = me.placesLived.Where (x => x.primary).First (); + if (pplace != null) + HttpContext.Profile.SetPropertyValue ("CityAndState", pplace.value); + } + if (me.url != null) + HttpContext.Profile.SetPropertyValue ("WebSite", me.url); + // Will be done in SaveToken: HttpContext.Profile.Save (); + SaveToken (HttpContext.Profile, gat); + Session ["returnUrl"] = null; + return Redirect (returnUrl); + } + ViewData ["returnUrl"] = returnUrl; + } + return View (regmod); + } + + + [Authorize] + [HttpGet] + ActionResult PushPos () + { + return View (); + } + + /// + /// Chooses the calendar. + /// + /// The calendar. + /// Return URL. + [Authorize] + [HttpGet] + public ActionResult ChooseCalendar (string returnUrl) + { + if (returnUrl != null) { + Session ["chooseCalReturnUrl"] = returnUrl; + return RedirectToAction ("GetCalAuth", + new { + returnUrl = Url.Action ("ChooseCalendar") // "ChooseCalendar?returnUrl="+HttpUtility.UrlEncode(returnUrl) + }); + } + string cred = OAuth2.GetFreshGoogleCredential (HttpContext.Profile); + CalendarApi c = new CalendarApi (clientApiKey); + CalendarList cl = c.GetCalendars (cred); + ViewData ["returnUrl"] = Session ["chooseCalReturnUrl"]; + return View (cl); + } + + /// + /// Sets the calendar. + /// + /// The calendar. + /// Calchoice. + /// return Url. + [HttpPost] + [Authorize] + public ActionResult SetCalendar (string calchoice,string returnUrl) + { + HttpContext.Profile.SetPropertyValue ("gcalid", calchoice); + HttpContext.Profile.Save (); + + if (returnUrl != null) { + return Redirect (returnUrl); + } + return Redirect ("/"); + } + + /// + /// Dates the query. + /// + /// The query. + [Authorize,HttpGet] + public ActionResult Book () + { + var model = new BookQuery (); + model.StartDate = DateTime.Now; + model.EndDate = model.StartDate.AddDays(2); + model.StartHour = DateTime.Now.ToString("HH:mm"); + model.EndHour = DateTime.Now.AddHours(1).ToString("HH:mm"); + return View (model); + } + + /// + /// Dates the query. + /// + /// The query. + /// Model. + [Authorize,HttpPost] + public ActionResult Book (BookQuery model) + { + if (ModelState.IsValid) { + DateTime mindate = DateTime.Now; + if (model.StartDate.Date < mindate.Date){ + ModelState.AddModelError ("StartDate", LocalizedText.FillInAFutureDate); + } + if (model.EndDate < model.StartDate) + ModelState.AddModelError ("EndDate", LocalizedText.StartDateAfterEndDate); + + var muc = Membership.FindUsersByName (model.Person); + if (muc.Count == 0) { + ModelState.AddModelError ("Person", LocalizedText.Non_existent_user); + } + if (!Roles.IsUserInRole (model.Role)) { + ModelState.AddModelError ("Role", LocalizedText.UserNotInThisRole); + } + ProfileBase upr = ProfileBase.Create (model.Person); + var gcalid = upr.GetPropertyValue ("gcalid"); + if (gcalid is DBNull) + ModelState.AddModelError ("Person", LocalizedText.No_calendar_for_this_user); + if (ModelState.IsValid) { + string calid = (string) gcalid; + DateTime maxdate = model.EndDate; + CalendarApi c = new CalendarApi (clientApiKey); + CalendarEventList events; + try { + string creds = OAuth2.GetFreshGoogleCredential (upr); + events = c.GetCalendar (calid, mindate, maxdate, creds); + YavscHelpers.Notify (ViewData, "Google calendar API call success"); + } catch (WebException ex) { + string response; + using (var stream = ex.Response.GetResponseStream()) + using (var reader = new StreamReader(stream)) + { + response = reader.ReadToEnd(); + } + YavscHelpers.Notify (ViewData, + string.Format( + "Google calendar API exception {0} : {1}
{2}
", + ex.Status.ToString(), + ex.Message, + response)); + } + } + } + return View (model); + } + } +} diff --git a/booking/Controllers/HomeController.cs b/booking/Controllers/HomeController.cs new file mode 100644 index 00000000..80e58123 --- /dev/null +++ b/booking/Controllers/HomeController.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Mail; +using System.Web; +using System.Web.Configuration; +using System.Reflection; +using System.Resources; +using Yavsc.Model; +using Npgsql.Web; +using Npgsql.Web.Blog; +using Yavsc.Helpers; +using Yavsc; +using System.Web.Mvc; +using Yavsc.Model.Blogs; +using System.Web.Security; +using System.Web.Profile; + +namespace Yavsc.Controllers +{ + /// + /// Home controller. + /// + public class HomeController : Controller + { + + /// + /// Lists the referenced assemblies. + /// + /// The info. + public ActionResult AssemblyInfo() + { + Assembly[] aslist = { + GetType ().Assembly, + typeof(ITCPNpgsqlProvider).Assembly, + typeof(NpgsqlMembershipProvider).Assembly, + typeof(NpgsqlContentProvider).Assembly, + typeof(NpgsqlBlogProvider).Assembly + }; + + List asnlist = new List (); + foreach (Assembly asse in aslist) { + foreach (AssemblyName an in asse.GetReferencedAssemblies ()) { + if (asnlist.All(x=> string.Compare(x.Name,an.Name)!=0)) + asnlist.Add (an); + } + } + asnlist.Sort (delegate(AssemblyName x, AssemblyName y) { + return string.Compare (x.Name, y.Name); + }); + return View (asnlist.ToArray()) ; + } + + private static string owneremail = null; + /// + /// Gets or sets the owner email. + /// + /// The owner email. + public static string OwnerEmail { + get { + if (owneremail == null) + owneremail = WebConfigurationManager.AppSettings.Get ("OwnerEMail"); + return owneremail; + } + set { + owneremail = value; + } + } + /// + /// Index this instance. + /// + public ActionResult Index () + { + if (Session.IsNewSession) { + string uid = (!Request.IsAuthenticated) ? Request.AnonymousID : User.Identity.Name; + ProfileBase pr = + ProfileBase.Create (uid); + bool ac = (bool) pr.GetPropertyValue ("allowcookies"); + if (!ac) + YavscHelpers.Notify (ViewData, LocalizedText.ThisSiteUsesCookies, + "function(){Yavsc.ajax(\"/Yavsc/AllowCookies\", { id:'"+uid+"' });}", + LocalizedText.I_understood); + } + + foreach (string tagname in new string[] {"Accueil","Événements","Mentions légales"}) + { + TagInfo ti = BlogManager.GetTagInfo (tagname); + // TODO specialyze BlogEntry creating a PhotoEntry + ViewData [tagname] = ti; + } + return View (); + } + /// + /// Credits this instance. + /// + public ActionResult Credits () + { + return View (); + } + + /// + /// Contact the specified email, reason and body. + /// + /// Email. + /// Reason. + /// Body. + public ActionResult Contact (string email, string reason, string body) + { + if (email==null) + ModelState.AddModelError("email","Enter your email"); + + if (reason==null) + ModelState.AddModelError("reason","Please, fill in a reason"); + + if (body==null) + ModelState.AddModelError("body","Please, fill in a body"); + if (!ModelState.IsValid) + return View (); + + // requires valid owner and admin email? + if (OwnerEmail == null) + throw new Exception ("No site owner!"); + + using (System.Net.Mail.MailMessage msg = new MailMessage(email,OwnerEmail,"[Contact] "+reason,body)) + { + msg.CC.Add(new MailAddress(YavscHelpers.Admail)); + using (System.Net.Mail.SmtpClient sc = new SmtpClient()) + { + sc.Send (msg); + YavscHelpers.Notify(ViewData, LocalizedText.Message_sent); + return View (new { email=email, reason="", body="" }); + } + } + } + + } +} diff --git a/booking/Controllers/ModuleController.cs b/booking/Controllers/ModuleController.cs new file mode 100644 index 00000000..4aae72c1 --- /dev/null +++ b/booking/Controllers/ModuleController.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using Yavsc.Model; +using System.Configuration; + +namespace Yavsc.Controllers +{ + /// + /// Module controller. + /// + public class ModuleController : Controller + { + /// + /// Initialize the specified requestContext. + /// + /// Request context. + protected override void Initialize (System.Web.Routing.RequestContext requestContext) + { + base.Initialize (requestContext); + ConfigurationManager.GetSection ("ymodules"); + + } + + // List modules = new List (); + /// + /// Index this instance. + /// + public ActionResult Index() + { + return View (); + } + } +} diff --git a/booking/Formatters/ErrorHtmlFormatter.cs b/booking/Formatters/ErrorHtmlFormatter.cs new file mode 100644 index 00000000..9e1fbe8c --- /dev/null +++ b/booking/Formatters/ErrorHtmlFormatter.cs @@ -0,0 +1,95 @@ +// +// ErrorHtmlFormatter.cs +// +// Author: +// paul <${AuthorEmail}> +// +// Copyright (c) 2015 paul +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Web.Mvc; +using System.Net; +using MarkdownDeep; +using Yavsc.Helpers; +using Yavsc.Model.Blogs; + +namespace Yavsc.Formatters +{ + /// + /// Formats a given error message to respond + /// in case of error, and in an html format + /// + public class ErrorHtmlFormatter:SimpleFormatter { + + /// + /// Gets or sets the title. + /// + /// The title. + public string Title { get ; set; } + /// + /// Gets or sets the error code. + /// + /// The error code. + public HttpStatusCode ErrorCode { get ; set; } + + string doctype=""; + /// + /// Initializes a new instance of the class. + /// + /// Error code. + /// Title. + public ErrorHtmlFormatter + (HttpStatusCode errorCode, string title):base("text/html") + { + ErrorCode = errorCode; + Title = title; + + } + + public override void WriteToStream (Type type, object value, Stream stream, HttpContent content) + { + // TODO create a type containing T4 parameters, and generate from them + using (var writer = new StreamWriter(stream)) + { + string message = value as string; + TagBuilder doc = new TagBuilder ("html"); + TagBuilder body = new TagBuilder ("body"); + TagBuilder h1 = new TagBuilder ("h1"); + TagBuilder p = new TagBuilder ("p"); + TagBuilder head = new TagBuilder ("head"); + head.InnerHtml = "" + + "" + + ""; + p.InnerHtml = MarkdownHelper.Markdown(message).ToHtmlString(); + h1.InnerHtml = MvcHtmlString.Create (Title).ToHtmlString(); + body.InnerHtml = h1.ToString()+p.ToString (); + doc.InnerHtml = head.ToString()+"\n"+body.ToString (); + writer.WriteLine (doctype); + writer.Write (doc.ToString()); + } + + } + + } +} diff --git a/booking/Formatters/EstimToPdfFormatter.MSAN.cs b/booking/Formatters/EstimToPdfFormatter.MSAN.cs new file mode 100644 index 00000000..87339dfc --- /dev/null +++ b/booking/Formatters/EstimToPdfFormatter.MSAN.cs @@ -0,0 +1,138 @@ +// +// EstimToPdfFormatter.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +#if MicrosoftAspNetMvc + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Web; +using System.Web.Profile; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Model.WorkFlow; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; + +namespace Yavsc.Formatters +{ + /// + /// Estim to pdf formatter. + /// + public class EstimToPdfFormatter: BufferedMediaTypeFormatter + { + /// + /// Initializes a new instance of the class. + /// + public EstimToPdfFormatter () + { + SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/pdf")); + } + /// + /// Determines whether this instance can read type the specified type. + /// + /// true if this instance can read type the specified type; otherwise, false. + /// Type. + public override bool CanReadType(Type type) + { + return false; + } + /// + /// Determines whether this instance can write type the specified type. + /// + /// true if this instance can write type the specified type; otherwise, false. + /// Type. + public override bool CanWriteType(System.Type type) + { + if (type == typeof(Estimate)) + { + return true; + } + else + { + Type enumerableType = typeof(IEnumerable); + return enumerableType.IsAssignableFrom(type); + } + } + public override void WriteToStream (Type type, object value, Stream writeStream, System.Net.Http.HttpContent content) + { + + // TODO create a type containing generation parameters, including a template path, and generate from them + + Yavsc.templates.Estim tmpe = new Yavsc.templates.Estim(); + tmpe.Session = new Dictionary(); + Estimate e = value as Estimate; + tmpe.Session.Add ("estim", e); + + Profile prpro = new Profile (ProfileBase.Create (e.Responsible)); + + var pbc = ProfileBase.Create (e.Client); + Profile prcli = new Profile (pbc); + + if (!prpro.HasBankAccount || !prcli.IsBillable) + throw new Exception("account number for provider, or client not billable."); + + tmpe.Session.Add ("from", prpro); + tmpe.Session.Add ("to", prcli); + tmpe.Init (); + + string contentStr = tmpe.TransformText (); + + string name = string.Format ("tmpestimtex-{0}", e.Id); + string fullname = Path.Combine ( + HttpRuntime.CodegenDir, name); + FileInfo fi = new FileInfo(fullname + ".tex"); + FileInfo fo = new FileInfo(fullname + ".pdf"); + using (StreamWriter sw = new StreamWriter (fi.FullName)) + { + sw.Write (contentStr); + } + using (Process p = new Process ()) { + p.StartInfo.WorkingDirectory = HttpRuntime.CodegenDir; + p.StartInfo = new ProcessStartInfo (); + p.StartInfo.UseShellExecute = false; + p.StartInfo.FileName = "/usr/bin/texi2pdf"; + p.StartInfo.Arguments = + string.Format ("--batch --build-dir={2} -o {0} {1}", + fo.FullName, + fi.FullName,HttpRuntime.CodegenDir); + p.Start (); + p.WaitForExit (); + if (p.ExitCode != 0) + throw new Exception ("Pdf generation failed with exit code:" + p.ExitCode); + } + + using (StreamReader sr = new StreamReader (fo.FullName)) { + byte[] buffer = File.ReadAllBytes (fo.FullName); + writeStream.Write(buffer,0,buffer.Length); + } + fi.Delete(); + fo.Delete(); + + } + + + } +} + + + +#endif diff --git a/booking/Formatters/EstimToPdfFormatter.cs b/booking/Formatters/EstimToPdfFormatter.cs new file mode 100644 index 00000000..3d4a7143 --- /dev/null +++ b/booking/Formatters/EstimToPdfFormatter.cs @@ -0,0 +1,134 @@ +// +// EstimToPdfFormatter.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Web; +using System.Web.Profile; +using Yavsc.Model.RolesAndMembers; +using Yavsc.Model.WorkFlow; + +namespace Yavsc.Formatters +{ + /// + /// Estim to pdf formatter. + /// + public class EstimToPdfFormatter: BufferedMediaTypeFormatter + { + /// + /// Initializes a new instance of the class. + /// + public EstimToPdfFormatter () + { + SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/pdf")); + } + /// + /// Determines whether this instance can read type the specified type. + /// + /// true if this instance can read type the specified type; otherwise, false. + /// Type. + public override bool CanReadType(Type type) + { + return false; + } + /// + /// Determines whether this instance can write type the specified type. + /// + /// true if this instance can write type the specified type; otherwise, false. + /// Type. + public override bool CanWriteType(System.Type type) + { + if (type == typeof(Estimate)) + { + return true; + } + else + { + Type enumerableType = typeof(IEnumerable); + return enumerableType.IsAssignableFrom(type); + } + } + + public override void WriteToStream (Type type, object value, Stream stream, HttpContent content) + { + + // TODO create a type containing generation parameters, including a template path, and generate from them + + Yavsc.templates.Estim tmpe = new Yavsc.templates.Estim(); + tmpe.Session = new Dictionary(); + Estimate e = value as Estimate; + tmpe.Session.Add ("estim", e); + + Profile prpro = new Profile (ProfileBase.Create (e.Responsible)); + + var pbc = ProfileBase.Create (e.Client); + Profile prcli = new Profile (pbc); + + if (!prpro.HasBankAccount || !prcli.IsBillable) + throw new Exception("account number for provider, or client not billable."); + + tmpe.Session.Add ("from", prpro); + tmpe.Session.Add ("to", prcli); + tmpe.Init (); + + string contentStr = tmpe.TransformText (); + + string name = string.Format ("tmpestimtex-{0}", e.Id); + string fullname = Path.Combine ( + HttpRuntime.CodegenDir, name); + FileInfo fi = new FileInfo(fullname + ".tex"); + FileInfo fo = new FileInfo(fullname + ".pdf"); + using (StreamWriter sw = new StreamWriter (fi.FullName)) + { + sw.Write (contentStr); + } + using (Process p = new Process ()) { + p.StartInfo.WorkingDirectory = HttpRuntime.CodegenDir; + p.StartInfo = new ProcessStartInfo (); + p.StartInfo.UseShellExecute = false; + p.StartInfo.FileName = "/usr/bin/texi2pdf"; + p.StartInfo.Arguments = + string.Format ("--batch --build-dir={2} -o {0} {1}", + fo.FullName, + fi.FullName,HttpRuntime.CodegenDir); + p.Start (); + p.WaitForExit (); + if (p.ExitCode != 0) + throw new Exception ("Pdf generation failed with exit code:" + p.ExitCode); + } + + using (StreamReader sr = new StreamReader (fo.FullName)) { + byte[] buffer = File.ReadAllBytes (fo.FullName); + stream.Write(buffer,0,buffer.Length); + } + fi.Delete(); + fo.Delete(); + + } + + + } +} + diff --git a/booking/Formatters/FormatterException.cs b/booking/Formatters/FormatterException.cs new file mode 100644 index 00000000..08725d8e --- /dev/null +++ b/booking/Formatters/FormatterException.cs @@ -0,0 +1,57 @@ +// +// TexToPdfFormatter.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Collections.Generic; +using System.IO; +using System.Web; +using System.Diagnostics; +using System.Net.Http; +using Yavsc.Helpers; + +namespace Yavsc.Formatters +{ + /// + /// Formatter exception. + /// + public class FormatterException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// Message. + public FormatterException(string message):base(message) + { + } + /// + /// Initializes a new instance of the class. + /// + /// Message. + /// Inner exception. + public FormatterException(string message,Exception innerException):base(message,innerException) + { + } + + public string Output { get; set; } + public string Error { get; set; } + } +} diff --git a/booking/Formatters/RssFeedsFormatter.cs b/booking/Formatters/RssFeedsFormatter.cs new file mode 100644 index 00000000..076b7b9c --- /dev/null +++ b/booking/Formatters/RssFeedsFormatter.cs @@ -0,0 +1,100 @@ +// +// RssFormatter.cs +// +// Author: +// paul <${AuthorEmail}> +// +// Copyright (c) 2015 paul +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Web.Mvc; +using System.Net; +using Yavsc.Model; +using System.Text; + +namespace Yavsc.Formatters +{ + /// + /// Rss feeds formatter. + /// + public class RssFeedsFormatter:SimpleFormatter + { + string doctype = ""; + /// + /// Initializes a new instance of the class. + /// + public RssFeedsFormatter + () : base ("application/rss+xml") + { + } + + private const string dateformat = "ddd, dd MMM yyyy HH:mm:ss K"; + public override void WriteToStream (Type type, object value, Stream stream, HttpContent content) + { + RssFeedsChannel feeds = value as RssFeedsChannel; + using (var writer = new StreamWriter (stream)) { + TagBuilder rss = new TagBuilder ("rss"); + rss.Attributes.Add ("version", "2.0"); + TagBuilder channel = new TagBuilder ("channel"); + TagBuilder title = new TagBuilder ("title"); + TagBuilder description = new TagBuilder ("description"); + TagBuilder lastBuildDate = new TagBuilder ("lastBuildDate"); + TagBuilder link = new TagBuilder ("link"); + + title.InnerHtml = MvcHtmlString.Create (feeds.Title).ToHtmlString (); + description.InnerHtml = MvcHtmlString.Create (feeds.Description).ToHtmlString (); + lastBuildDate.InnerHtml = MvcHtmlString.Create (feeds.LastBuildDate.ToString (dateformat)).ToHtmlString (); + link.InnerHtml = MvcHtmlString.Create (feeds.Link).ToHtmlString (); + StringBuilder sb = new StringBuilder (); + foreach (RssFeedsEntry e in feeds.Entries) { + TagBuilder item = new TagBuilder ("item"); + TagBuilder ititle = new TagBuilder ("title"); + ititle.InnerHtml = e.Title; + + TagBuilder idescription = new TagBuilder ("description"); + idescription.InnerHtml = MvcHtmlString.Create (e.Description).ToHtmlString (); + TagBuilder ipubDate = new TagBuilder ("pubDate"); + ipubDate.InnerHtml = MvcHtmlString.Create ( + e.PubDate.ToString (dateformat)).ToHtmlString (); + + TagBuilder ilink = new TagBuilder ("link"); + ilink.InnerHtml = MvcHtmlString.Create (e.Link).ToHtmlString (); + + item.InnerHtml = ititle.ToString () + "\n" + + idescription.ToString () + "\n" + + ipubDate.ToString () + "\n" + + ilink.ToString () + "\n"; + + sb.Append (item.ToString () + "\n"); + } + channel.InnerHtml = title.ToString () + "\n" + + description.ToString () + "\n" + + lastBuildDate.ToString () + "\n" + + link.ToString () + "\n" + + sb.ToString () + "\n"; + rss.InnerHtml = channel.ToString (); + writer.WriteLine (doctype); + writer.Write (rss.ToString ()); + } + } + } + +} diff --git a/booking/Formatters/SimpleFormatter.cs b/booking/Formatters/SimpleFormatter.cs new file mode 100644 index 00000000..cf6701f7 --- /dev/null +++ b/booking/Formatters/SimpleFormatter.cs @@ -0,0 +1,84 @@ +// +// TexFormatter.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Web.Mvc; +using System.Net; + +namespace Yavsc.Formatters +{ + /// + /// Simple formatter. + /// + public class SimpleFormatter : BufferedMediaTypeFormatter + { + /// + /// Initializes a new instance of the class. + /// + /// Mimetype. + public SimpleFormatter (string mimetype) + { + SupportedMediaTypes.Add(new MediaTypeHeaderValue(mimetype)); + } + /// + /// Determines whether this instance can write type the specified type. + /// + /// true if this instance can write type the specified type; otherwise, false. + /// Type. + public override bool CanWriteType(System.Type type) + { + if (type == typeof(string)) + { + return true; + } + else + { + Type enumerableType = typeof(IEnumerable); + return enumerableType.IsAssignableFrom(type); + } + } + /// + /// Determines whether this instance can read type the specified type. + /// + /// true if this instance can read type the specified type; otherwise, false. + /// Type. + public override bool CanReadType(Type type) + { + return false; + } + + + public override void WriteToStream (Type type, object value, Stream writeStream, HttpContent content) + { + using (var writer = new StreamWriter(writeStream)) + { + string doc = value as string; + writer.Write (doc); + } + + } + } +} + diff --git a/booking/Formatters/TexToPdfFormatter.cs b/booking/Formatters/TexToPdfFormatter.cs new file mode 100644 index 00000000..f627d2cf --- /dev/null +++ b/booking/Formatters/TexToPdfFormatter.cs @@ -0,0 +1,137 @@ +// +// TexToPdfFormatter.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Collections.Generic; +using System.IO; +using System.Web; +using System.Diagnostics; +using System.Net.Http; +using Yavsc.Helpers; + +namespace Yavsc.Formatters +{ + /// + /// Tex to pdf formatter. + /// + public class TexToPdfFormatter: BufferedMediaTypeFormatter + { + /// + /// Initializes a new instance of the class. + /// + public TexToPdfFormatter () + { + SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/pdf")); + } + /// + /// Determines whether this instance can read type the specified type. + /// + /// true if this instance can read type the specified type; otherwise, false. + /// Type. + public override bool CanReadType(Type type) + { + return false; + } + /// + /// Determines whether this instance can write type the specified type. + /// + /// true if this instance can write type the specified type; otherwise, false. + /// Type. + public override bool CanWriteType(System.Type type) + { + if (type == typeof(string)) + { + return true; + } + else + { + Type enumerableType = typeof(IEnumerable); + return enumerableType.IsAssignableFrom(type); + } + } + /// + /// Writes to stream. + /// + /// Type. + /// Value. + /// Stream. + /// Content headers. + /// + public override void WriteToStream (Type type, object value, Stream stream, HttpContent content) + { + + string temp = Path.GetTempPath (); + string cntStr = value as string; + string name = "tmpdoc-"+Guid.NewGuid().ToString(); + string fullname = Path.Combine (temp, name); + FileInfo fi = new FileInfo(fullname + ".tex"); + FileInfo fo = null; + using (StreamWriter sw = new StreamWriter (fi.OpenWrite())) + { + sw.Write (cntStr); + sw.Close (); + } + + using (Process p = new Process ()) { + + Directory.SetCurrentDirectory (temp); + + p.StartInfo.WorkingDirectory = temp; + p.StartInfo = new ProcessStartInfo (); + p.StartInfo.UseShellExecute = false; + p.StartInfo.FileName = "texi2pdf"; + p.StartInfo.Arguments = + string.Format ("--batch {0}", + fi.FullName); + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + + p.Start (); + p.WaitForExit (); + + if (p.ExitCode != 0) { + var ex = new FormatterException ("Pdf generation failed with exit code:" + p.ExitCode); + ex.Output = p.StandardOutput.ReadToEnd ()+"\nCWD:"+temp; + ex.Error = p.StandardError.ReadToEnd (); + throw ex; + } + fo = new FileInfo(name + ".pdf"); + } + + byte[] buffer = File.ReadAllBytes (fo.Name); + stream.Write(buffer,0,buffer.Length); + if (content.Headers != null) + SetFileName(content.Headers, value.GetHashCode ().ToString ()); + } + + /// + /// Sets the name of the file. + /// + /// Content headers. + /// Basename. + public static void SetFileName(HttpContentHeaders contentHeaders, string basename) { + contentHeaders.ContentDisposition = new ContentDispositionHeaderValue ("attachment") { + FileName = "doc-" + basename + ".pdf" + }; + } + } +} diff --git a/booking/Global.asax b/booking/Global.asax new file mode 100644 index 00000000..569f561d --- /dev/null +++ b/booking/Global.asax @@ -0,0 +1 @@ +<%@ Application Inherits="Yavsc.MvcApplication" %> diff --git a/booking/Global.asax.cs b/booking/Global.asax.cs new file mode 100644 index 00000000..c5681ea6 --- /dev/null +++ b/booking/Global.asax.cs @@ -0,0 +1,132 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Routing; +using Yavsc.Formatters; +using Yavsc.Model.FrontOffice; +using System.Web.SessionState; +using System.Web.Mvc; +using System.Web.Http; +using System.Web.WebPages.Scope; +using System.Reflection; +using System.Web.Configuration; + +namespace Yavsc +{ + + /// + /// Mvc application. + /// + public class MvcApplication : System.Web.HttpApplication + { + + /// + /// Registers the routes. + /// + /// Routes. + public static void RegisterRoutes (RouteCollection routes) + { + // Should be FrontOffice in a POS, + string defaultController = + WebConfigurationManager.AppSettings ["DefaultController"]; + if (defaultController == null) + defaultController = "Home"; + routes.IgnoreRoute ("{resource}.axd/{*pathInfo}"); // not used + routes.IgnoreRoute ("Scripts/{*pathInfo}"); // web user side scripts + routes.IgnoreRoute ("App_Theme/{*pathInfo}"); // sites themes + routes.IgnoreRoute ("users/{*pathInfo}"); // user's files + routes.IgnoreRoute ("avatars/{*pathInfo}"); // user's avatar + routes.IgnoreRoute ("bfiles/{*pathInfo}"); // Blog files + routes.IgnoreRoute ("xmldoc/{*pathInfo}"); // xml doc + routes.IgnoreRoute ("htmldoc/{*pathInfo}"); // html doc + routes.IgnoreRoute ("fonts/{*pathInfo}"); // fonts + routes.IgnoreRoute ("favicon.ico"); // favorite icon + routes.IgnoreRoute ("favicon.png"); // favorite icon + routes.IgnoreRoute ("robots.txt"); // for search engine robots + routes.MapRoute ( + "Titles", + "title/{title}", + new { controller = "Blogs", action = "Index", + title=UrlParameter.Optional } + ); + routes.MapRoute ( + "Blogs", + "blog/{user}", + new { controller = "Blogs", + action = "UserPosts", + user="Paul Schneider" } + ); + + routes.MapRoute ( + "BlogByTitle", + "by/{user}/{title}/{id}", + new { controller = "Blogs", + action = "UserPosts", + user="Paul Schneider", + title=UrlParameter.Optional, + id=UrlParameter.Optional } + ); + routes.MapRoute ( + "BlogById", + "b/{action}/{postid}", + new { controller = "Blogs", action = "Index", + postid=UrlParameter.Optional } + ); + routes.MapRoute ( + "BackCompat", + "Blogs/{action}/{user}/{title}", + new { controller = "Blogs", action = "Index", user = UrlParameter.Optional, title = UrlParameter.Optional } + ); + + routes.MapRoute ( + "Default", + "{controller}/{action}/{id}", + new { controller = "Home", action = "Index", id = UrlParameter.Optional } + ); + + } + + /// + /// Starts the Application. + /// + protected void Application_Start () + { + AreaRegistration.RegisterAllAreas (); + WebApiConfig.Register (GlobalConfiguration.Configuration); + RegisterRoutes (RouteTable.Routes); + } + + /// + /// Applications the post authorize request. + /// + protected void Application_PostAuthorizeRequest() + { + if (IsWebApiRequest()) + { + HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required); + } + } + + private bool IsWebApiRequest() + { + return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative); + } + + /// + /// begins a request against this application. + /// + protected void Application_BeginRequest() + { + var ob = typeof( + AspNetRequestScopeStorageProvider).Assembly.GetType( + "System.Web.WebPages.WebPageHttpModule").GetProperty + ("AppStartExecuteCompleted", + BindingFlags.NonPublic | BindingFlags.Static); + ob.SetValue(null, true, null); + + } + } +} + diff --git a/booking/Helpers/Google/ApiClient.cs b/booking/Helpers/Google/ApiClient.cs new file mode 100644 index 00000000..6c4ca425 --- /dev/null +++ b/booking/Helpers/Google/ApiClient.cs @@ -0,0 +1,78 @@ +// +// Manager.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using Yavsc.Helpers; +using System.Web.Profile; +using Yavsc.Model.Google; +using System.Net; +using System.IO; +using System.Text; +using Newtonsoft.Json; + +namespace Yavsc.Helpers.Google +{ + /// + /// Google base API client. + /// This class implements the identification values for a Google Api, + /// and provides some scope values. + /// + public class ApiClient + { + /// + /// The CLIENT Id. + /// + public static string CLIENT_ID { get ; set ; } + + /// + /// The CLIENt SECREt + /// + public static string CLIENT_SECRET { get ; set ; } + + /// + /// The API KEY. + /// + public static string API_KEY { get ; set ; } + /* // to use in descendence + * + protected static string getPeopleUri = "https://www.googleapis.com/plus/v1/people"; + private static string authUri = "https://accounts.google.com/o/oauth2/auth"; + */ + /// + /// The Map tracks scope . + /// + protected static string scopeTracks = "https://www.googleapis.com/auth/tracks"; + /// + /// The calendar scope. + /// + protected static string scopeCalendar = "https://www.googleapis.com/auth/calendar"; + + /// + /// The scope openid. + /// + protected static string[] scopeOpenid = { + "openid", + "profile", + "email" + }; + } + +} diff --git a/booking/Helpers/Google/CalendarApi.cs b/booking/Helpers/Google/CalendarApi.cs new file mode 100644 index 00000000..116a54af --- /dev/null +++ b/booking/Helpers/Google/CalendarApi.cs @@ -0,0 +1,138 @@ +// +// Calendar.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using Yavsc.Helpers; +using System.Web.Profile; +using Yavsc.Model.Google; +using System.Net; +using System.IO; +using System.Text; +using Newtonsoft.Json; +using System.Web; +using Yavsc.Model; + +namespace Yavsc.Helpers.Google +{ + /// + /// Google Calendar API client. + /// + public class CalendarApi : ApiClient + { + public CalendarApi(string apiKey) + { + API_KEY = apiKey; + } + /// + /// The get cal list URI. + /// + protected static string getCalListUri = "https://www.googleapis.com/calendar/v3/users/me/calendarList"; + /// + /// The get cal entries URI. + /// + protected static string getCalEntriesUri = "https://www.googleapis.com/calendar/v3/calendars/{0}/events"; + + /// + /// The date format. + /// + private static string dateFormat = "yyyy-MM-ddTHH:mm:ss"; + + /// + /// The time zone. TODO Fixme with machine time zone + /// + private string timeZone = "+01:00"; + + /// + /// Gets the calendar list. + /// + /// The calendars. + /// Cred. + public CalendarList GetCalendars (string cred) + { + CalendarList res = null; + HttpWebRequest webreq = WebRequest.CreateHttp (getCalListUri); + webreq.Headers.Add (HttpRequestHeader.Authorization, cred); + webreq.Method = "GET"; + webreq.ContentType = "application/http"; + using (WebResponse resp = webreq.GetResponse ()) { + using (Stream respstream = resp.GetResponseStream ()) { + var cr = new Newtonsoft.Json.JsonSerializer (); + using (var rdr = new StreamReader (respstream)) + res = (CalendarList) cr.Deserialize (rdr, typeof(CalendarList)); + } + resp.Close (); + } + webreq.Abort (); + return res; + } + + /// + /// Gets a calendar. + /// + /// The calendar. + /// Calid. + /// Mindate. + /// Maxdate. + /// Upr. + public CalendarEventList GetCalendar (string calid, DateTime mindate, DateTime maxdate,string cred) + { + if (string.IsNullOrWhiteSpace (calid)) + throw new Exception ("the calendar identifier is not specified"); + + string uri = string.Format ( + getCalEntriesUri, HttpUtility.UrlEncode (calid)) + + string.Format ("?orderBy=startTime&singleEvents=true&timeMin={0}&timeMax={1}&key=" + API_KEY, + HttpUtility.UrlEncode (mindate.ToString (dateFormat) + timeZone), + HttpUtility.UrlEncode (maxdate.ToString (dateFormat) + timeZone)); + + HttpWebRequest webreq = WebRequest.CreateHttp (uri); + var cr = new Newtonsoft.Json.JsonSerializer (); + webreq.Headers.Add (HttpRequestHeader.Authorization, cred); + webreq.Method = "GET"; + webreq.ContentType = "application/http"; + CalendarEventList res = null; + try { + using (WebResponse resp = webreq.GetResponse ()) { + using (Stream respstream = resp.GetResponseStream ()) { + try { + using (var rdr = new StreamReader(respstream)) { + res = (CalendarEventList) + cr.Deserialize(rdr,typeof(CalendarEventList)); + } + } catch (Exception ) { + respstream.Close (); + resp.Close (); + webreq.Abort (); + throw ; + } + } + resp.Close (); + } + } catch (WebException ) { + webreq.Abort (); + throw; + } + webreq.Abort (); + return res; + } + + } +} diff --git a/booking/Helpers/Google/Entity.cs b/booking/Helpers/Google/Entity.cs new file mode 100644 index 00000000..73ee123f --- /dev/null +++ b/booking/Helpers/Google/Entity.cs @@ -0,0 +1,56 @@ +// +// Entity.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using Yavsc.Helpers; +using System.Web.Profile; +using Yavsc.Model.Google; +using System.Net; +using System.IO; +using System.Text; +using Newtonsoft.Json; + +namespace Yavsc.Helpers.Google +{ + /// + /// Entity. + /// + public class Entity + { + /// + /// The I. + /// + public string ID; + /// + /// The name. + /// + public string Name; + + /// + /// The type: AUTOMOBILE: A car or passenger vehicle. + /// * TRUCK: A truck or cargo vehicle. + /// * WATERCRAFT: A boat or other waterborne vehicle. + /// * PERSON: A person. + /// + public string Type; + } + +} diff --git a/booking/Helpers/Google/EntityQuery.cs b/booking/Helpers/Google/EntityQuery.cs new file mode 100644 index 00000000..9b393e7f --- /dev/null +++ b/booking/Helpers/Google/EntityQuery.cs @@ -0,0 +1,46 @@ +// +// EntityQuery.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using Yavsc.Helpers; +using System.Web.Profile; +using Yavsc.Model.Google; +using System.Net; +using System.IO; +using System.Text; +using Newtonsoft.Json; + +namespace Yavsc.Helpers.Google +{ + /// + /// Entity query. + /// + public class EntityQuery { + /// + /// The entity identifiers. + /// + public string [] EntityIds; + /// + /// The minimum identifier. + /// + public string MinId; + } +} diff --git a/booking/Helpers/Google/MapTracks.cs b/booking/Helpers/Google/MapTracks.cs new file mode 100644 index 00000000..388e40c2 --- /dev/null +++ b/booking/Helpers/Google/MapTracks.cs @@ -0,0 +1,81 @@ +// +// Google.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using Yavsc.Helpers; +using System.Web.Profile; +using Yavsc.Model.Google; +using System.Net; +using System.IO; +using System.Text; +using Newtonsoft.Json; + +namespace Yavsc.Helpers.Google +{ + /// + /// Google Map tracks Api client. + /// + public class MapTracks:ApiClient { + + /// + /// The google map tracks path (uri of the service). + /// + protected static string googleMapTracksPath = "https://www.googleapis.com/tracks/v1/"; + // entities/[create|list|delete] + // collections/[list|create|[add|remove]entities|delete] + // crumbs/[record|getrecent|gethistory|report|summarize|getlocationinfo|delete + + + // entities/[create|list|delete] + // collections/[list|create|[add|remove]entities|delete] + // crumbs/[record|getrecent|gethistory|report|summarize|getlocationinfo|delete + + /// + /// Creates the entity. + /// + /// The entity. + /// Entities. + public static string [] CreateEntity( Entity[] entities ) { + string [] ans = null; + using (SimpleJsonPostMethod< Entity[] ,string []> wr = + new SimpleJsonPostMethod< Entity[] ,string[]> (googleMapTracksPath + "entities/create")) + { + ans = wr.Invoke (entities); + } + return ans; + } + + /// + /// Lists the entities. + /// + /// The entities. + /// Eq. + static Entity[] ListEntities (EntityQuery eq) + { + Entity [] ans = null; + using (SimpleJsonPostMethod wr = + new SimpleJsonPostMethod (googleMapTracksPath + "entities/create")) + { + ans = wr.Invoke (eq); + } + return ans; + } + } +} diff --git a/booking/Helpers/Google/OAuth2.cs b/booking/Helpers/Google/OAuth2.cs new file mode 100644 index 00000000..305ab412 --- /dev/null +++ b/booking/Helpers/Google/OAuth2.cs @@ -0,0 +1,250 @@ +// +// OAuth2.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.IO; +using System.Net; +using System.Text; +using Newtonsoft.Json; +using Yavsc.Model.Google; +using System.Web.Profile; +using System.Web; +using Yavsc.Model; +using Yavsc.Helpers.Google; + +namespace Yavsc.Helpers.Google +{ + + /// + /// Google O auth2 client. + /// + public class OAuth2 : ApiClient + { + /// + /// The URI used to get tokens. + /// + protected static string tokenUri = "https://accounts.google.com/o/oauth2/token"; + + /// + /// The URI used to get authorized to. + /// + protected static string authUri = "https://accounts.google.com/o/oauth2/auth"; + + /// + /// Gets or sets the redirect URI sent to Google. + /// + /// The redirect URI. + public string RedirectUri { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// Redirect URI. + public OAuth2 (string redirectUri, string clientId, string clientSecret) + { + RedirectUri = redirectUri; + CLIENT_ID = clientId; + CLIENT_SECRET = clientSecret; + } + + /// + /// Login with Google + /// by redirecting the specified http web response bresp, + /// and using the specified session state. + /// + /// Bresp. + /// State. + public void Login (HttpResponseBase bresp, string state) + { + string scope = string.Join ("%20", scopeOpenid); + + string prms = String.Format ("response_type=code&client_id={0}&redirect_uri={1}&scope={2}&state={3}&include_granted_scopes=false&approval_prompt=force", + CLIENT_ID, RedirectUri, scope, state); + GetAuthResponse (bresp, prms); + } + + /// + /// Gets the cal authorization. + /// + /// Bresp. + /// State. + public void GetCalendarScope (HttpResponseBase bresp, string state) + { + string prms = String.Format ("response_type=code&client_id={0}&redirect_uri={1}&scope={2}&state={3}&include_granted_scopes=true&access_type=offline&approval_prompt=force", + CLIENT_ID, RedirectUri, scopeCalendar, state); + GetAuthResponse (bresp, prms); + } + + private void GetAuthResponse (HttpResponseBase bresp, string prms) + { + string cont = null; + WebRequest wr = WebRequest.Create (authUri + "?" + prms); + wr.Method = "GET"; + using (WebResponse response = wr.GetResponse ()) { + string resQuery = response.ResponseUri.Query; + cont = HttpUtility.ParseQueryString (resQuery) ["continue"]; + response.Close (); + } + wr.Abort (); + bresp.Redirect (cont); + } + + /// + /// Builds the post data, from code given + /// by Google in the request parameters, + /// and using the given redirectUri. + /// This request body is used to get a new + /// OAuth2 token from Google, it is Url encoded. + /// + /// The post data from code. + /// Redirect URI. + /// Code. + public static string TokenPostDataFromCode (string redirectUri, string code) + { + string postdata = + string.Format ( + "redirect_uri={0}&client_id={1}&client_secret={2}&code={3}&grant_type=authorization_code", + HttpUtility.UrlEncode (redirectUri), + HttpUtility.UrlEncode (CLIENT_ID), + HttpUtility.UrlEncode (CLIENT_SECRET), + HttpUtility.UrlEncode (code)); + return postdata; + } + + /// + /// Gets the Google Authorization token. + /// + /// The token. + /// Rq. + /// State. + /// Message. + public AuthToken GetToken (HttpRequestBase rq, string state, out string message) + { + string code = OAuth2.GetCodeFromRequest (rq, state, out message); + string postdata = OAuth2.TokenPostDataFromCode (RedirectUri, code); + return GetTokenPosting (postdata); + } + + internal static AuthToken GetTokenPosting (string postdata) + { + var cr = new Newtonsoft.Json.JsonSerializer (); + HttpWebRequest webreq = WebRequest.CreateHttp (tokenUri); + webreq.Method = "POST"; + webreq.Accept = "application/json"; + webreq.ContentType = "application/x-www-form-urlencoded"; + Byte[] bytes = System.Text.Encoding.UTF8.GetBytes (postdata); + webreq.ContentLength = bytes.Length; + + using (Stream dataStream = webreq.GetRequestStream ()) { + dataStream.Write (bytes, 0, bytes.Length); + dataStream.Close (); + } + + AuthToken gat = null; + using (WebResponse response = webreq.GetResponse ()) { + using (Stream responseStream = response.GetResponseStream ()) { + gat = (AuthToken) cr.Deserialize (new StreamReader (responseStream),typeof(AuthToken)); + responseStream.Close (); + } + response.Close (); + } + webreq.Abort (); + return gat; + } + + /// + /// Gets the code from the Google request. + /// + /// The code from request. + /// Rq. + /// State. + /// Message. + public static string GetCodeFromRequest (HttpRequestBase rq, string state, out string message) + { + message = ""; + string code = rq.Params ["code"]; + string error = rq.Params ["error"]; + if (error != null) { + message = + string.Format (LocalizedText.Google_error, + LocalizedText.ResourceManager.GetString (error)); + return null; + } + string rqstate = rq.Params ["state"]; + if (state != null && string.Compare (rqstate, state) != 0) { + message = + LocalizedText.ResourceManager.GetString ("invalid request state"); + return null; + } + return code; + } + + /// + /// Invalid O auth2 refresh token. + /// + public class InvalidOAuth2RefreshToken: Exception + { + /// + /// Initializes a new instance of the class. + /// + /// Message. + public InvalidOAuth2RefreshToken(string message):base(message) + { + } + /// + /// Initializes a new instance of the class. + /// + /// Message. + /// Inner exception. + public InvalidOAuth2RefreshToken(string message,Exception innerException):base(message,innerException) + { + } + } + + /// + /// Gets fresh google credential. + /// + /// The fresh google credential. + /// Pr. + public static string GetFreshGoogleCredential (ProfileBase pr) + { + string token = (string)pr.GetPropertyValue ("gtoken"); + string token_type = (string) pr.GetPropertyValue ("gtokentype"); + DateTime token_exp = (DateTime) pr.GetPropertyValue ("gtokenexpir"); + if (token_exp < DateTime.Now) { + object ort = pr.GetPropertyValue ("grefreshtoken"); + if (ort is DBNull || string.IsNullOrWhiteSpace((string)ort)) { + throw new InvalidOAuth2RefreshToken ("Google"); + } + string refresh_token = ort as string; + AuthToken gat = OAuth2.GetTokenPosting ( + string.Format ("grant_type=refresh_token&client_id={0}&client_secret={1}&refresh_token={2}", + CLIENT_ID, CLIENT_SECRET, refresh_token)); + token = gat.access_token; + pr.SetPropertyValue ("gtoken", token); + pr.Save (); + // ASSERT gat.token_type == pr.GetPropertyValue("gtokentype") + } + return token_type + " " + token; + } + + } +} + diff --git a/booking/Helpers/Google/PeopleApi.cs b/booking/Helpers/Google/PeopleApi.cs new file mode 100644 index 00000000..15a3e12e --- /dev/null +++ b/booking/Helpers/Google/PeopleApi.cs @@ -0,0 +1,69 @@ +// +// PeopleApi.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 GNU GPL +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +using System; +using System.IO; +using System.Net; +using System.Text; +using Newtonsoft.Json; +using Yavsc.Model.Google; +using System.Web.Profile; +using System.Web; +using Yavsc.Model; +using Yavsc.Helpers.Google; + +namespace Yavsc.Helpers.Google +{ + /// + /// Google People API. + /// + public class PeopleApi: ApiClient + { + private static string getPeopleUri = "https://www.googleapis.com/plus/v1/people"; + + /// + /// Gets the People object associated to the given Google Access Token + /// + /// The me. + /// The Google Access Token object class. + public static People GetMe (AuthToken gat) + { + People me; + var cr = new Newtonsoft.Json.JsonSerializer (); + HttpWebRequest webreppro = WebRequest.CreateHttp (getPeopleUri + "/me"); + webreppro.ContentType = "application/http"; + webreppro.Headers.Add (HttpRequestHeader.Authorization, gat.token_type + " " + gat.access_token); + webreppro.Method = "GET"; + using (WebResponse proresp = webreppro.GetResponse ()) { + using (Stream prresponseStream = proresp.GetResponseStream ()) { + using (var rdr = new StreamReader (prresponseStream)) { + me = (People)cr.Deserialize (new StreamReader (prresponseStream),typeof(People)); + } + prresponseStream.Close (); + } + proresp.Close (); + } + webreppro.Abort (); + return me; + } + } + +} diff --git a/booking/Helpers/SimpleJsonPostMethod.cs b/booking/Helpers/SimpleJsonPostMethod.cs new file mode 100644 index 00000000..511c7cab --- /dev/null +++ b/booking/Helpers/SimpleJsonPostMethod.cs @@ -0,0 +1,110 @@ +// +// PostJson.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Net; +using System.Text; +using System.IO; + +namespace Yavsc.Helpers +{ + /// + /// Simple json post method. + /// + public class SimpleJsonPostMethod: IDisposable + { + internal HttpWebRequest request = null; + internal HttpWebRequest Request { get { return request; } } + + string CharSet { + get { return Request.TransferEncoding; } + set { Request.TransferEncoding=value;} + } + string Method { get { return Request.Method; } } + /// + /// Gets the path. + /// + /// The path. + public string Path { + get{ return Request.RequestUri.ToString(); } + } + /// + /// Sets the credential. + /// + /// Cred. + public void SetCredential(string cred) { + Request.Headers.Set(HttpRequestHeader.Authorization,cred); + } + + /// + /// Initializes a new instance of the Yavsc.Helpers.SimpleJsonPostMethod class. + /// + /// Path to method. + public SimpleJsonPostMethod (string pathToMethod) + { + // ASSERT Request == null + request = WebRequest.CreateHttp (pathToMethod); + + Request.Method = "POST"; + Request.Accept = "application/json"; + Request.ContentType = "application/json"; + Request.TransferEncoding = "UTF-8"; + } + /// + /// Invoke the specified query. + /// + /// Query. + public TAnswer Invoke(TQuery query) + { + + // DataContractJsonSerializer serquery = new DataContractJsonSerializer (typeof(TQuery)); + // DataContractJsonSerializer seransw = new DataContractJsonSerializer (typeof(TAnswer)); + var cr = new Newtonsoft.Json.JsonSerializer (); + using (MemoryStream streamQuery = new MemoryStream ()) { + using (StreamWriter swr = new StreamWriter (streamQuery)) { + cr.Serialize (swr, query); + } + } + + TAnswer ans = default (TAnswer); + using (WebResponse response = Request.GetResponse ()) { + using (Stream responseStream = response.GetResponseStream ()) { + using (var rdr = new StreamReader (responseStream)) { + ans = (TAnswer)cr.Deserialize (rdr, typeof(TAnswer)); + } + } + response.Close(); + } + return ans; + } + + #region IDisposable implementation + + /// + /// Releases all resource used by the Yavsc.Helpers.SimpleJsonPostMethod object. + /// + public void Dispose () + { + if (Request != null) Request.Abort (); + } + #endregion + } +} + diff --git a/booking/Helpers/T.cs b/booking/Helpers/T.cs new file mode 100644 index 00000000..36428a98 --- /dev/null +++ b/booking/Helpers/T.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Configuration; +using System.Web.Mvc; +using System.Web.Mvc.Ajax; +using System.Net.Mail; +using Yavsc; +using System.Globalization; +using Yavsc.Model; + +namespace Yavsc.Helpers +{ + /// + /// T. + /// + public static class T + { + + /// + /// Gets the string. + /// + /// The string. + /// Message. + public static string GetString(string msg) + { + string tr = LocalizedText.ResourceManager.GetString (msg.Replace (" ", "_")); + return tr==null?msg:tr; + } + /// + /// Translate the specified helper and text. + /// + /// Helper. + /// Text. + public static IHtmlString Translate(this HtmlHelper helper, string text) + { + // Just call the other one, to avoid having two copies (we don't use the HtmlHelper). + return new MvcHtmlString(helper.Encode(GetString(text))); + } + + } +} diff --git a/booking/Helpers/TemplateException.cs b/booking/Helpers/TemplateException.cs new file mode 100644 index 00000000..fe8b1357 --- /dev/null +++ b/booking/Helpers/TemplateException.cs @@ -0,0 +1,15 @@ +using System; + +namespace Yavsc.Helpers +{ + class TemplateException : Exception + { + public TemplateException(string message):base(message) + { + } + public TemplateException(string message,Exception innerException):base(message,innerException) + { + } + } +} + diff --git a/booking/Helpers/ThanksHelper.cs b/booking/Helpers/ThanksHelper.cs new file mode 100644 index 00000000..6b47711b --- /dev/null +++ b/booking/Helpers/ThanksHelper.cs @@ -0,0 +1,63 @@ +using System; +using System.Configuration; +using System.Collections.Generic; +using System.Web.Mvc; +using System.Linq.Expressions; +using Yavsc.Model.Circles; + +namespace Yavsc.Helpers +{ + /// + /// Link. + /// + public class Link { + /// + /// Gets or sets the text. + /// + /// The text. + public string Text { get; set; } + /// + /// Gets or sets the URL. + /// + /// The URL. + public string Url { get; set; } + /// + /// Gets or sets the image. + /// + /// The image. + public string Image { get; set; } + } + + /// + /// Thanks helper. + /// + public static class ThanksHelper { + + static private ThanksConfigurationSection configurationSection=null; + /// + /// Gets the configuration section. + /// + /// The configuration section. + static public ThanksConfigurationSection ConfigurationSection { + get { + if (configurationSection==null) + configurationSection = (ThanksConfigurationSection) ConfigurationManager.GetSection ("system.web/thanks"); + return configurationSection; + } + } + /// + /// Html code for each entry + /// + public static Link[] Thanks (this HtmlHelper helper) + { + List result = new List() ; + if (ConfigurationSection == null) return result.ToArray(); + if (ConfigurationSection.To == null) return result.ToArray(); + foreach (ThanksConfigurationElement e in ConfigurationSection.To) + result.Add( new Link { Url = e.Url, Image=e.Image, Text = e.Name }); + return result.ToArray(); + } + + } + +} diff --git a/booking/Helpers/YavscAjaxHelper.cs b/booking/Helpers/YavscAjaxHelper.cs new file mode 100644 index 00000000..83487ba6 --- /dev/null +++ b/booking/Helpers/YavscAjaxHelper.cs @@ -0,0 +1,64 @@ +// +// YavscAjaxHelper.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 GNU GPL +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Web.Mvc; +using System.Collections.Generic; +using Yavsc.Model.Messaging; + +namespace Yavsc.Helpers +{ + /// + /// Yavsc ajax helper. + /// + public static class YavscAjaxHelper + { + /// + /// Notify the specified helper, message and click_action. + /// + /// Helper. + /// Message. + /// Click action. + public static void Notify(this AjaxHelper helper, string message, string click_action=null) { + + if (helper.ViewData ["Notifications"] == null) + helper.ViewData ["Notifications"] = new List (); + (helper.ViewData ["Notifications"] as List).Add ( + new Notification { body = QuoteJavascriptString(message), + click_action = click_action } ) ; + } + + /// + /// Quotes the javascript string. + /// + /// The javascript string. + /// String. + public static string QuoteJavascriptString(string str) + { + str = str.Replace ("\n", "\\n"); + if (str.Contains ("'")) + if (str.Contains ("\"")) + return "'" + str.Replace ("'", "\\'") + "'"; + else + return "\"" + str + "\""; + return "'" + str + "'"; + } + } +} diff --git a/booking/Helpers/YavscHelpers.cs b/booking/Helpers/YavscHelpers.cs new file mode 100644 index 00000000..ea00a02b --- /dev/null +++ b/booking/Helpers/YavscHelpers.cs @@ -0,0 +1,345 @@ +using System; +using System.Web; +using System.Configuration; +using System.Web.Security; +using System.IO; +using System.Web.Configuration; +using System.Net.Mail; +using Yavsc.Model.RolesAndMembers; +using System.Collections.Generic; +using System.Collections.Specialized; +using Yavsc.Model.Circles; +using System.Web.UI; +using System.Linq.Expressions; +using System.Web.Profile; +using System.Web.Script.Serialization; +using System.Web.Mvc; +using System.Text.RegularExpressions; +using Yavsc.Model.Messaging; + +namespace Yavsc.Helpers +{ + + /// + /// Yavsc helpers. + /// + public static class YavscHelpers + { + + + private static string siteName = null; + /// + /// Gets the name of the site. + /// + /// The name of the site. + public static string SiteName { + get { + if (siteName == null) + siteName = WebConfigurationManager.AppSettings ["Name"]; + return siteName; + } + } + // Administrator email + private static string admail = + WebConfigurationManager.AppSettings ["AdminEmail"]; + /// + /// Gets the Administrator email. + /// + /// The admail. + public static string Admail { + get { + return admail; + } + } + + /// + /// Sends the activation message. + /// + /// Helper. + /// User. + public static void SendActivationMessage(this System.Web.Http.Routing.UrlHelper helper, MembershipUser user) + { + SendActivationMessage (helper.Route("Default", new { controller="Account", + action = "Validate", + key=user.ProviderUserKey.ToString(), id = user.UserName } ) + , WebConfigurationManager.AppSettings ["RegistrationMessage"], + user); + } + /// + /// Sends the activation message. + /// + /// Helper. + /// User. + public static void SendActivationMessage(this System.Web.Mvc.UrlHelper helper, MembershipUser user) + { + SendActivationMessage ( + string.Format("{2}://{3}/Account/Validate/{1}?key={0}", + user.ProviderUserKey.ToString(), user.UserName , + helper.RequestContext.HttpContext.Request.Url.Scheme, + helper.RequestContext.HttpContext.Request.Url.Authority + ) + , WebConfigurationManager.AppSettings ["RegistrationMessage"], + user); + } + + /// + /// Sends the activation message. + /// + /// Validation URL. + /// Registration message. + /// User. + public static void SendActivationMessage(string validationUrl, string registrationMessage, MembershipUser user) { + FileInfo fi = new FileInfo ( + HttpContext.Current.Server.MapPath (registrationMessage)); + if (!fi.Exists) { + throw new Exception( + string.Format ( + "Erreur inattendue (pas de corps de message " + + "à envoyer pour le message de confirmation ({0}))", + registrationMessage)); + } + + using (StreamReader sr = fi.OpenText ()) { + string body = sr.ReadToEnd (); + body = body.Replace ("<%SiteName%>", YavscHelpers.SiteName); + body = body.Replace ("<%UserName%>", user.UserName); + body = body.Replace ("<%UserActivatonUrl%>", validationUrl); + + using (MailMessage msg = new MailMessage ( + Admail, user.Email, + string.Format ("Validation de votre compte {0}", YavscHelpers.SiteName), + body)) { + using (SmtpClient sc = new SmtpClient ()) { + sc.Send (msg); + } + } + + } + } + + /// + /// Validates the password reset. + /// + /// Model. + /// Errors. + /// User. + public static void ValidatePasswordReset(LostPasswordModel model, out StringDictionary errors, out MembershipUser user) + { + MembershipUserCollection users = null; + errors = new StringDictionary (); + user = null; + if (!string.IsNullOrEmpty (model.UserName)) { + users = + Membership.FindUsersByName (model.UserName); + if (users.Count < 1) { + errors.Add ("UserName", "User name not found"); + return ; + } + if (users.Count != 1) { + errors.Add ("UserName", "Found more than one user!(sic)"); + return ; + } + } + if (!string.IsNullOrEmpty (model.Email)) { + users = + Membership.FindUsersByEmail (model.Email); + if (users.Count < 1) { + errors.Add ( "Email", "Email not found"); + return ; + } + if (users.Count != 1) { + errors.Add ("Email", "Found more than one user!(sic)"); + return ; + } + } + if (users==null) + return; + // Assert users.Count == 1 + foreach (MembershipUser u in users) user = u; + } + /// + /// Avatars the URL. + /// + /// The URL. + /// Helper. + /// Username. + public static string AvatarUrl (this System.Web.Mvc.UrlHelper helper, string username) { + if (username == null) return null; + ProfileBase pr = ProfileBase.Create (username); + object avpath = null; + if (pr != null) avpath = pr.GetPropertyValue("Avatar"); + if (avpath == null || avpath is DBNull) + return DefaultAvatar==null?"/bfiles/"+username+".png":DefaultAvatar; + string avatarLocation = avpath as string; + if (avatarLocation.StartsWith ("~/")) { + avatarLocation = helper.RouteUrl("Default", avatarLocation); + } + return avatarLocation; + + } + + private static string avatarDir = "~/avatars"; + private static string defaultAvatar = null; + private static string defaultAvatarMimetype = null; + public static string DefaultAvatar { + get { + if (defaultAvatar == null) + GetAvatarConfig (); + return defaultAvatar; + } + } + public static string AvatarDir { + get { + return avatarDir; + } + } + /// + /// Initializes a new instance of the class. + /// + private static void GetAvatarConfig () + { + string[] defaultAvatarSpec = ConfigurationManager.AppSettings.Get ("DefaultAvatar").Split (';'); + if (defaultAvatarSpec.Length != 2) + throw new ConfigurationErrorsException ("the DefaultAvatar spec should be found as ; "); + defaultAvatar = defaultAvatarSpec [0]; + defaultAvatarMimetype = defaultAvatarSpec [1]; + } + + /// + /// Javas the script. + /// + /// The script. + /// Html. + /// Object. + public static string JavaScript(this System.Web.Mvc.HtmlHelper html, object obj) + { + return JavaScript (obj); + } + /// + /// Javas the script. + /// + /// The script. + /// Object. + public static string JavaScript(object obj) + { + JavaScriptSerializer serializer = new JavaScriptSerializer(); + return serializer.Serialize(obj); + } + + public static void Notify(ViewDataDictionary ViewData, string message, string click_action=null, string clickActionName="Ok") { + Notify(ViewData, new Notification { body = YavscAjaxHelper.QuoteJavascriptString(message), + click_action = click_action, click_action_name = YavscAjaxHelper.QuoteJavascriptString(clickActionName)} ) ; + } + + public static void Notify(ViewDataDictionary ViewData, Notification note) { + if (ViewData ["Notifications"] == null) + ViewData ["Notifications"] = new List (); + (ViewData ["Notifications"] as List).Add ( + note ) ; + } + /// + /// Files the list. + /// + /// The list. + /// Html. + /// Path. + /// Patterns. + public static IHtmlString FileList(this System.Web.Mvc.HtmlHelper html, string path, string [] patterns = null) { + StringWriter str = new StringWriter(); + HtmlTextWriter writter = new HtmlTextWriter (str); + DirectoryInfo di = new DirectoryInfo (HttpContext.Current.Server.MapPath(path)); + if (!di.Exists) + return new System.Web.Mvc.MvcHtmlString (""); + var files = new List (); + if (patterns == null) + patterns = new string[] { "*" }; + var url = new System.Web.Mvc.UrlHelper(html.ViewContext.RequestContext, + html.RouteCollection); + + foreach (string pattern in patterns) + files.AddRange( + di.EnumerateFiles ( + pattern, + SearchOption.TopDirectoryOnly)); + writter.RenderBeginTag ("table"); + writter.RenderBeginTag ("tr"); + writter.RenderBeginTag ("td"); + writter.Write (html.Translate ("Name")); + writter.RenderEndTag (); + writter.RenderBeginTag ("td"); + writter.Write (html.Translate ("Created")); + writter.RenderEndTag (); + writter.RenderBeginTag ("td"); + writter.Write (html.Translate ("Modified")); + writter.RenderEndTag (); + writter.RenderEndTag (); + foreach (FileInfo fi in files) { + writter.RenderBeginTag ("tr"); + writter.RenderBeginTag ("td"); + writter.AddAttribute ("href", url.Content(path+"/"+fi.Name)); + writter.RenderBeginTag ("a"); + writter.Write (fi.Name); + writter.RenderEndTag (); + writter.RenderEndTag (); + writter.RenderBeginTag ("td"); + writter.Write (fi.LastWriteTime.ToString ("U")); + writter.RenderEndTag (); + writter.RenderBeginTag ("td"); + writter.Write (fi.CreationTime.ToString("U")); + writter.RenderEndTag (); + writter.RenderEndTag (); + } + writter.RenderEndTag (); + return new System.Web.Mvc.MvcHtmlString (str.ToString ()); + } + + /// + /// Renders the page links. + /// + /// The page links. + /// Helper. + /// Result count. + /// Page size. + /// Page index. + public static IHtmlString RenderPageLinks ( + this HtmlHelper helper, + int PageIndex, int PageSize, int ResultCount, + string args="?PageIndex={0}", + string pagesLabel="Pages: ", string singlePage="", + string none="néant" + ) + { + StringWriter strwr = new StringWriter (); + HtmlTextWriter writer = new HtmlTextWriter(strwr); + + if (ResultCount > 0 && ResultCount > PageSize ) { + int pageCount = ((ResultCount-1) / PageSize) + 1; + if ( pageCount > 1 ) { + writer.WriteEncodedText (pagesLabel); + for (int pi = (PageIndex < 5) ? 0 : PageIndex - 5; pi < pageCount && pi < PageIndex + 5; pi++) { + if (PageIndex == pi) + writer.RenderBeginTag ("b"); + else { + writer.AddAttribute (HtmlTextWriterAttribute.Href, + string.Format (args, pi)); + writer.RenderBeginTag ("a"); + } + writer.Write (pi + 1); + writer.RenderEndTag (); + writer.Write (" "); + } + } + else { + writer.Write (singlePage); + } + } + if (ResultCount == 0) { + writer.WriteEncodedText(none); + } + return new MvcHtmlString(strwr.ToString()); + } + + + } +} + diff --git a/booking/Models/App.master b/booking/Models/App.master new file mode 100644 index 00000000..0e2e44c2 --- /dev/null +++ b/booking/Models/App.master @@ -0,0 +1,92 @@ +<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %> + + +<% ViewState["orgtitle"] = Html.Translate(Page.Title); %> +<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; %> + + + + + +" /> +" /> +" /> +" /> +" /> + + + + + + + + + + + + +
+ +

"> +<%=ViewState["orgtitle"]%> +- "><%= YavscHelpers.SiteName %> +

+
+ +
+<% if (ViewData ["Notifications"]!=null) { %> + +<% } %> +
+ +
+ + +
+ + + + + diff --git a/booking/Models/AppAdmin.master b/booking/Models/AppAdmin.master new file mode 100644 index 00000000..40944eb8 --- /dev/null +++ b/booking/Models/AppAdmin.master @@ -0,0 +1,107 @@ +<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %> + + +<% ViewState["orgtitle"] = Html.Translate(Page.Title); %> +<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; %> + + + + + +" /> +" /> +" /> +" /> +" /> + + + + + + + + + +<%=Ajax.GlobalizationScript()%> + + + + + + +
+ +

"> +<%=ViewState["orgtitle"]%> +- "><%= YavscHelpers.SiteName %> +

+
+ +
+<% if (ViewData ["Notifications"]!=null) { %> + +<% } %> +
+ +
+ + +
+ + + + + diff --git a/booking/Models/NoLogin.master b/booking/Models/NoLogin.master new file mode 100644 index 00000000..0e88e45a --- /dev/null +++ b/booking/Models/NoLogin.master @@ -0,0 +1,69 @@ +<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %> + + +<% ViewState["orgtitle"] = Html.Translate(Page.Title); %> +<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; %> + + + + + +" /> +" /> +" /> +" /> +" /> + + + + + + + + + + + + +
+ +

"> +<%=ViewState["orgtitle"]%> +- "><%= YavscHelpers.SiteName %> +

+
+ +
+<% if (ViewData ["Notifications"]!=null) { %> + +<% } %> +
+
+ + +
+ + + + + diff --git a/booking/RegistrationMail.txt b/booking/RegistrationMail.txt new file mode 100644 index 00000000..90e5bec0 --- /dev/null +++ b/booking/RegistrationMail.txt @@ -0,0 +1,9 @@ + +Votre compte <%SiteName%> a été créé, votre nom d'utilisateur +est <%UserName%>. + +Pour l'activer, veuillez suivre le lien suivant : + +<%UserActivatonUrl%> + +Merci d'avoir créé un compte utilisateur. diff --git a/booking/Settings/ModuleConfigurationElement.cs b/booking/Settings/ModuleConfigurationElement.cs new file mode 100644 index 00000000..aeb4c166 --- /dev/null +++ b/booking/Settings/ModuleConfigurationElement.cs @@ -0,0 +1,62 @@ +// +// ModuleConfigurationElement.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Configuration; + +namespace Yavsc.Settings +{ + /// + /// Module configuration element. (NOTUSED) + /// + public class ModuleConfigurationElement : ConfigurationElement + { + /// + /// Initializes a new instance of the class. + /// + public ModuleConfigurationElement () + { + } + /// + /// Gets or sets the name of the module. + /// + /// The name. + [ConfigurationProperty("name", IsKey=true, IsRequired=true)] + public string Name { + get { + return (string) base ["name"]; + } + set { base ["name"] = value; } + } + /// + /// Gets or sets the name of the class. + /// + /// The name of the class. + [ConfigurationProperty("name", IsKey=true, IsRequired=true)] + public string ClassName { + get { + return (string) base ["classname"]; + } + set { base ["classname"] = value; } + } + + } +} + diff --git a/booking/Settings/ModuleConfigurationElementCollection.cs b/booking/Settings/ModuleConfigurationElementCollection.cs new file mode 100644 index 00000000..ec33c853 --- /dev/null +++ b/booking/Settings/ModuleConfigurationElementCollection.cs @@ -0,0 +1,60 @@ +// +// ModuleConfigurationElementCollection.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Configuration; + +namespace Yavsc.Settings +{ + /// + /// Module configuration element collection. + /// + public class ModuleConfigurationElementCollection : ConfigurationElementCollection + { + /// + /// Initializes a new instance of the class. + /// + public ModuleConfigurationElementCollection () + { + } + + #region implemented abstract members of ConfigurationElementCollection + /// + /// Creates the new element. + /// + /// The new element. + protected override ConfigurationElement CreateNewElement () + { + throw new NotImplementedException (); + } + /// + /// Gets the element key. + /// + /// The element key. + /// Element. + protected override object GetElementKey (ConfigurationElement element) + { + throw new NotImplementedException (); + } + + #endregion + } +} + diff --git a/booking/Settings/ModulesConfigurationSection.cs b/booking/Settings/ModulesConfigurationSection.cs new file mode 100644 index 00000000..221739d8 --- /dev/null +++ b/booking/Settings/ModulesConfigurationSection.cs @@ -0,0 +1,40 @@ +// +// ModulesConfigurationSection.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Configuration; + +namespace Yavsc.Settings +{ + /// + /// Modules configuration section. + /// This class is not yet used ... + /// + public class ModulesConfigurationSection : ConfigurationSection + { + /// + /// Initializes a new instance of the class. + /// + public ModulesConfigurationSection () + { + } + } +} + diff --git a/booking/Settings/ThanksConfigurationCollection.cs b/booking/Settings/ThanksConfigurationCollection.cs new file mode 100644 index 00000000..426969bf --- /dev/null +++ b/booking/Settings/ThanksConfigurationCollection.cs @@ -0,0 +1,41 @@ +using System; +using System.Configuration; + +namespace Yavsc +{ + /// + /// Thanks configuration collection. + /// Imlements the configuration, + /// providing the thanks collection + /// + public class ThanksConfigurationCollection : ConfigurationElementCollection + { + /// + /// Gets the element key. + /// + /// The element key. + /// Element. + protected override object GetElementKey (ConfigurationElement element) + { + return ((ThanksConfigurationElement) element).Name; + } + /// + /// Creates the new element. + /// + /// The new element. + protected override ConfigurationElement CreateNewElement () + { + return new ThanksConfigurationElement(); + } + /// + /// Gets the element. + /// + /// The element. + /// Name. + public ThanksConfigurationElement GetElement (string name) + { + return this.BaseGet(name) as ThanksConfigurationElement; + } + } +} + diff --git a/booking/Settings/ThanksConfigurationElement.cs b/booking/Settings/ThanksConfigurationElement.cs new file mode 100644 index 00000000..44b8e883 --- /dev/null +++ b/booking/Settings/ThanksConfigurationElement.cs @@ -0,0 +1,61 @@ +using System; +using System.Configuration; + +namespace Yavsc +{ + /// + /// Thanks configuration element. + /// + public class ThanksConfigurationElement : ConfigurationElement + { + /// + /// Gets or sets the name. + /// + /// The name. + [ConfigurationProperty("name", IsKey=true, IsRequired=true)] + public string Name { + get { + return (string) base ["name"]; + } + set { base ["name"] = value; } + } + /// + /// Gets or sets the URL. + /// + /// The URL. + [ConfigurationProperty("url")] + public string Url { + get { + return (string) base ["url"]; + } + set { base ["url"] = value; } + } + /// + /// Gets or sets the image. + /// + /// The image. + [ConfigurationProperty("image")] + public string Image { + get { + return (string) base ["image"]; + } + set { base ["image"] = value; } + } + + /// + /// Gets or sets the display. + /// + /// + /// The displaied text when no image is provided and we + /// don't want use the name attribute. + /// + [ConfigurationProperty("display")] + public string Display { + get { + return (string) base ["display"]; + } + set { base ["display"] = value; } + } + } +} + diff --git a/booking/Settings/ThanksConfigurationSection.cs b/booking/Settings/ThanksConfigurationSection.cs new file mode 100644 index 00000000..7e205f72 --- /dev/null +++ b/booking/Settings/ThanksConfigurationSection.cs @@ -0,0 +1,52 @@ +using System; +using System.Configuration; + +namespace Yavsc +{ + /// + /// Thanks configuration section. + /// + public class ThanksConfigurationSection : ConfigurationSection + { + /// + /// Gets or sets to. + /// + /// To. + [ConfigurationProperty("to")] + public ThanksConfigurationCollection To { + get { + return (ThanksConfigurationCollection) this["to"]; + } + set { + this ["to"] = value; + } + } + /// + /// Gets or sets the html class. + /// + /// The html class. + [ConfigurationProperty("html_class")] + public string HtmlClass { + get { + return (string)this ["html_class"]; + } + set { + this ["html_class"] = value; + } + } + /// + /// Gets or sets the title format. + /// + /// The title format. + [ConfigurationProperty("title_format")] + public string TitleFormat { + get { + return (string)this ["title_format"]; + } + set { + this ["title_format"] = value; + } + } + } +} + diff --git a/booking/ValidateAjaxAttribute.cs b/booking/ValidateAjaxAttribute.cs new file mode 100644 index 00000000..391b3d4d --- /dev/null +++ b/booking/ValidateAjaxAttribute.cs @@ -0,0 +1,72 @@ +// +// ValidateAjaxAttribute.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http.Filters; +using System.Web.Http.ModelBinding; + +namespace Yavsc +{ + /// + /// Validate ajax attribute. + /// + public class ValidateAjaxAttribute : ActionFilterAttribute + { + /// + /// Gets the error model object. + /// + /// The error model object. + /// Model state. + public static object GetErrorModelObject(ModelStateDictionary modelState) { + var errorModel = + from x in modelState.Keys + where modelState[x].Errors.Count > 0 + select new + { + // FIXME why not directly underscores? + key = x.Replace(".","_"), + errors = modelState[x].Errors. + Select(y => y.ErrorMessage). + ToArray() + }; + return errorModel; + + } + /// + /// Raises the action executed event. + /// + /// Action executed context. + public override void OnActionExecuted (HttpActionExecutedContext actionExecutedContext) + { + var modelState = actionExecutedContext.ActionContext.ModelState; + + if (!modelState.IsValid) + { + actionExecutedContext.Response = + actionExecutedContext.Request.CreateResponse (System.Net.HttpStatusCode.BadRequest, + ValidateAjaxAttribute.GetErrorModelObject (modelState)); + } + } + } +} + diff --git a/booking/Views/Account/ChangePassword.aspx b/booking/Views/Account/ChangePassword.aspx new file mode 100644 index 00000000..fd46b38b --- /dev/null +++ b/booking/Views/Account/ChangePassword.aspx @@ -0,0 +1,22 @@ +<%@ Page Title="Change your Password" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + +<%= Html.ValidationSummary("Modification de mot de passe") %> +<% using(Html.BeginForm("ChangePassword", "Account")) { %> + +<%= Html.TextBox( "UserName" ) %> +<%= Html.ValidationMessage("UserName", "*") %>
+ + <%= Html.Password( "OldPassword" ) %> +<%= Html.ValidationMessage("OldPassword", "*") %>
+ + <%= Html.Password( "NewPassword" ) %> +<%= Html.ValidationMessage("NewPassword", "*") %>
+ + <%= Html.Password( "ConfirmPassword" ) %> +<%= Html.ValidationMessage("ConfirmPassword", "*") %> + +<% } %> + +
+ + diff --git a/booking/Views/Account/ChangePasswordSuccess.aspx b/booking/Views/Account/ChangePasswordSuccess.aspx new file mode 100644 index 00000000..ce22f4fd --- /dev/null +++ b/booking/Views/Account/ChangePasswordSuccess.aspx @@ -0,0 +1,6 @@ +<%@ Page Title="Successfully changed your password" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + +
+<%= Html.ActionLink("Register","Register")%>
+
<%= Html.ActionLink("ChangePassword","ChangePassword")%>
+
diff --git a/booking/Views/Account/Circles.aspx b/booking/Views/Account/Circles.aspx new file mode 100644 index 00000000..e1f46398 --- /dev/null +++ b/booking/Views/Account/Circles.aspx @@ -0,0 +1,74 @@ +<%@ Page Title="Circles" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> +<%@ Register Assembly="Yavsc.WebControls" TagPrefix="yavsc" Namespace="Yavsc.WebControls" %> + + + + + + + + + + + + + +<% int lc=0; + foreach (var ci in (IEnumerable) ViewData["Circles"]) { lc++; %> +row" id="c_<%=ci.Id%>"> + + + +<% } %> + +
<%=Html.Translate("Title")%> +
<%=ci.Title%>" + class="btnremovecircle actionlink" cid="<%=ci.Id%>"/> +
+ + + +
+ + + + + diff --git a/booking/Views/Account/Index.aspx b/booking/Views/Account/Index.aspx new file mode 100644 index 00000000..1d76ed12 --- /dev/null +++ b/booking/Views/Account/Index.aspx @@ -0,0 +1,4 @@ +<%@ Page Title="Comptes utilisateur" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + +Pas de contenu :-( + diff --git a/booking/Views/Account/Login.aspx b/booking/Views/Account/Login.aspx new file mode 100644 index 00000000..54990dd6 --- /dev/null +++ b/booking/Views/Account/Login.aspx @@ -0,0 +1,32 @@ +<%@ Page Title="Login" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/NoLogin.master" %> + +
+<%= Html.ValidationSummary("Ouverture de session") %> +<% using(Html.BeginForm("Login", "Account")) %> +<% { %> +<%= Html.LabelFor(model => model.UserName) %> +<%= Html.TextBox( "UserName" ) %> +<%= Html.ValidationMessage("UserName", "*") %>
+ +<%= Html.LabelFor(model => model.Password) %> +<%= Html.Password( "Password" ) %> +<%= Html.ValidationMessage("Password", "*") %>
+ +<%= Html.LabelFor(model => model.RememberMe) %> +<%= Html.CheckBox("RememberMe") %> +<%= Html.ValidationMessage("RememberMe", "") %>
+<%= Html.Hidden("returnUrl",ViewData["returnUrl"]) %> +<%= Html.AntiForgeryToken() %> + + +<% } %>
+
+<%= Html.ActionLink("S'enregistrer","GetRegister",new {returnUrl=ViewData["returnUrl"]}, new { @class="actionlink" }) %> +
+ +
\ No newline at end of file diff --git a/booking/Views/Account/Profile.aspx b/booking/Views/Account/Profile.aspx new file mode 100644 index 00000000..b6ba4bae --- /dev/null +++ b/booking/Views/Account/Profile.aspx @@ -0,0 +1,140 @@ +<%@ Page Title="Profile_edition" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + +<% Title = ViewData["UserName"] + " : " +Html.Translate("Profile_edition"); %> + + + + + <%= Html.ValidationSummary() %> +<% using(Html.BeginForm("Profile", "Account", FormMethod.Post, new { enctype = "multipart/form-data" })) %> +<% { %> + + <%= Html.Hidden("UserName",ViewData["ProfileUserName"]) %> + +
Informations publiques + + +<%= Html.LabelFor(model => model.NewUserName) %> : +<%= Html.TextBox("NewUserName") %> +<%= Html.ValidationMessage("NewUserName", "*") %> +
+ +<%= Html.LabelFor(model => model.WebSite) %> : +<%= Html.TextBox("WebSite") %> +<%= Html.ValidationMessage("WebSite", "*") %> +
+ +Avatar : avatar + + +<%= Html.ValidationMessage("AvatarFile", "*") %> + +
+
Informations administratives +<%= Html.LabelFor(model => model.Name) %> : +<%= Html.TextBox("Name") %> +<%= Html.ValidationMessage("Name", "*") %> +
+
Blog +
+<%= Html.LabelFor(model => model.BlogVisible) %> : +<%= Html.CheckBox("BlogVisible") %> +<%= Html.ValidationMessage("BlogVisible", "*") %> +
+ +<%= Html.LabelFor(model => model.BlogTitle) %> : +<%= Html.TextBox("BlogTitle") %> +<%= Html.ValidationMessage("BlogTitle", "*") %> +
+
+ +
Contact +
+<%= Html.LabelFor(model => model.Phone) %> +<%= Html.TextBox("Phone") %> +<%= Html.ValidationMessage("Phone", "*") %> +
+<%= Html.LabelFor(model => model.Mobile) %> +<%= Html.TextBox("Mobile") %> +<%= Html.ValidationMessage("Mobile", "*") %> +
+<%= Html.LabelFor(model => model.Address) %> +<%= Html.TextBox("Address") %> +<%= Html.ValidationMessage("Address", "*") %> +
+<%= Html.LabelFor(model => model.CityAndState) %> +<%= Html.TextBox("CityAndState") %> +<%= Html.ValidationMessage("CityAndState", "*") %> +
+<%= Html.LabelFor(model => model.ZipCode) %> +<%= Html.TextBox("ZipCode") %> +<%= Html.ValidationMessage("ZipCode", "*") %> +
+<%= Html.LabelFor(model => model.Country) %> +<%= Html.TextBox("Country") %> +<%= Html.ValidationMessage("Country", "*") %> +
+
+
Disponibilité +
+ <%= Html.LabelFor(model => model.GoogleCalendar) %> : + + <%= Html.Encode(Model.GoogleCalendar) %> + <%= Html.ActionLink("Choisir l'agenda","ChooseCalendar","Google",new { returnUrl= Request.Url.AbsolutePath }, new { @class="actionlink" }) %> +
+
Informations de facturation + +
+<%= Html.LabelFor(model => model.BankCode) %> : +<%= Html.TextBox("BankCode") %> +<%= Html.ValidationMessage("BankCode", "*") %> +
+ +<%= Html.LabelFor(model => model.WicketCode) %> : +<%= Html.TextBox("WicketCode") %> +<%= Html.ValidationMessage("WicketCode", "*") %> +
+ +<%= Html.LabelFor(model => model.AccountNumber) %> : +<%= Html.TextBox("AccountNumber") %> +<%= Html.ValidationMessage("AccountNumber", "*") %> +
+<%= Html.LabelFor(model => model.BankedKey) %> : +<%= Html.TextBox("BankedKey") %> +<%= Html.ValidationMessage("BankedKey", "*") %> +
+<%= Html.LabelFor(model => model.BIC) %> : +<%= Html.TextBox("BIC") %> +<%= Html.ValidationMessage("BIC", "*") %> +
+<%= Html.LabelFor(model => model.IBAN) %> : +<%= Html.TextBox("IBAN") %> +<%= Html.ValidationMessage("IBAN", "*") %> +
+
+ + +<% } %> + + + +
+ diff --git a/booking/Views/Account/Register.ascx b/booking/Views/Account/Register.ascx new file mode 100644 index 00000000..d0d0f61c --- /dev/null +++ b/booking/Views/Account/Register.ascx @@ -0,0 +1,73 @@ +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> +<%= Html.ValidationSummary() %> +<% using(Html.BeginForm("Register")) %> +<% { %> +

Nouvel utilisateur

+* + + + + + + + + + + + + + + + + + + + +
+<%= Html.LabelFor(model => model.Name) %> + +<%= Html.TextBox( "Name" ) %> +<%= Html.ValidationMessage("Name", "*", new { @id="Err_ur_Name", @class="error" }) %>
+<%= Html.LabelFor(model => model.UserName) %> + +<%= Html.TextBox( "UserName" ) %> +<%= Html.ValidationMessage("UserName", "*", new { @id="Err_ur_UserName", @class="error" }) %>
+<%= Html.LabelFor(model => model.Password) %> + +<%= Html.Password( "Password" ) %> +<%= Html.ValidationMessage("Password", "*", new { @id="Err_ur_Password", @class="error" }) %> +
+<%= Html.LabelFor(model => model.Email) %> + +<%= Html.TextBox( "Email" ) %> +<%= Html.ValidationMessage("Email", "*", new { @id="Err_ur_Email", @class="error" }) %> +
+<%= Html.LabelFor(model => model.Address) %> + +<%= Html.TextBox( "Address" ) %> +<%= Html.ValidationMessage("Address", "*", new { @id="Err_ur_Address", @class="error" }) %>
+<%= Html.LabelFor(model => model.CityAndState) %> + +<%= Html.TextBox( "CityAndState" ) %> +<%= Html.ValidationMessage("CityAndState", "*", new { @id="Err_ur_CityAndState", @class="error" }) %> + + +
+<%= Html.LabelFor(model => model.ZipCode) %> + +<%= Html.TextBox( "ZipCode" ) %> +<%= Html.ValidationMessage("ZipCode", "*", new { @id="Err_ur_ZipCode", @class="error" }) %>
+<%= Html.LabelFor(model => model.Phone) %> + +<%= Html.TextBox( "Phone" ) %> +<%= Html.ValidationMessage("Phone", "*", new { @id="Err_ur_Phone", @class="error" }) %>
+<%= Html.LabelFor(model => model.Mobile) %> + +<%= Html.TextBox( "Mobile" ) %> +<%= Html.ValidationMessage("Mobile", "*", new { @id="Err_ur_Mobile", @class="error" }) %>
+ +<% } %> + + + + diff --git a/booking/Views/Account/Register.aspx b/booking/Views/Account/Register.aspx new file mode 100644 index 00000000..90d45436 --- /dev/null +++ b/booking/Views/Account/Register.aspx @@ -0,0 +1,39 @@ +<%@ Page Title="Register" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +<%= Html.ValidationSummary() %> +<% using(Html.BeginForm("Register", "Account")) %> +<% { %> + + + + + +
+<%= Html.LabelFor(model => model.UserName) %> + +<%= Html.TextBox( "UserName" ) %> +<%= Html.ValidationMessage("UserName", "*") %>
+<%= Html.LabelFor(model => model.Password) %> + +<%= Html.Password( "Password" ) %> +<%= Html.ValidationMessage("Password", "*") %> +
+<%= Html.LabelFor(model => model.ConfirmPassword) %> + + <%= Html.Password( "ConfirmPassword" ) %> +<%= Html.ValidationMessage("ConfirmPassword", "*") %> +
+<%= Html.LabelFor(model => model.Email) %> + +<%= Html.TextBox( "Email" ) %> +<%= Html.ValidationMessage("Email", "*") %> +
+
+<%= Html.Hidden("returnUrl",ViewData["returnUrl"]) %> + + +<% } %> +
+ + diff --git a/booking/Views/Account/RegistrationPending.aspx b/booking/Views/Account/RegistrationPending.aspx new file mode 100644 index 00000000..f5e01246 --- /dev/null +++ b/booking/Views/Account/RegistrationPending.aspx @@ -0,0 +1,13 @@ +<%@ Page Title="Comptes utilisateur" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +Votre compte utilisateur +<%= Html.Encode(YavscHelpers.SiteName) %> +a été créé, un e-mail de validation de votre compte a été envoyé a l'adresse fournie:
+<<%= Html.Encode(ViewData["email"]) %>>.
+Vous devriez le recevoir rapidement.
+Pour valider votre compte, suivez le lien indiqué dans cet e-mail. + +
diff --git a/booking/Views/Account/ResetPassword.aspx b/booking/Views/Account/ResetPassword.aspx new file mode 100644 index 00000000..7970c614 --- /dev/null +++ b/booking/Views/Account/ResetPassword.aspx @@ -0,0 +1,23 @@ +<%@ Page Title="Reset your Password" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + +<%= Html.ValidationSummary("Modification de mot de passe") %> + +<% using(Html.BeginForm("ResetPassword", "Account")) { %> +Enter one of the following :
+
  • + +<%= Html.TextBox( "UserName" ) %> +<%= Html.ValidationMessage("UserName", "*") %>
  • +
  • + +<%= Html.TextBox( "Email" ) %> +<%= Html.ValidationMessage("Email", "*") %> +
  • +
+Then, hit the following button: +
+A message will be sent to you, containning a link that you'll can use to reset your password. +<% } %> +
+ + diff --git a/booking/Views/Account/Unregister.aspx b/booking/Views/Account/Unregister.aspx new file mode 100644 index 00000000..ba7e7541 --- /dev/null +++ b/booking/Views/Account/Unregister.aspx @@ -0,0 +1,12 @@ + +<%@ Page Title="Unregister" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +Warning: This will delete all of your data here, your profile, your posts and other data. +<% using(Html.BeginForm("Unregister", "Account")) { %> + + <%=Html.CheckBox("confirmed")%> + + <%= Html.Hidden("UserName") %> + <% } %> + diff --git a/booking/Views/Account/Validate.aspx b/booking/Views/Account/Validate.aspx new file mode 100644 index 00000000..a5975bd6 --- /dev/null +++ b/booking/Views/Account/Validate.aspx @@ -0,0 +1,2 @@ +<%@ Page Title="Comptes utilisateur - Validation" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + diff --git a/booking/Views/Admin/AddRole.aspx b/booking/Views/Admin/AddRole.aspx new file mode 100644 index 00000000..ded10055 --- /dev/null +++ b/booking/Views/Admin/AddRole.aspx @@ -0,0 +1,15 @@ +<%@ Page Title="Ajout d'un role" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + + +<%= Html.ValidationSummary() %> +<% using(Html.BeginForm()) + { %> +Nom du rôle : +<%= Html.TextBox( "RoleName" ) %> +<%= Html.ValidationMessage("RoleName", "*") %>
+ +<% } %> +<%= Html.Partial("AddMemberToRole")%> +
+ + diff --git a/booking/Views/Admin/AddUserToRole.ascx b/booking/Views/Admin/AddUserToRole.ascx new file mode 100644 index 00000000..0b69946e --- /dev/null +++ b/booking/Views/Admin/AddUserToRole.ascx @@ -0,0 +1,14 @@ +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> +<%= Html.ValidationSummary() %> +<% using(Ajax.BeginForm("AddUserToRole", "Admin", new AjaxOptions() { UpdateTargetId = "roleaddedresult" })) + { %> +
+
+ +<%= Html.ValidationMessage("UserName", "*") %>
+ + +<%= Html.ValidationMessage("RoleName", "*") %>
+<%= Ajax.ActionLink("AddUserToRole", "AddUserToRole", new { RoleName = "Admin" } , new AjaxOptions() { UpdateTargetId = "roleaddedresult" }) %> +
+<% } %> \ No newline at end of file diff --git a/booking/Views/Admin/Admin.aspx b/booking/Views/Admin/Admin.aspx new file mode 100644 index 00000000..aec1c763 --- /dev/null +++ b/booking/Views/Admin/Admin.aspx @@ -0,0 +1,31 @@ +<%@ Page Title="Liste des administrateurs" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + + +
+ + +<% foreach (string u in (string[])ViewData["admins"]) { %> + + +<% } %> +
+<%= u %> <%= Html.ActionLink("Remove","RemoveFromRole",new { username = u, rolename="Admin", returnUrl = Request.Url.PathAndQuery })%> +
+
+
+

Ajout d'un administrateur +

+

<%= Html.ValidationSummary() %>

+ +<% using ( Html.BeginForm("Admin", "Admin") ) { %> + +<%= Html.LabelFor(model => model.UserName) %> : +<%= Html.DropDownListFor(model => model.UserName, (List)ViewData["useritems"] ) %> +<%= Html.ValidationMessage("UserName", "*") %> + + + <% } %> + +
+
+ diff --git a/booking/Views/Admin/BackupCreated.aspx b/booking/Views/Admin/BackupCreated.aspx new file mode 100644 index 00000000..88e00d26 --- /dev/null +++ b/booking/Views/Admin/BackupCreated.aspx @@ -0,0 +1,7 @@ +<%@ Page Title="Backup created" Language="C#" MasterPageFile="~/Models/AppAdmin.master" Inherits="System.Web.Mvc.ViewPage" %> + +

Error message

<%= Html.Encode(Model.Error) %>
+

Message

<%= Html.Encode(Model.Message) %>
+

File name

<%= Html.Encode(Model.FileName) %>
+

Exit Code

<%= Html.Encode(Model.ExitCode) %>
+
diff --git a/booking/Views/Admin/Backups.aspx b/booking/Views/Admin/Backups.aspx new file mode 100644 index 00000000..b74298a6 --- /dev/null +++ b/booking/Views/Admin/Backups.aspx @@ -0,0 +1,6 @@ +<%@ Page Language="C#" MasterPageFile="~/Models/AppAdmin.master" Inherits="System.Web.Mvc.ViewPage" %> + + +<%=Html.ActionLink("Create a database backup", "CreateBackup")%>
+<%=Html.ActionLink("Restaurations", "Restore")%>
+
diff --git a/booking/Views/Admin/CreateBackup.aspx b/booking/Views/Admin/CreateBackup.aspx new file mode 100644 index 00000000..04cc7b2f --- /dev/null +++ b/booking/Views/Admin/CreateBackup.aspx @@ -0,0 +1,23 @@ +<%@ Page Title="Backup creation" Language="C#" MasterPageFile="~/Models/AppAdmin.master" Inherits="System.Web.Mvc.ViewPage" %> + +<%= Html.ValidationSummary("CreateBackup","Admin") %> +<% using (Html.BeginForm("CreateBackup")) { %> + +<%= Html.LabelFor(model => model.Host) %>: +<%= Html.TextBox( "Host" ) %> +<%= Html.ValidationMessage("Host", "*") %>
+<%= Html.LabelFor(model => model.Port) %>: +<%= Html.TextBox( "Port" ) %> +<%= Html.ValidationMessage("Port", "*") %>
+<%= Html.LabelFor(model => model.DbName) %>: +<%= Html.TextBox( "DbName" ) %> +<%= Html.ValidationMessage("DbName", "*") %>
+<%= Html.LabelFor(model => model.DbUser) %>: +<%= Html.TextBox( "DbUser" ) %> +<%= Html.ValidationMessage("DbUser", "*") %>
+<%= Html.LabelFor(model => model.Password) %>: +<%= Html.Password( "Password" ) %> +<%= Html.ValidationMessage("Password", "*") %>
+ +<% } %> +
diff --git a/booking/Views/Admin/Created.aspx b/booking/Views/Admin/Created.aspx new file mode 100644 index 00000000..33bdcedd --- /dev/null +++ b/booking/Views/Admin/Created.aspx @@ -0,0 +1,12 @@ +<%@ Page Title="Db init" Language="C#" MasterPageFile="~/Models/NoLogin.master" Inherits="System.Web.Mvc.ViewPage" %> + +

Initialisation de la base de données

+

Error message

<%= Html.Encode(Model.Error) %>
+

Message

<%= Html.Encode(Model.Message) %>
+

Exit Code

<%= Html.Encode(Model.ExitCode) %>
+
Acces à la base de donnée +<%= Html.Encode(ViewData["DbName"]) %>
+<%= Html.Encode(ViewData["DbUser"]) %>
+<%= Html.Encode(ViewData["Host"]) %>
+
+
diff --git a/booking/Views/Admin/Index.aspx b/booking/Views/Admin/Index.aspx new file mode 100644 index 00000000..62a71ac0 --- /dev/null +++ b/booking/Views/Admin/Index.aspx @@ -0,0 +1,4 @@ +<%@ Page Title="Administration" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + + + diff --git a/booking/Views/Admin/InitDb.aspx b/booking/Views/Admin/InitDb.aspx new file mode 100644 index 00000000..47d4431b --- /dev/null +++ b/booking/Views/Admin/InitDb.aspx @@ -0,0 +1,23 @@ +<%@ Page Title="Init db" Language="C#" MasterPageFile="~/Models/NoLogin.master" Inherits="System.Web.Mvc.ViewPage" %> + +<%= Html.ValidationSummary("Init a new data base") %> +<% using (Html.BeginForm("InitDb","Admin")) { %> +<%= Html.LabelFor(model => model.Host) %>: +<%= Html.TextBox( "Host" ) %> +<%= Html.ValidationMessage("Host", "*") %>
+<%= Html.LabelFor(model => model.Port) %>: +<%= Html.TextBox( "Port" ) %> +<%= Html.ValidationMessage("Port", "*") %>
+<%= Html.LabelFor(model => model.DbName) %>: +<%= Html.TextBox( "DbName" ) %> +<%= Html.ValidationMessage("DbName", "*") %>
+<%= Html.LabelFor(model => model.DbUser) %>: +<%= Html.TextBox( "DbUser" ) %> +<%= Html.ValidationMessage("DbUser", "*") %>
+<%= Html.LabelFor(model => model.Password) %>: +<%= Html.Password( "Password" ) %> +<%= Html.ValidationMessage("Password", "*") %>
+ + +<% } %> +
diff --git a/booking/Views/Admin/RemoveRole.aspx b/booking/Views/Admin/RemoveRole.aspx new file mode 100644 index 00000000..73c1f659 --- /dev/null +++ b/booking/Views/Admin/RemoveRole.aspx @@ -0,0 +1,17 @@ +<%@ Page Title="User removal" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + +
+<%= Html.ValidationSummary() %> +<% using ( Html.BeginForm("RemoveRole","Admin") ) { %> +Supprimer le rôle +<%= Html.Encode( ViewData["roletoremove"] ) %> ? +
+"/> + + +<% } %> +
+ +
+ + diff --git a/booking/Views/Admin/RemoveUser.aspx b/booking/Views/Admin/RemoveUser.aspx new file mode 100644 index 00000000..f810e4f9 --- /dev/null +++ b/booking/Views/Admin/RemoveUser.aspx @@ -0,0 +1,18 @@ +<%@ Page Title="User removal" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + +
+ +<%= Html.ValidationSummary() %> +<% using ( Html.BeginForm("RemoveUser","Admin") ) { %> +Supprimer l'utilisateur +<%= Html.Encode( ViewData["usertoremove"] ) %> ? +
+"/> + + +<% } %> +
+ +
+ + diff --git a/booking/Views/Admin/Restore.aspx b/booking/Views/Admin/Restore.aspx new file mode 100644 index 00000000..ee2d34ca --- /dev/null +++ b/booking/Views/Admin/Restore.aspx @@ -0,0 +1,37 @@ +<%@ Page Title="Restore" Language="C#" MasterPageFile="~/Models/AppAdmin.master" Inherits="System.Web.Mvc.ViewPage" %> + +<%= Html.ValidationSummary("Restore a database backup") %> +<% using (Html.BeginForm("Restore","Admin")) { %> + +<% string [] bcfiles = (string[]) ViewData["Backups"]; %> + + +<%= Html.CheckBox("dataOnly")%> + +<%= Html.LabelFor(model => model.Host) %>: +<%= Html.TextBox( "Host" ) %> +<%= Html.ValidationMessage("Host", "*") %>
+<%= Html.LabelFor(model => model.Port) %>: +<%= Html.TextBox( "Port" ) %> +<%= Html.ValidationMessage("Port", "*") %>
+<%= Html.LabelFor(model => model.DbName) %>: +<%= Html.TextBox( "DbName" ) %> +<%= Html.ValidationMessage("DbName", "*") %>
+<%= Html.LabelFor(model => model.DbUser) %>: +<%= Html.TextBox( "DbUser" ) %> +<%= Html.ValidationMessage("DbUser", "*") %>
+<%= Html.LabelFor(model => model.Password) %>: +<%= Html.Password( "Password" ) %> +<%= Html.ValidationMessage("Password", "*") %>
+ + +<% } %> +
diff --git a/booking/Views/Admin/Restored.aspx b/booking/Views/Admin/Restored.aspx new file mode 100644 index 00000000..877a5d74 --- /dev/null +++ b/booking/Views/Admin/Restored.aspx @@ -0,0 +1,8 @@ +<%@ Page Language="C#" MasterPageFile="~/Models/AppAdmin.master" Inherits="System.Web.Mvc.ViewPage" %> + +

<%=Html.Encode(ViewData["BackupName"])%> Restauration

+

Error message

<%= Html.Encode(Model.Error) %>
+

Message

<%= Html.Encode(Model.Message) %>
+

Exit Code

<%= Html.Encode(Model.ExitCode) %>
+ +
diff --git a/booking/Views/Admin/RoleList.aspx b/booking/Views/Admin/RoleList.aspx new file mode 100644 index 00000000..8706a1dc --- /dev/null +++ b/booking/Views/Admin/RoleList.aspx @@ -0,0 +1,20 @@ +<%@ Page Title="Liste des rôles" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + + + +Roles: +
    + <%foreach (string rolename in (string[]) Model){ %> + +
  • <%=Html.ActionLink(rolename,"UsersInRole", new { rolename = rolename }, new { @class="actionlink" } )%> <% if (Roles.IsUserInRole("Admin")) { %> + <%= Html.ActionLink("Supprimer","RemoveRole", new { rolename = rolename }, new { @class="actionlink" } ) %> +<% } %>
  • + <% } %> + +
+ <% if (Roles.IsUserInRole("Admin")) { %> + <%= Html.ActionLink("Ajouter un rôle","AddRole", null, new { @class="actionlink" } ) %> +<% } %> +
+ + diff --git a/booking/Views/Admin/UserList.aspx b/booking/Views/Admin/UserList.aspx new file mode 100644 index 00000000..c329ef09 --- /dev/null +++ b/booking/Views/Admin/UserList.aspx @@ -0,0 +1,16 @@ +<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + + <% Page.Title = LocalizedText.User_List; %> + + +
    + <%foreach (MembershipUser user in Model){ %> +
  • <%=user.UserName%> (created <%=user.CreationDate.ToString("D")%>) <%=user.Email%> <%=(user.IsApproved)?"":"("+LocalizedText.Not_Approuved+")"%> <%=user.IsOnline?LocalizedText.Online:LocalizedText.Offline%> +<% if (Roles.IsUserInRole("Admin")) { %> + <%= Html.ActionLink(LocalizedText.Remove,"RemoveUser", new { username = user.UserName }, new { @class="actionlink" } ) %> +<% } %> +
  • <% }%> +
+ +
+ diff --git a/booking/Views/Admin/UsersInRole.aspx b/booking/Views/Admin/UsersInRole.aspx new file mode 100644 index 00000000..bc48b6ed --- /dev/null +++ b/booking/Views/Admin/UsersInRole.aspx @@ -0,0 +1,21 @@ +<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" Title="UsersInRoleOf" MasterPageFile="~/Models/AppAdmin.master" %> + +<% ViewState["orgtitle"] = "Liste des utilisateurs assumant le rôle " + Html.Encode(ViewData["RoleName"]); %> +<% Page.Title = "Liste des utilisateurs assumant le rôle " + Html.Encode(ViewData["RoleName"]) + " - " + YavscHelpers.SiteName; %> + + + +

"> +<%=ViewState["orgtitle"]%> +- "><%= YavscHelpers.SiteName %> +

+
+ + <%foreach (string username in (string[]) ViewData["UsersInRole"]){ %> + <%= username %> + <% } %> + + + +<%= Html.Partial("AddUserToRole") %> + \ No newline at end of file diff --git a/booking/Views/BackOffice/Index.aspx b/booking/Views/BackOffice/Index.aspx new file mode 100644 index 00000000..66563f3f --- /dev/null +++ b/booking/Views/BackOffice/Index.aspx @@ -0,0 +1,9 @@ +<%@ Page Title="Back office" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + + + +
  • + <%= Html.ActionLink("Catalog","Catalog","FrontOffice" ) %> +
+
diff --git a/web/Views/Blogs/ChooseMedia.aspx b/booking/Views/Blogs/ChooseMedia.aspx similarity index 100% rename from web/Views/Blogs/ChooseMedia.aspx rename to booking/Views/Blogs/ChooseMedia.aspx diff --git a/booking/Views/Blogs/Edit.aspx b/booking/Views/Blogs/Edit.aspx new file mode 100644 index 00000000..b6513414 --- /dev/null +++ b/booking/Views/Blogs/Edit.aspx @@ -0,0 +1,243 @@ +<%@ Page Title="Bill_edition" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> +<%@ Register Assembly="Yavsc.WebControls" TagPrefix="yavsc" Namespace="Yavsc.WebControls" %> + + + + + + + + + + + + + +<% using(Html.BeginForm("ValidateEdit","Blogs")) { %> +
+Contrôle d'accès au Billet +<%= Html.LabelFor(model => model.Visible) %> : <%= Html.CheckBox( "Visible" ) %> +Note: Si un ou plusieurs cercles sont séléctionnés ici, + le billet ne sera visible qu'aux membres de ces cercles. +<%= Html.ValidationMessage("Visible", "*") %> +<%= Html.LabelFor(model => model.AllowedCircles) %> +<%= Html.ListBox("AllowedCircles") %> +<%= Html.ValidationMessage("AllowedCircles", "*") %> +
+ +<%=Html.Translate("View_source")%> + + + +<% } %> + +
+
+Attacher des fichiers + + + +
+
+ + +photo + + +

<%=Html.Markdown(Model.Title)%>

+
+<%=Html.Markdown(Model.Content,"/bfiles/"+Model.Id+"/")%> +
+

Fichiers attachées

+ +<%= Html.FileList("~/bfiles/"+Model.Id) %> + +
+ + + + + + + + + +
\ No newline at end of file diff --git a/booking/Views/Blogs/Index.aspx b/booking/Views/Blogs/Index.aspx new file mode 100644 index 00000000..67e0ad52 --- /dev/null +++ b/booking/Views/Blogs/Index.aspx @@ -0,0 +1,18 @@ +<%@ Page Title="Articles" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" EnableTheming="True" StylesheetTheme="dark" %> +<%@ Register Assembly="Yavsc.WebControls" TagPrefix="yavsc" Namespace="Yavsc.WebControls" %> + + +
+<% foreach (var g in Model.GroupByTitle()) { %> +
+

" class="usertitleref"><%=Html.Encode(g.Key)%>

+<% foreach (var p in g) { %> +
+

<%= Html.Markdown(p.Intro,"/bfiles/"+p.Id+"/") %>

+ <%= Html.Partial("PostActions",p)%> +
<% } %> +
+ <% } %> +
+ <%= Html.RenderPageLinks((int)ViewData["PageIndex"],(int)ViewData["PageSize"],(int)ViewData["ResultCount"])%> +
diff --git a/web/Views/Blogs/NotAuthorized.aspx b/booking/Views/Blogs/NotAuthorized.aspx similarity index 100% rename from web/Views/Blogs/NotAuthorized.aspx rename to booking/Views/Blogs/NotAuthorized.aspx diff --git a/booking/Views/Blogs/PostActions.ascx b/booking/Views/Blogs/PostActions.ascx new file mode 100644 index 00000000..befcdb55 --- /dev/null +++ b/booking/Views/Blogs/PostActions.ascx @@ -0,0 +1,26 @@ +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> + diff --git a/booking/Views/Blogs/RemovePost.aspx b/booking/Views/Blogs/RemovePost.aspx new file mode 100644 index 00000000..dbcfaa76 --- /dev/null +++ b/booking/Views/Blogs/RemovePost.aspx @@ -0,0 +1,20 @@ +<%@ Page Title="Bill_removal" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + + +<%= Html.ValidationSummary() %> +<% using (Html.BeginForm("RemovePost","Blogs",new {postid=Model.Id})) { %> + +<%= Html.LabelFor(model => model.Title) %> : +<%= Html.TextBox( "Title" ) %> +<%= Html.ValidationMessage("Title", "*") %>
+Identifiant du post : <%= Model.Id %>
+<%= Html.Markdown(Model.Content) %> + +<%= Html.CheckBox( "confirm" ) %> +<%= Html.ValidationMessage("confirm", "*") %> +<%= Html.Hidden("returnUrl") %> + + <% } %> + +
diff --git a/booking/Views/Blogs/RemoveTitle.aspx b/booking/Views/Blogs/RemoveTitle.aspx new file mode 100644 index 00000000..2aeee4ff --- /dev/null +++ b/booking/Views/Blogs/RemoveTitle.aspx @@ -0,0 +1,22 @@ +<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + + +<%= Html.ValidationSummary() %> +<% using (Html.BeginForm("RemoveTitle","Blogs")) { %> + +<%= Html.LabelFor(model => model.Titles) %> : +<%= Html.TextBox( "Titles" ) %> +<%= Html.ValidationMessage("Titles", "*") %>
+<%= Html.Hidden("Author") %> + +<%= Html.CheckBox( "confirm" ) %> +<%= Html.ValidationMessage("AgreeToRemove", "*") %> + +<%= Html.Hidden("returnUrl") %> + + <% } %> + +
+ + diff --git a/booking/Views/Blogs/TagControl.ascx b/booking/Views/Blogs/TagControl.ascx new file mode 100644 index 00000000..cbaca288 --- /dev/null +++ b/booking/Views/Blogs/TagControl.ascx @@ -0,0 +1,47 @@ +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> +

+<% if (Model.Tags != null) { +foreach ( var tagname in Model.Tags) { %> +<%=tagname%> <% +%><% } } %> +

+<% if (Membership.GetUser()!=null) { %> +<% if (Membership.GetUser().UserName==Model.Author || Roles.IsUserInRole("Admin")) +{ // grant all permissions: to choose a given set of tags, also create some new tags %> + +<%=Html.Translate("DoTag")%> + +
+
+Associer des tags au billet + + + + +"> +
+
+ +<% } %> +<% } %> diff --git a/booking/Views/Blogs/TagPanel.ascx b/booking/Views/Blogs/TagPanel.ascx new file mode 100644 index 00000000..1525e29f --- /dev/null +++ b/booking/Views/Blogs/TagPanel.ascx @@ -0,0 +1,12 @@ +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> +
+<%=Html.Encode(Model.Name)%> +<% foreach (var t in Model.Titles) { %> +"> +placard +<%= Html.Markdown(t.Title,"/bfiles/"+t.Id+"/")%> +
+

<%= Html.Markdown(t.Intro,"/bfiles/"+t.Id+"/") %>

+
+<% } %> +
\ No newline at end of file diff --git a/booking/Views/Blogs/Title.aspx b/booking/Views/Blogs/Title.aspx new file mode 100644 index 00000000..4fb1912f --- /dev/null +++ b/booking/Views/Blogs/Title.aspx @@ -0,0 +1,34 @@ +<%@ Page Title="Titre" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master"%> +<%@ Register Assembly="Yavsc.WebControls" TagPrefix="yavsc" Namespace="Yavsc.WebControls" %> + +<% Title = Model.Title + " - " + YavscHelpers.SiteName; %> + + + +

+<%=Html.ActionLink(Model.Title, "Title", new{title=Model.Title}, null)%> +- "><%= YavscHelpers.SiteName %> +

+
+ + +<% foreach (BlogEntry e in this.Model) { %> +
+<% if (e.Photo!=null) { %><% } %> +<%= Html.Markdown(e.Content,"/bfiles/"+e.Id+"/") %> +<%= Html.Partial("PostActions",e)%> +
+<% } %> +<% +if (((int) ViewData["RecordCount"]) > ((int) ViewData["PageSize"])) { + rp1.ResultCount = (int) ViewData["RecordCount"]; + rp1.PageIndex = (int) ViewData["PageIndex"]; + rp1.PageSize = (int) ViewData["PageSize"]; +%> +<% } %> +
diff --git a/booking/Views/Blogs/TitleNotFound.aspx b/booking/Views/Blogs/TitleNotFound.aspx new file mode 100644 index 00000000..1162746d --- /dev/null +++ b/booking/Views/Blogs/TitleNotFound.aspx @@ -0,0 +1,5 @@ +<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master"%> + +Pas d'article trouvé ici: <<%= Html.Encode(ViewData["BlogUser"]) %>/<%= Html.Encode(ViewData["PostTitle"]) %>> +
<%= Html.ActionLink("Poster?","Post/", new { user = ViewData["BlogUser"], title = ViewData["PostTitle"]}, new { @class="actionlink" }) %> +
diff --git a/booking/Views/Blogs/UserPost.aspx b/booking/Views/Blogs/UserPost.aspx new file mode 100644 index 00000000..aea9c74d --- /dev/null +++ b/booking/Views/Blogs/UserPost.aspx @@ -0,0 +1,41 @@ +<%@ Page Title="Billet" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master"%> + +<% Title = Model.Title+ " - " + ViewData ["BlogTitle"] ; %> + + + +<% if (!string.IsNullOrEmpty((string)ViewData["Avatar"])) { %> +" id="avatar"> +" /> + +<% } %> +

+"> +<%=Model.Title%> + + - <%= Html.ActionLink((string)ViewData ["BlogTitle"] ,"UserPosts",new{user=Model.Author}, null) %> + - +"><%= YavscHelpers.SiteName %> +

+
+ + +<% foreach (var be in Model) { %> +
+<% if (be.Photo != null) { %> + <%=be.Title%> +<% } %> + +<%= Html.Markdown(be.Content,"/bfiles/"+be.Id+"/") %> + <%= Html.Partial("PostActions",be)%> + <% string username = Membership.GetUser()==null ? null : Membership.GetUser().UserName; %> + <% foreach (var c in (Comment[]) BlogManager.GetComments(be.Id)) { %> +
+" alt="<%=c.From%>"/> +<%= Html.Markdown(c.CommentText) %> + <% if (Model.Author == username || c.From == username ) { %> + <%= Html.ActionLink("Supprimer","RemoveComment", new { cmtid = c.Id } , new { @class="actionlink" })%> + <% } %> +
<% } %> +
<% } %> +
diff --git a/booking/Views/Blogs/UserPosts.aspx b/booking/Views/Blogs/UserPosts.aspx new file mode 100644 index 00000000..c22d1570 --- /dev/null +++ b/booking/Views/Blogs/UserPosts.aspx @@ -0,0 +1,48 @@ +<%@ Page Title="Blog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master"%> +<%@ Register Assembly="Yavsc.WebControls" TagPrefix="yavsc" Namespace="Yavsc.WebControls" %> + +<% Title = (string) ViewData ["BlogTitle"] ; %> + + + + +<% if (!string.IsNullOrEmpty((string)ViewData["Avatar"])) { %> +" id="avatar"> +" /> + +<% } %> +

+"> +<%=Html.Encode(ViewData["BlogTitle"])%> +- "><%= YavscHelpers.SiteName %> +

+
+ + +<% foreach (BlogEntry e in this.Model) { %> +
+

" class="usertitleref"> +<%=Html.Markdown(e.Title)%>

+<% bool truncated = false; %> +<%= Html.MarkdownToHtmlIntro(out truncated, e.Content,"/bfiles/"+e.Id+"/") %> +<% if (truncated) { %> +"> + <%=Html.Translate("ReadMore")%> + <% } %> +<%= Html.Partial("PostActions",e)%> +
+<% } %> + + + +
diff --git a/booking/Views/FileSystem/Create.aspx b/booking/Views/FileSystem/Create.aspx new file mode 100644 index 00000000..7f8ef9d5 --- /dev/null +++ b/booking/Views/FileSystem/Create.aspx @@ -0,0 +1,20 @@ +<%@ Page Title="File posting" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + +<% using(Html.BeginForm("Create", "FileSystem", FormMethod.Post, new { enctype = "multipart/form-data" })) { %> +
+ + + <%= Html.ValidationMessage("AFile") %> + +
+ + + +
+ + +
+ <% } %> +
diff --git a/booking/Views/FileSystem/Delete.aspx b/booking/Views/FileSystem/Delete.aspx new file mode 100644 index 00000000..b8c893bb --- /dev/null +++ b/booking/Views/FileSystem/Delete.aspx @@ -0,0 +1,3 @@ +<%@ Page Title="File System - File removal" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + diff --git a/booking/Views/FileSystem/Details.aspx b/booking/Views/FileSystem/Details.aspx new file mode 100644 index 00000000..3fd36c33 --- /dev/null +++ b/booking/Views/FileSystem/Details.aspx @@ -0,0 +1,11 @@ +<%@ Page Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + +<%= Model.Name %>
+Création : +<%= Model.CreationTime %>
+Dérnière modification : +<%= Model.LastWriteTime %>
+Dernier accès : +<%= Model.LastAccessTime %>
+Lien permanent : "><%= ViewData["url"] %> +
diff --git a/booking/Views/FileSystem/Edit.aspx b/booking/Views/FileSystem/Edit.aspx new file mode 100644 index 00000000..b4de16ff --- /dev/null +++ b/booking/Views/FileSystem/Edit.aspx @@ -0,0 +1,5 @@ +<%@ Page Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + +<%= Html.ActionLink("Delete","FileSystem") %> +<%= Html.ActionLink("Rename","FileSystem") %> + diff --git a/booking/Views/FileSystem/Index.aspx b/booking/Views/FileSystem/Index.aspx new file mode 100644 index 00000000..05c9aac0 --- /dev/null +++ b/booking/Views/FileSystem/Index.aspx @@ -0,0 +1,12 @@ +<%@ Page Title="Files" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + +

Index +

+
    +<% foreach (System.IO.FileInfo fi in Model) { %> +
  • <%= Html.ActionLink(fi.Name,"Details",new {id = fi.Name}) %>
  • +<% } %> +
+ +<%= Html.ActionLink("Ajouter des fichiers","Create") %> +
diff --git a/booking/Views/FrontOffice/Basket.aspx b/booking/Views/FrontOffice/Basket.aspx new file mode 100644 index 00000000..72841343 --- /dev/null +++ b/booking/Views/FrontOffice/Basket.aspx @@ -0,0 +1,38 @@ +<%@ Page Title="Basket" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + + +<% Title = Title +" "+ Model.Count+" article(s)"; %> + + + + +<% if (Model.Count>0) { %> +
    +<% foreach (Command cmd in Model.Values) { %> +
  • + <%= cmd.Id %> + <%= cmd.CreationDate %> + + <%= cmd.Status %> + <%= cmd.ProductRef %> +
      + <% if (cmd.Parameters!=null) + foreach (string key in cmd.Parameters.Keys) { %> +
    • <%=key%>: <%=cmd.Parameters[key]%>
    • + <% } %> +
    +
  • +<% } %> +
+<% } %> + +
+ + +
  • + <%= Html.ActionLink("Catalog","Catalog" ) %> +
  • + <%= Html.ActionLink("Estimates","Estimates" ) %> +
+
\ No newline at end of file diff --git a/booking/Views/FrontOffice/Brand.aspx b/booking/Views/FrontOffice/Brand.aspx new file mode 100644 index 00000000..86e8bde4 --- /dev/null +++ b/booking/Views/FrontOffice/Brand.aspx @@ -0,0 +1,31 @@ +<%@ Page Title="Catalog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +

<% if (Model.Logo!=null) { %> + <%=Model.Logo.Alt%> + <% } %> + <%=Html.Encode(Model.Name)%>

+

<%=Html.Encode(Model.Slogan)%>

+
+ +<% foreach (ProductCategory pc in Model.Categories ) { %> +
+

<%= Html.ActionLink( pc.Name, "ProductCategory", new { id = Model.Name, pc = pc.Reference }, new { @class="actionlink" } ) %>

+
+ + <% foreach (Product p in pc.Products ) { %> +
+

<%= Html.ActionLink( p.Name, "Product", new { id = Model.Name, pc = pc.Reference , pref = p.Reference }, new { @class="actionlink" } ) %>

+

+ <%= p.Description %> + <% if (p.Images !=null) + foreach (ProductImage i in p.Images ) { %> + <%=i.Alt%> + <% } %> +

+
+<% } %> +<% } %> + + +
diff --git a/booking/Views/FrontOffice/Catalog.aspx b/booking/Views/FrontOffice/Catalog.aspx new file mode 100644 index 00000000..4c1be629 --- /dev/null +++ b/booking/Views/FrontOffice/Catalog.aspx @@ -0,0 +1,28 @@ +<%@ Page Title="Catalog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +<% foreach (Brand b in Model.Brands ) { %>
+

<%= Html.ActionLink( b.Name, "Brand", new { id = b.Name }, new { @class="actionlink" } ) %>

+

<%= Html.Encode( b.Slogan ) %>

+ <% foreach (ProductCategory pc in b.Categories ) { %> +
+

<%= Html.ActionLink( pc.Name, "ProductCategory", new { brandid= b.Name, pcid = pc.Reference }, new { @class="actionlink" } ) %>

+
+ + <% foreach (Product p in pc.Products ) { %> +
+

<%= Html.ActionLink( p.Name, "Product", new { id = b.Name, pc = pc.Reference , pref = p.Reference }, new { @class="actionlink" } ) %>

+

+ <%= p.Description %> + <% if (p.Images !=null) + foreach (ProductImage i in p.Images ) { %> + <%=i.Alt%> + <% } %> +

+
+<% } %> +<% } %> +
<% } %> +
+ + diff --git a/booking/Views/FrontOffice/Command.aspx b/booking/Views/FrontOffice/Command.aspx new file mode 100644 index 00000000..9f7cae32 --- /dev/null +++ b/booking/Views/FrontOffice/Command.aspx @@ -0,0 +1,13 @@ +<%@ Page Title="Commande" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + + <%= Html.ActionLink("Votre panier","Basket","FrontOffice" ) %> + + + +
  • + <%= Html.ActionLink("Catalog","Catalog" ) %> +
  • + <%= Html.ActionLink("Estimates","Estimates" ) %> +
+
\ No newline at end of file diff --git a/booking/Views/FrontOffice/Estimate.aspx b/booking/Views/FrontOffice/Estimate.aspx new file mode 100644 index 00000000..1c2a3a90 --- /dev/null +++ b/booking/Views/FrontOffice/Estimate.aspx @@ -0,0 +1,357 @@ +<%@ Page Title="Devis" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> +<%@ Register Assembly="Yavsc.WebControls" TagPrefix="yavsc" Namespace="Yavsc.WebControls" %> + + + + + + +<%= Html.ValidationSummary("Devis") %> + +<% using (Html.BeginForm("Estimate","FrontOffice")) { %> +<%= Html.LabelFor(model => model.Title) %>:<%= Html.TextBox( "Title" ) %> +<%= Html.ValidationMessage("Title", "*") %> +
+<%= Html.Hidden ("Responsible") %> + +<%= Html.LabelFor(model => model.Client) %>: + + <% Client.Value = Model.Client ; %> + + + + <%= Html.ValidationMessage("Client", "*") %> +
+<%= Html.LabelFor(model => model.Description) %>:<%=Html.TextArea( "Description") %> +<%= Html.ValidationMessage("Description", "*") %> +
+<%= Html.Hidden( "Id" ) %> +
+<% if (Model.Id==0) { %> + +<% } else { %> + +<% } %> + + <% if (Model.Id>0) { %> + + + + + + + + + + + +<% int lc=0; + if (Model.Lines!=null) + foreach (Writting wr in Model.Lines) { lc++; %> +row" id="wr<%=wr.Id%>"> + + + + + + +<% } %> + +
<%=Yavsc.Model.LocalizedText.Description%><%=Yavsc.Model.LocalizedText.Product_reference%><%=Yavsc.Model.LocalizedText.Count%><%=Yavsc.Model.LocalizedText.Unitary_cost%>
<%=wr.Description%><%=wr.ProductReference%><%=wr.Count%><%=wr.UnitaryCost%> + +
+<% } %> +<% } %> + +
+ + + + + + + + \ No newline at end of file diff --git a/booking/Views/FrontOffice/Estimates.aspx b/booking/Views/FrontOffice/Estimates.aspx new file mode 100644 index 00000000..daaaeef0 --- /dev/null +++ b/booking/Views/FrontOffice/Estimates.aspx @@ -0,0 +1,24 @@ +<%@ Page Title="My estimates" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage>" %> + +<% if (((int)ViewData["ResponsibleCount"])>0) { %> +
+Les estimations que vous avez faites (<%=ViewData["ResponsibleCount"]%>):
+<% +foreach (Estimate estim in Model) { + if (string.Compare(estim.Responsible,(string) ViewData["UserName"])==0) { %> + + <%= Html.ActionLink("Titre:"+estim.Title+" Client:"+estim.Client,"Estimate",new{id=estim.Id}) %> +
+ <% }}%> +
+<% } %> +<% if (((int)ViewData["ClientCount"])>0) { %> +
+ Vos estimations en tant que client + (<%=ViewData["ClientCount"]%>):
+ <% foreach (Estimate estim in Model) { %> + <%= Html.ActionLink("Titre:"+estim.Title+" Responsable:"+estim.Responsible,"Estimate",new{id=estim.Id}) %> +
<% } %> +
+ <% } %> +
diff --git a/booking/Views/FrontOffice/EventPub.aspx b/booking/Views/FrontOffice/EventPub.aspx new file mode 100644 index 00000000..7b8ca523 --- /dev/null +++ b/booking/Views/FrontOffice/EventPub.aspx @@ -0,0 +1,20 @@ +<%@ Page Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + + +<% using (Html.BeginForm()) { %> + +<%= Html.LabelFor(model => model.Title) %>: <%=Model.Title%>
+<%= Html.LabelFor(model => model.Description) %>: <%=Model.Description%>
+<%= Html.LabelFor(model => model.Location) %>: <%=Model.Location%>
+<%= Html.LabelFor(model => model.StartDate) %>: <%=Model.StartDate%>
+<%= Html.LabelFor(model => model.EndDate) %>: <%=Model.EndDate%>
+<%= Html.LabelFor(model => model.Circles) %>: <%=Model.Circles%>
+<%= Html.LabelFor(model => model.ImgLocator) %>: <%=Model.ImgLocator%>
+<%= Html.LabelFor(model => model.EventWebPage) %>: <%=Model.EventWebPage%>
+<%= Html.LabelFor(model => model.ProviderName) %>: <%=Model.ProviderName%>
+<%= Html.LabelFor(model => model.Comment) %>: <%=Model.Comment%>
+ +<% } %> + +
\ No newline at end of file diff --git a/booking/Views/FrontOffice/Index.aspx b/booking/Views/FrontOffice/Index.aspx new file mode 100644 index 00000000..42ce313e --- /dev/null +++ b/booking/Views/FrontOffice/Index.aspx @@ -0,0 +1,11 @@ +<%@ Page Title="Front office" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + + + +
  • + <%= Html.ActionLink("Catalog","Catalog" ) %> +
  • + <%= Html.ActionLink("Estimates","Estimates" ) %> +
+
diff --git a/booking/Views/FrontOffice/Product.aspx b/booking/Views/FrontOffice/Product.aspx new file mode 100644 index 00000000..0182890f --- /dev/null +++ b/booking/Views/FrontOffice/Product.aspx @@ -0,0 +1,33 @@ +<%@ Page Title="Catalog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +<% Title = Model.Name; %> + + + +<%= Html.Encode(Model.Reference) %> + + +
+

<%= Html.Encode(Model.Description) %>

+<% if (Model.Images!=null) foreach (ProductImage i in Model.Images) { %> +<%=i.Alt%> +<% } %> +<% if (Model.UnitaryPrice !=null) { %> +Prix unitaire : <%= Html.Encode(Model.UnitaryPrice.Quantity.ToString())%> +<%= Html.Encode(Model.UnitaryPrice.Unit.Name)%> +<% } else { %> Gratuit! <% } %> +
+
+ +<%= Html.CommandForm(Model,"Ajouter au panier") %> + +<% if (Model.CommandValidityDates!=null) { %> +Offre valable du <%= Model.CommandValidityDates.StartDate.ToString("dd/MM/yyyy") %> au +<%= Model.CommandValidityDates.EndDate.ToString("dd/MM/yyyy") %>. +<% } %> + +
+
diff --git a/booking/Views/FrontOffice/ProductCategory.aspx b/booking/Views/FrontOffice/ProductCategory.aspx new file mode 100644 index 00000000..de875d71 --- /dev/null +++ b/booking/Views/FrontOffice/ProductCategory.aspx @@ -0,0 +1,20 @@ +<%@ Page Title="Catalog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +<% foreach (Product p in Model.Products ) { %> + +

<%= Html.ActionLink( p.Name, "Product", new { id = ViewData["BrandId"], pc = Model.Reference , pref = p.Reference }, new { @class="actionlink" } ) %>

+ +

+ <%= p.Description %> + <% if (p.Images !=null) + foreach (ProductImage i in p.Images ) { %> + <%=i.Alt%> + <% } %> +

+ + + <% } %> + + +
diff --git a/booking/Views/FrontOffice/ReferenceNotFound.aspx b/booking/Views/FrontOffice/ReferenceNotFound.aspx new file mode 100644 index 00000000..51855ace --- /dev/null +++ b/booking/Views/FrontOffice/ReferenceNotFound.aspx @@ -0,0 +1,5 @@ +<%@ Page Title="Référence absente au catalogue" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + + + diff --git a/booking/Views/FrontOffice/Service.aspx b/booking/Views/FrontOffice/Service.aspx new file mode 100644 index 00000000..34a787be --- /dev/null +++ b/booking/Views/FrontOffice/Service.aspx @@ -0,0 +1,31 @@ +<%@ Page Title="Catalog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + +<% Title = ViewData ["BrandName"] + " " + Model.Name; %> + + +

<%=ViewData ["ProdCatName"]%> - <%= Html.ActionLink( Model.Name, "Product", new { id = ViewData ["BrandName"], pc = ViewData ["ProdCatRef"] , pref = Model.Reference } ) %>

+ +
+ +
+

<%= Html.Encode(Model.Description) %>

+<% if (Model.Images!=null) foreach (ProductImage i in Model.Images) { %> +<%=i.Alt%> +<% } %> +<% if (Model.HourPrice !=null) { %> +Prix horaire de la prestation : +<%= Html.Encode(Model.HourPrice.Quantity.ToString())%> +<%= Html.Encode(Model.HourPrice.Unit.Name)%> +<% } %> +
+ +
+<%= Html.CommandForm(Model,"Ajouter au panier") %> + +<% if (Model.CommandValidityDates!=null) { %> +Offre valable du <%= Model.CommandValidityDates.StartDate.ToString("dd/MM/yyyy") %> au +<%= Model.CommandValidityDates.EndDate.ToString("dd/MM/yyyy") %>. +<% } %> + +
+
diff --git a/booking/Views/FrontOffice/Writting.ascx b/booking/Views/FrontOffice/Writting.ascx new file mode 100644 index 00000000..f7efe79c --- /dev/null +++ b/booking/Views/FrontOffice/Writting.ascx @@ -0,0 +1,25 @@ +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> + +<%= Html.ValidationSummary("Ligne de devis") %> +<% using (Html.BeginForm("Write","WorkFlow")) { %> +
+<%= Html.Hidden( "Id" ) %> + +<%= Html.LabelFor(model => model.Description) %>:<%= Html.TextArea( "Description" ) %> +<%= Html.ValidationMessage("Description", "*", new { @id="Err_wr_Description", @class="error" }) %> +
+<%= Html.LabelFor(model => model.ProductReference) %>:<%= Html.TextBox( "ProductReference" ) %> +<%= Html.ValidationMessage("ProductReference", "*", new { @id="Err_wr_ProductReference", @class="error" }) %> +
+<%= Html.LabelFor(model => model.UnitaryCost) %>:<%= Html.TextBox( "UnitaryCost" ) %> +<%= Html.ValidationMessage("UnitaryCost", "", new { @id="Err_wr_UnitaryCost", @class="error" }) %> +
+<%= Html.LabelFor(model => model.Count) %>:<%= Html.TextBox( "Count" ) %> +<%= Html.ValidationMessage("Count", "", new { @id="Err_wr_Count" , @class="error"}) %> + +
+<% } %> + + + + diff --git a/booking/Views/Google/Auth.aspx b/booking/Views/Google/Auth.aspx new file mode 100644 index 00000000..48f10005 --- /dev/null +++ b/booking/Views/Google/Auth.aspx @@ -0,0 +1,6 @@ +<%@ Page Title="Catalog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + +AccessToken : <%= Session["AccessToken"] %> +Target in error : <%= ViewData["TargetNameError"] %> + diff --git a/booking/Views/Google/Book.aspx b/booking/Views/Google/Book.aspx new file mode 100644 index 00000000..22683cce --- /dev/null +++ b/booking/Views/Google/Book.aspx @@ -0,0 +1,65 @@ +<%@ Page Title="Booking" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + + + + + + + + + + + + +<% using ( Html.BeginForm("Book","Google") ) { %> +
Date d'intervention : +Intervention souhaitée entre le + "> + <%= Html.ValidationMessageFor( model=>model.StartDate ) %> +et le + "> + <%= Html.ValidationMessageFor(model=>model.EndDate) %> +
+ Heure et durée d'intervention souhaitée +<%= Html.LabelFor(model=>model.StartHour) %> + + <%= Html.ValidationMessageFor(model=>model.StartHour) %> + +<%= Html.LabelFor(model=>model.EndHour) %> + + <%= Html.ValidationMessageFor(model=>model.EndHour) %> +
+
+Intervenant + <%= Html.LabelFor(model=>model.Role) %>: + <%= Html.TextBoxFor(model=>model.Role) %> + <%= Html.ValidationMessageFor(model=>model.Role) %> +
+ <%= Html.LabelFor(model=>model.Person) %>: + <%= Html.TextBoxFor(model=>model.Person) %> + <%= Html.ValidationMessageFor(model=>model.Person) %> +
+ + + +<% } %> +
diff --git a/booking/Views/Google/CalAuth.aspx b/booking/Views/Google/CalAuth.aspx new file mode 100644 index 00000000..7825a886 --- /dev/null +++ b/booking/Views/Google/CalAuth.aspx @@ -0,0 +1,5 @@ +<%@ Page Title="Catalog" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + + + diff --git a/booking/Views/Google/ChooseADate.aspx b/booking/Views/Google/ChooseADate.aspx new file mode 100644 index 00000000..63e3053a --- /dev/null +++ b/booking/Views/Google/ChooseADate.aspx @@ -0,0 +1,13 @@ +<%@ Page Title="Google calendar usage" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + + +<% using ( Html.BeginForm("ChooseAStartingDate","Google") ) { %> +<% foreach (Period e in Model.Values) { %> + +<% } %> +"> + +<% } %> + + diff --git a/booking/Views/Google/ChooseCalendar.aspx b/booking/Views/Google/ChooseCalendar.aspx new file mode 100644 index 00000000..9be76b57 --- /dev/null +++ b/booking/Views/Google/ChooseCalendar.aspx @@ -0,0 +1,17 @@ +<%@ Page Title="Google calendar usage" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + + +<% using ( Html.BeginForm("SetCalendar","Google") ) { %> + +
+<% foreach (CalendarListEntry e in Model.items.Where(x=>x.accessRole=="owner")) { %> + +
+<% } %> +"> + +<% } %> + +
diff --git a/booking/Views/Google/Index.aspx b/booking/Views/Google/Index.aspx new file mode 100644 index 00000000..93794753 --- /dev/null +++ b/booking/Views/Google/Index.aspx @@ -0,0 +1,4 @@ +<%@ Page Title="Google interface" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + + + diff --git a/booking/Views/Google/OtherWebException.aspx b/booking/Views/Google/OtherWebException.aspx new file mode 100644 index 00000000..985fa792 --- /dev/null +++ b/booking/Views/Google/OtherWebException.aspx @@ -0,0 +1,9 @@ +<%@ Page Title="Google error message" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + +

<%= Html.Encode(Model.Title)%>

+
+ 
+ <%= Html.Encode(Model.Content) %>
+ 
+
+ diff --git a/booking/Views/Home/AssemblyInfo.aspx b/booking/Views/Home/AssemblyInfo.aspx new file mode 100644 index 00000000..fabbd382 --- /dev/null +++ b/booking/Views/Home/AssemblyInfo.aspx @@ -0,0 +1,19 @@ +<%@ Page Title="Assemblies" Language="C#" Inherits="System.Web.Mvc.ViewPage>" MasterPageFile="~/Models/App.master"%> + +
+

+Running assembly : +<%= GetType().Assembly.FullName %>

+
+
+

+Assemblies referenced in this application : +

+
    +<% foreach (System.Reflection.AssemblyName item in Model) { %> +
  • <%= item.FullName %>
  • +<% } %> +
+
+
+ diff --git a/booking/Views/Home/Contact.aspx b/booking/Views/Home/Contact.aspx new file mode 100644 index 00000000..0c30f7cc --- /dev/null +++ b/booking/Views/Home/Contact.aspx @@ -0,0 +1,37 @@ +<%@ Page Title="Contact" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + +
+

+Directeur :
+Adresse postale :
+Tél. : +33
+Tél. : +33
+SIREN :
+SIRET :
+Activité Principalement Exercée (APE) :
+

+<% using (Html.BeginForm("Contact", "Home")) { %> +
+Message +

+<%= Html.Label("email") %>: +<%= Html.ValidationMessage("email") %>
+<%= Html.TextBox("email") %> +

+

+<%= Html.Label("reason") %>: +<%= Html.ValidationMessage("reason") %>
+<%= Html.TextBox("reason") %> +

+

+<%= Html.Label("body") %>: +<%= Html.ValidationMessage("body") %>
+<%= Html.TextArea("body") %> +

+
+"> + +<% } %> +
+
\ No newline at end of file diff --git a/booking/Views/Home/Credits.aspx b/booking/Views/Home/Credits.aspx new file mode 100644 index 00000000..d9bd9381 --- /dev/null +++ b/booking/Views/Home/Credits.aspx @@ -0,0 +1,5 @@ +<%@ Page Title="Credits" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> + + +
Icons made by Vectorgraphit from www.flaticon.com is licensed by CC BY 3.0
+
diff --git a/booking/Views/Home/Index.aspx b/booking/Views/Home/Index.aspx new file mode 100644 index 00000000..7e24cb2f --- /dev/null +++ b/booking/Views/Home/Index.aspx @@ -0,0 +1,16 @@ +<%@ Page Title="Home" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master"%> + +<%= Html.Partial("TagPanel",ViewData["Accueil"]) %> +<%= Html.Partial("TagPanel",ViewData["Événements"]) %> +<%= Html.Partial("TagPanel",ViewData["Mentions légales"]) %> + + + +
+<%= Html.ActionLink("Les articles", "Index", "Blogs") %> +<%= Html.ActionLink("Contact", "Contact", "Home", null, new { @class="actionlink" }) %> +<%= Html.ActionLink("Credits", "Credits", "Home", null, new { @class="actionlink" }) %> +<%= Html.ActionLink("Version des librairies", "AssemblyInfo", "Home", null, new { @class="actionlink" }) %> +
+
+ diff --git a/booking/Views/Home/TagPanel.ascx b/booking/Views/Home/TagPanel.ascx new file mode 100644 index 00000000..450c3739 --- /dev/null +++ b/booking/Views/Home/TagPanel.ascx @@ -0,0 +1,13 @@ +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> +
+

<%=Html.Encode(Model.Name)%>

+<% foreach (var t in Model.Titles) { %> + +<% } %> +
diff --git a/booking/Views/Web.config b/booking/Views/Web.config new file mode 100644 index 00000000..db86717b --- /dev/null +++ b/booking/Views/Web.config @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/booking/Views/WorkFlow/Index.aspx b/booking/Views/WorkFlow/Index.aspx new file mode 100644 index 00000000..a70bbb42 --- /dev/null +++ b/booking/Views/WorkFlow/Index.aspx @@ -0,0 +1,9 @@ +<%@ Page Title="Workflow" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master"%> + + +
+<%= Html.ActionLink("blogs","Index","WorkFlow") %> +
+ +
+ diff --git a/booking/Views/WorkFlow/NewProject.aspx b/booking/Views/WorkFlow/NewProject.aspx new file mode 100644 index 00000000..00167a19 --- /dev/null +++ b/booking/Views/WorkFlow/NewProject.aspx @@ -0,0 +1,20 @@ +<%@ Page Title="Nouveau projet" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/App.master" %> + +
+<%= Html.ValidationSummary("Nouveau projet") %> +<% using ( Html.BeginForm("NewProject", "WorkFlow") ) { %> +<%= Html.LabelFor(model => model.Name) %> : +<%= Html.TextBox( "Name" ) %> +<%= Html.ValidationMessage("Name", "*") %>
+<%= Html.LabelFor(model => model.Manager) %> : +<%= Html.TextBox( "Manager" ) %> +<%= Html.ValidationMessage("Manager", "*") %>
+<%= Html.LabelFor(model => model.Description) %> : +<%= Html.TextBox( "Description" ) %> +<%= Html.ValidationMessage("Description", "*") %>
+ +<% } %> +
+
+ + diff --git a/booking/Web.config b/booking/Web.config new file mode 100644 index 00000000..ac56993e --- /dev/null +++ b/booking/Web.config @@ -0,0 +1,269 @@ + + + + + + +
+ +
+
+
+
+ + + +
+ +
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/booking/WebApiConfig.cs b/booking/WebApiConfig.cs new file mode 100644 index 00000000..1f01b584 --- /dev/null +++ b/booking/WebApiConfig.cs @@ -0,0 +1,66 @@ +// +// WebApiConfig.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2015 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; +using Yavsc.Formatters; +using Yavsc.Model.FrontOffice; +using System.Web.Http; + +namespace Yavsc +{ + /// + /// Web API config. + /// + public static class WebApiConfig + { + /// + /// Gets the URL prefix. + /// + /// The URL prefix. + public static string UrlPrefix { get { return "api"; } } + + /// + /// Gets the URL prefix relative. + /// + /// The URL prefix relative. + public static string UrlPrefixRelative { get { return "~/api"; } } + + /// + /// Register the specified config. + /// + /// Config. + public static void Register(HttpConfiguration config) + { + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{action}/{id}", + defaults: new { action="Index", id = RouteParameter.Optional } + ); + } + } + +} diff --git a/booking/booking.csproj b/booking/booking.csproj new file mode 100644 index 00000000..748ed188 --- /dev/null +++ b/booking/booking.csproj @@ -0,0 +1,295 @@ + + + + Debug + AnyCPU + {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 8.0.30703 + 2.0 + {8B39864D-D427-41EC-939B-BA23FBAEE396} + Library + booking + booking + v4.5 + + + true + full + false + bin + DEBUG; + prompt + 4 + false + + + full + true + bin + prompt + 4 + false + + + + + + + + + + + + + + + + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + + ..\packages\Microsoft.AspNet.Mvc.5.2.0\lib\net45\System.Web.Mvc.dll + + + + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll + + + ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll + + + ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll + + + + ..\web\lib\MarkdownDeep.dll + + + + ..\packages\PayPalCoreSDK.1.6.3\lib\net45\PayPalCoreSDK.dll + + + ..\packages\PayPalButtonManagerSDK.2.9.109\lib\net20\PayPalButtonManagerSDK.dll + + + ..\packages\PayPal.1.6.0\lib\net45\PayPal.dll + + + ..\packages\PayPalAdaptiveAccountsSDK.2.8.110\lib\net20\PayPalAdaptiveAccountsSDK.dll + + + ..\packages\Npgsql.3.0.3\lib\net45\Npgsql.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Global.asax + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\web\templates\Estim.tt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {68F5B80A-616E-4C3C-91A0-828AA40000BD} + YavscModel + + + {C6E9E91B-97D3-48D9-8AA7-05356929E162} + NpgsqlBlogProvider + + + {821FF72D-9F4B-4A2C-B95C-7B965291F119} + NpgsqlContentProvider + + + {BBA7175D-7F92-4278-96FC-84C495A2B5A6} + NpgsqlMRPProviders + + + {9D7D892E-9B77-4713-892D-C26E1E944119} + ITContentProvider + + + {90BF2234-7252-4CD5-B2A4-17501B19279B} + SalesCatalog + + + + + TextTemplatingFilePreprocessor + Estim.cs + + + \ No newline at end of file diff --git a/booking/packages.config b/booking/packages.config new file mode 100644 index 00000000..88ed482a --- /dev/null +++ b/booking/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/booking/robots.txt b/booking/robots.txt new file mode 100644 index 00000000..9c664a25 --- /dev/null +++ b/booking/robots.txt @@ -0,0 +1,7 @@ + +User-agent: * +Disallow: /Google/Login +Disallow: /Account/Login +Disallow: /Admin/ +Disallow: /App_Themes/ +Disallow: /Scripts/ diff --git a/booking/templates/Estim.cs b/booking/templates/Estim.cs new file mode 100644 index 00000000..3c973e6f --- /dev/null +++ b/booking/templates/Estim.cs @@ -0,0 +1,1033 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Mono Runtime Version: 4.0.30319.17020 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ + +namespace Yavsc.templates { + using System.Linq; + using System.Text; + using System.Collections.Generic; + using Yavsc.Model.WorkFlow; + using Yavsc.Model.RolesAndMembers; + using System; + + + public partial class Estim : EstimBase { + + private Estimate _estimField; + public Estimate estim { + get { + return this._estimField; + } + } + private Profile _fromField; + public Profile from { + get { + return this._fromField; + } + } + private Profile _toField; + public Profile to { + get { + return this._toField; + } + } + private String _efromField; + public String efrom { + get { + return this._efromField; + } + } + private String _etoField; + public String eto { + get { + return this._etoField; + } + } + + + public virtual string TransformText() { + this.GenerationEnvironment = null; + + #line 10 "" + this.Write("\n"); + + #line default + #line hidden + + #line 16 "" + this.Write("\n\\documentclass[french,11pt]{article}\n\\usepackage{babel}\n\\usepackage[T1]{fontenc}\n\\usepackage[utf8]{inputenc}\n\\usepackage[a4paper]{geometry}\n\\usepackage{units}\n\\usepackage{bera}\n\\usepackage{graphicx}\n\\usepackage{fancyhdr}\n\\usepackage{fp}\n\n\\def\\TVA{20} % Taux de la TVA\n\n\\def\\TotalHT{0}\n\\def\\TotalTVA{0}\n\n\\newcommand{\\AjouterService}[3]{% Arguments : Désignation, quantité, prix\n \\FPround{\\prix}{#3}{2}\n \\FPeval{\\montant}{#2 * #3}\n \\FPround{\\montant}{\\montant}{2}\n \\FPadd{\\TotalHT}{\\TotalHT}{\\montant}\n \n \\eaddto\\ListeProduits{#1 & \\prix & #2 & \\montant \\cr}\n}\n\n\n\\newcommand{\\AfficheResultat}{%\n \\ListeProduits\n \n \\FPeval{\\TotalTVA}{\\TotalHT * \\TVA / 100}\n \\FPadd{\\TotalTTC}{\\TotalHT}{\\TotalTVA}\n \\FPround{\\TotalHT}{\\TotalHT}{2}\n \\FPround{\\TotalTVA}{\\TotalTVA}{2}\n \\FPround{\\TotalTTC}{\\TotalTTC}{2}\n \\global\\let\\TotalHT\\TotalHT\n \\global\\let\\TotalTVA\\TotalTVA\n \\global\\let\\TotalTTC\\TotalTTC\n \n\n \\cr \n \\hline\n \\textbf{Total} & & & \\TotalHT\n}\n\n\\newcommand*\\eaddto[2]{% version développée de \\addto\n \\edef\\tmp{#2}%\n \\expandafter\\addto\n \\expandafter#1%\n \\expandafter{\\tmp}%\n}\n\n\\newcommand{\\ListeProduits}{}\n\n\n\n\n%%%%%%%%%%%%%%%%%%%%% A MODIFIER DANS LA FACTURE %%%%%%%%%%%%%%%%%%%%%\n\n\\def\\FactureNum {"); + + #line default + #line hidden + + #line 75 "" + this.Write(this.ToStringHelper.ToStringWithCulture( estim.Id.ToString() )); + + #line default + #line hidden + + #line 75 "" + this.Write("} % Numéro de facture\n\\def\\FactureAcquittee {non} % Facture acquittée : oui/non\n\\def\\FactureLieu {"); + + #line default + #line hidden + + #line 77 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.CityAndState )); + + #line default + #line hidden + + #line 77 "" + this.Write("} % Lieu de l'édition de la facture\n\\def\\FactureObjet {Facture : "); + + #line default + #line hidden + + #line 78 "" + this.Write(this.ToStringHelper.ToStringWithCulture( estim.Title )); + + #line default + #line hidden + + #line 78 "" + this.Write("} % Objet du document\n% Description de la facture\n\\def\\FactureDescr {%\n "); + + #line default + #line hidden + + #line 81 "" + this.Write(this.ToStringHelper.ToStringWithCulture( estim.Description )); + + #line default + #line hidden + + #line 81 "" + this.Write("\n}\n\n% Infos Client\n\\def\\ClientNom{"); + + #line default + #line hidden + + #line 85 "" + this.Write(this.ToStringHelper.ToStringWithCulture( to.Name )); + + #line default + #line hidden + + #line 85 "" + this.Write("} % Nom du client\n\\def\\ClientAdresse{% % Adresse du client\n "); + + #line default + #line hidden + + #line 87 "" + if (!string.IsNullOrWhiteSpace(to.Address)) { + + #line default + #line hidden + + #line 88 "" + this.Write(" "); + + #line default + #line hidden + + #line 88 "" + this.Write(this.ToStringHelper.ToStringWithCulture( to.Address )); + + #line default + #line hidden + + #line 88 "" + this.Write("\\\\\n "); + + #line default + #line hidden + + #line 89 "" + } + + #line default + #line hidden + + #line 90 "" + this.Write(" "); + + #line default + #line hidden + + #line 90 "" + if (!string.IsNullOrWhiteSpace(to.ZipCode)) { + + #line default + #line hidden + + #line 91 "" + this.Write(" "); + + #line default + #line hidden + + #line 91 "" + this.Write(this.ToStringHelper.ToStringWithCulture( to.ZipCode )); + + #line default + #line hidden + + #line 91 "" + this.Write(" "); + + #line default + #line hidden + + #line 91 "" + } + + #line default + #line hidden + + #line 92 "" + this.Write(" "); + + #line default + #line hidden + + #line 92 "" + if (!string.IsNullOrWhiteSpace(to.ZipCode)) { + + #line default + #line hidden + + #line 93 "" + this.Write(" "); + + #line default + #line hidden + + #line 93 "" + this.Write(this.ToStringHelper.ToStringWithCulture( to.CityAndState )); + + #line default + #line hidden + + #line 93 "" + this.Write("\\\\ "); + + #line default + #line hidden + + #line 93 "" + } + + #line default + #line hidden + + #line 94 "" + this.Write(" \n"); + + #line default + #line hidden + + #line 95 "" + if (!string.IsNullOrWhiteSpace(to.Phone)) { + + #line default + #line hidden + + #line 96 "" + this.Write(" Téléphone fixe: "); + + #line default + #line hidden + + #line 96 "" + this.Write(this.ToStringHelper.ToStringWithCulture( to.Phone )); + + #line default + #line hidden + + #line 96 "" + this.Write("\\\\\n"); + + #line default + #line hidden + + #line 97 "" + } + + #line default + #line hidden + + #line 98 "" + if (!string.IsNullOrWhiteSpace(to.Mobile)) { + + #line default + #line hidden + + #line 99 "" + this.Write(" Mobile: "); + + #line default + #line hidden + + #line 99 "" + this.Write(this.ToStringHelper.ToStringWithCulture( to.Mobile )); + + #line default + #line hidden + + #line 99 "" + this.Write("\\\\\n"); + + #line default + #line hidden + + #line 100 "" + } + + #line default + #line hidden + + #line 101 "" + this.Write(" "); + + #line default + #line hidden + + #line 101 "" + if (!string.IsNullOrWhiteSpace(eto)) { + + #line default + #line hidden + + #line 102 "" + this.Write(" E-mail: "); + + #line default + #line hidden + + #line 102 "" + this.Write(this.ToStringHelper.ToStringWithCulture( eto )); + + #line default + #line hidden + + #line 102 "" + } + + #line default + #line hidden + + #line 103 "" + this.Write("}\n\n% Liste des produits facturés : Désignation, prix\n\n "); + + #line default + #line hidden + + #line 107 "" + foreach (Writting wr in estim.Lines) { + + #line default + #line hidden + + #line 108 "" + this.Write("\\AjouterService {"); + + #line default + #line hidden + + #line 108 "" + this.Write(this.ToStringHelper.ToStringWithCulture(wr.Description)); + + #line default + #line hidden + + #line 108 "" + this.Write(" "); + + #line default + #line hidden + + #line 108 "" + if (!string.IsNullOrWhiteSpace(wr.ProductReference)) { + + #line default + #line hidden + + #line 109 "" + this.Write(" ("); + + #line default + #line hidden + + #line 109 "" + this.Write(this.ToStringHelper.ToStringWithCulture(wr.ProductReference)); + + #line default + #line hidden + + #line 109 "" + this.Write(")"); + + #line default + #line hidden + + #line 109 "" + } + + #line default + #line hidden + + #line 110 "" + this.Write("} {"); + + #line default + #line hidden + + #line 110 "" + this.Write(this.ToStringHelper.ToStringWithCulture(wr.Count)); + + #line default + #line hidden + + #line 110 "" + this.Write("} {"); + + #line default + #line hidden + + #line 110 "" + this.Write(this.ToStringHelper.ToStringWithCulture(wr.UnitaryCost)); + + #line default + #line hidden + + #line 110 "" + this.Write("} \n "); + + #line default + #line hidden + + #line 111 "" + } + + #line default + #line hidden + + #line 112 "" + this.Write("\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n\\geometry{verbose,tmargin=4em,bmargin=8em,lmargin=6em,rmargin=6em}\n\\setlength{\\parindent}{0pt}\n\\setlength{\\parskip}{1ex plus 0.5ex minus 0.2ex}\n\n\\thispagestyle{fancy}\n\\pagestyle{fancy}\n\\setlength{\\parindent}{0pt}\n\n\\renewcommand{\\headrulewidth}{0pt}\n\\cfoot{\n "); + + #line default + #line hidden + + #line 125 "" + if (!string.IsNullOrWhiteSpace(from.Name)) { + + #line default + #line hidden + + #line 126 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.Name )); + + #line default + #line hidden + + #line 126 "" + } + + #line default + #line hidden + + #line 127 "" + this.Write(" "); + + #line default + #line hidden + + #line 127 "" + if (!string.IsNullOrWhiteSpace(from.Address)) { + + #line default + #line hidden + + #line 128 "" + this.Write(" - "); + + #line default + #line hidden + + #line 128 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.Address )); + + #line default + #line hidden + + #line 128 "" + } + + #line default + #line hidden + + #line 129 "" + this.Write(" \n "); + + #line default + #line hidden + + #line 130 "" + if (!string.IsNullOrWhiteSpace(from.CityAndState)) { + + #line default + #line hidden + + #line 131 "" + this.Write(" - "); + + #line default + #line hidden + + #line 131 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.CityAndState )); + + #line default + #line hidden + + #line 131 "" + } + + #line default + #line hidden + + #line 132 "" + this.Write(" \\newline\n \\small{\n "); + + #line default + #line hidden + + #line 134 "" + if (!string.IsNullOrWhiteSpace(efrom)) { + + #line default + #line hidden + + #line 135 "" + this.Write("E-mail: "); + + #line default + #line hidden + + #line 135 "" + this.Write(this.ToStringHelper.ToStringWithCulture( efrom )); + + #line default + #line hidden + + #line 135 "" + } + + #line default + #line hidden + + #line 136 "" + this.Write(" "); + + #line default + #line hidden + + #line 136 "" + if (!string.IsNullOrWhiteSpace(from.Mobile)) { + + #line default + #line hidden + + #line 137 "" + this.Write(" - Téléphone mobile: "); + + #line default + #line hidden + + #line 137 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.Mobile )); + + #line default + #line hidden + + #line 137 "" + } + + #line default + #line hidden + + #line 138 "" + this.Write(" "); + + #line default + #line hidden + + #line 138 "" + if (!string.IsNullOrWhiteSpace(from.Phone)) { + + #line default + #line hidden + + #line 139 "" + this.Write(" - Téléphone fixe: "); + + #line default + #line hidden + + #line 139 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.Phone )); + + #line default + #line hidden + + #line 139 "" + } + + #line default + #line hidden + + #line 140 "" + this.Write(" }\n}\n\n\\begin{document}\n\n% Logo de la société\n%\\includegraphics{logo.jpg}\n\n% Nom et adresse de la société\n"); + + #line default + #line hidden + + #line 149 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.Name )); + + #line default + #line hidden + + #line 149 "" + this.Write("\\\\\n"); + + #line default + #line hidden + + #line 150 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.Address )); + + #line default + #line hidden + + #line 150 "" + this.Write("\\\\\n"); + + #line default + #line hidden + + #line 151 "" + this.Write(this.ToStringHelper.ToStringWithCulture(from.ZipCode )); + + #line default + #line hidden + + #line 151 "" + this.Write(" "); + + #line default + #line hidden + + #line 151 "" + this.Write(this.ToStringHelper.ToStringWithCulture(from.CityAndState)); + + #line default + #line hidden + + #line 151 "" + this.Write("\\\\\n\nFacture n°\\FactureNum\n\n\n{\\addtolength{\\leftskip}{10.5cm} %in ERT\n \\textbf{\\ClientNom} \\\\\n \\ClientAdresse \\\\\n\n} %in ERT\n\n\n\\hspace*{10.5cm}\n\\FactureLieu, le \\today\n\n~\\\\~\\\\\n\n\\textbf{Objet : \\FactureObjet \\\\}\n\n\\textnormal{\\FactureDescr}\n\n~\\\\\n\n\\begin{center}\n \\begin{tabular}{lrrr}\n \\textbf{Désignation ~~~~~~} & \\textbf{Prix unitaire} & \\textbf{Quantité} & \\textbf{Montant (EUR)} \\\\\n \\hline\n \\AfficheResultat{}\n \\end{tabular}\n\\end{center}\n\n\\begin{flushright}\n\\textit{Auto entreprise en franchise de TVA}\\\\\n\n\\end{flushright}\n~\\\\\n\n\\ifthenelse{\\equal{\\FactureAcquittee}{oui}}{\n Facture acquittée.\n}{\n\n À régler par chèque ou par virement bancaire :\n\n \\begin{center}\n \\begin{tabular}{|c c c c|}\n "); + + #line default + #line hidden + + #line 196 "" + if (!string.IsNullOrWhiteSpace(from.BankCode) && !string.IsNullOrWhiteSpace(from.WicketCode) + && !string.IsNullOrWhiteSpace(from.AccountNumber) ) { + + #line default + #line hidden + + #line 198 "" + this.Write(" \\hline \\textbf{Code banque} & \\textbf{Code guichet} & \\textbf{N° de Compte} & \\textbf{Clé RIB} \\\\\n "); + + #line default + #line hidden + + #line 199 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.BankCode )); + + #line default + #line hidden + + #line 199 "" + this.Write(" & "); + + #line default + #line hidden + + #line 199 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.WicketCode )); + + #line default + #line hidden + + #line 199 "" + this.Write(" & "); + + #line default + #line hidden + + #line 199 "" + this.Write(this.ToStringHelper.ToStringWithCulture(from.AccountNumber )); + + #line default + #line hidden + + #line 199 "" + this.Write(" & "); + + #line default + #line hidden + + #line 199 "" + this.Write(this.ToStringHelper.ToStringWithCulture(from.BankedKey)); + + #line default + #line hidden + + #line 199 "" + this.Write(" \\\\\n "); + + #line default + #line hidden + + #line 200 "" + } + if (!string.IsNullOrWhiteSpace(from.IBAN) && !string.IsNullOrWhiteSpace(from.BIC)) { + + #line default + #line hidden + + #line 202 "" + this.Write(" \\hline \\textbf{IBAN N°} & \\multicolumn{3}{|l|}{ "); + + #line default + #line hidden + + #line 202 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.IBAN )); + + #line default + #line hidden + + #line 202 "" + this.Write(" } \\\\\n \\hline \\textbf{Code BIC} & \\multicolumn{3}{|l|}{ "); + + #line default + #line hidden + + #line 203 "" + this.Write(this.ToStringHelper.ToStringWithCulture( from.BIC )); + + #line default + #line hidden + + #line 203 "" + this.Write(" }\n "); + + #line default + #line hidden + + #line 204 "" + } + + #line default + #line hidden + + #line 205 "" + this.Write(" \\\\\n \\hline\n \\end{tabular}\n \\end{center}\n}\n\\end{document}\n"); + + #line default + #line hidden + return this.GenerationEnvironment.ToString(); + } + + public virtual void Initialize() { + if ((this.Errors.HasErrors == false)) { + bool _estimAcquired = false; + if (((this.Session != null) && this.Session.ContainsKey("estim"))) { + object data = this.Session["estim"]; + if (typeof(Estimate).IsAssignableFrom(data.GetType())) { + this._estimField = ((Estimate)(data)); + _estimAcquired = true; + } + else { + this.Error("The type 'Estimate' of the parameter 'estim' did not match the type passed to the template"); + } + } + if ((_estimAcquired == false)) { + object data = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("estim"); + if ((data != null)) { + if (typeof(Estimate).IsAssignableFrom(data.GetType())) { + this._estimField = ((Estimate)(data)); + _estimAcquired = true; + } + else { + this.Error("The type 'Estimate' of the parameter 'estim' did not match the type passed to the template"); + } + } + } + bool _fromAcquired = false; + if (((this.Session != null) && this.Session.ContainsKey("from"))) { + object data = this.Session["from"]; + if (typeof(Profile).IsAssignableFrom(data.GetType())) { + this._fromField = ((Profile)(data)); + _fromAcquired = true; + } + else { + this.Error("The type 'Profile' of the parameter 'from' did not match the type passed to the template"); + } + } + if ((_fromAcquired == false)) { + object data = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("from"); + if ((data != null)) { + if (typeof(Profile).IsAssignableFrom(data.GetType())) { + this._fromField = ((Profile)(data)); + _fromAcquired = true; + } + else { + this.Error("The type 'Profile' of the parameter 'from' did not match the type passed to the template"); + } + } + } + bool _toAcquired = false; + if (((this.Session != null) && this.Session.ContainsKey("to"))) { + object data = this.Session["to"]; + if (typeof(Profile).IsAssignableFrom(data.GetType())) { + this._toField = ((Profile)(data)); + _toAcquired = true; + } + else { + this.Error("The type 'Profile' of the parameter 'to' did not match the type passed to the template"); + } + } + if ((_toAcquired == false)) { + object data = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("to"); + if ((data != null)) { + if (typeof(Profile).IsAssignableFrom(data.GetType())) { + this._toField = ((Profile)(data)); + _toAcquired = true; + } + else { + this.Error("The type 'Profile' of the parameter 'to' did not match the type passed to the template"); + } + } + } + bool _efromAcquired = false; + if (((this.Session != null) && this.Session.ContainsKey("efrom"))) { + object data = this.Session["efrom"]; + if (typeof(String).IsAssignableFrom(data.GetType())) { + this._efromField = ((String)(data)); + _efromAcquired = true; + } + else { + this.Error("The type 'String' of the parameter 'efrom' did not match the type passed to the template"); + } + } + if ((_efromAcquired == false)) { + object data = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("efrom"); + if ((data != null)) { + if (typeof(String).IsAssignableFrom(data.GetType())) { + this._efromField = ((String)(data)); + _efromAcquired = true; + } + else { + this.Error("The type 'String' of the parameter 'efrom' did not match the type passed to the template"); + } + } + } + bool _etoAcquired = false; + if (((this.Session != null) && this.Session.ContainsKey("eto"))) { + object data = this.Session["eto"]; + if (typeof(String).IsAssignableFrom(data.GetType())) { + this._etoField = ((String)(data)); + _etoAcquired = true; + } + else { + this.Error("The type 'String' of the parameter 'eto' did not match the type passed to the template"); + } + } + if ((_etoAcquired == false)) { + object data = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("eto"); + if ((data != null)) { + if (typeof(String).IsAssignableFrom(data.GetType())) { + this._etoField = ((String)(data)); + _etoAcquired = true; + } + else { + this.Error("The type 'String' of the parameter 'eto' did not match the type passed to the template"); + } + } + } + } + + } + } + + public class EstimBase { + + private global::System.Text.StringBuilder builder; + + private global::System.Collections.Generic.IDictionary session; + + private global::System.CodeDom.Compiler.CompilerErrorCollection errors; + + private string currentIndent = string.Empty; + + private global::System.Collections.Generic.Stack indents; + + private ToStringInstanceHelper _toStringHelper = new ToStringInstanceHelper(); + + public virtual global::System.Collections.Generic.IDictionary Session { + get { + return this.session; + } + set { + this.session = value; + } + } + + public global::System.Text.StringBuilder GenerationEnvironment { + get { + if ((this.builder == null)) { + this.builder = new global::System.Text.StringBuilder(); + } + return this.builder; + } + set { + this.builder = value; + } + } + + protected global::System.CodeDom.Compiler.CompilerErrorCollection Errors { + get { + if ((this.errors == null)) { + this.errors = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errors; + } + } + + public string CurrentIndent { + get { + return this.currentIndent; + } + } + + private global::System.Collections.Generic.Stack Indents { + get { + if ((this.indents == null)) { + this.indents = new global::System.Collections.Generic.Stack(); + } + return this.indents; + } + } + + public ToStringInstanceHelper ToStringHelper { + get { + return this._toStringHelper; + } + } + + public void Error(string message) { + this.Errors.Add(new global::System.CodeDom.Compiler.CompilerError(null, -1, -1, null, message)); + } + + public void Warning(string message) { + global::System.CodeDom.Compiler.CompilerError val = new global::System.CodeDom.Compiler.CompilerError(null, -1, -1, null, message); + val.IsWarning = true; + this.Errors.Add(val); + } + + public string PopIndent() { + if ((this.Indents.Count == 0)) { + return string.Empty; + } + int lastPos = (this.currentIndent.Length - this.Indents.Pop()); + string last = this.currentIndent.Substring(lastPos); + this.currentIndent = this.currentIndent.Substring(0, lastPos); + return last; + } + + public void PushIndent(string indent) { + this.Indents.Push(indent.Length); + this.currentIndent = (this.currentIndent + indent); + } + + public void ClearIndent() { + this.currentIndent = string.Empty; + this.Indents.Clear(); + } + + public void Write(string textToAppend) { + this.GenerationEnvironment.Append(textToAppend); + } + + public void Write(string format, params object[] args) { + this.GenerationEnvironment.AppendFormat(format, args); + } + + public void WriteLine(string textToAppend) { + this.GenerationEnvironment.Append(this.currentIndent); + this.GenerationEnvironment.AppendLine(textToAppend); + } + + public void WriteLine(string format, params object[] args) { + this.GenerationEnvironment.Append(this.currentIndent); + this.GenerationEnvironment.AppendFormat(format, args); + this.GenerationEnvironment.AppendLine(); + } + + public class ToStringInstanceHelper { + + private global::System.IFormatProvider formatProvider = global::System.Globalization.CultureInfo.InvariantCulture; + + public global::System.IFormatProvider FormatProvider { + get { + return this.formatProvider; + } + set { + if ((value != null)) { + this.formatProvider = value; + } + } + } + + public string ToStringWithCulture(object objectToConvert) { + if ((objectToConvert == null)) { + throw new global::System.ArgumentNullException("objectToConvert"); + } + global::System.Type type = objectToConvert.GetType(); + global::System.Type iConvertibleType = typeof(global::System.IConvertible); + if (iConvertibleType.IsAssignableFrom(type)) { + return ((global::System.IConvertible)(objectToConvert)).ToString(this.formatProvider); + } + global::System.Reflection.MethodInfo methInfo = type.GetMethod("ToString", new global::System.Type[] { + iConvertibleType}); + if ((methInfo != null)) { + return ((string)(methInfo.Invoke(objectToConvert, new object[] { + this.formatProvider}))); + } + return objectToConvert.ToString(); + } + } + } +} diff --git a/booking/templates/Estim.tt b/booking/templates/Estim.tt new file mode 100644 index 00000000..3629333e --- /dev/null +++ b/booking/templates/Estim.tt @@ -0,0 +1,197 @@ +<#@ template language="C#" #> +<#@ output extension=".tex" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="$(SolutionDir)/web/bin/YavscModel.dll" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="Yavsc.Model.WorkFlow" #> +<#@ import namespace="Yavsc.Model.RolesAndMembers" #> + +<#@ parameter type="Estimate" name="estim" #> +<#@ parameter type="Profile" name="from" #> +<#@ parameter type="Profile" name="to" #> +<#@ parameter type="String" name="efrom" #> +<#@ parameter type="String" name="eto" #> + +\documentclass[french,11pt]{article} +\usepackage{babel} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[a4paper]{geometry} +\usepackage{units} +\usepackage{bera} +\usepackage{graphicx} +\usepackage{fancyhdr} +\usepackage{fp} + +\def\TVA{20} % Taux de la TVA + +\def\TotalHT{0} +\def\TotalTVA{0} + +\newcommand{\AjouterService}[3]{% Arguments : Désignation, quantité, prix + \FPround{\prix}{#3}{2} + \FPeval{\montant}{#2 * #3} + \FPround{\montant}{\montant}{2} + \FPadd{\TotalHT}{\TotalHT}{\montant} + + \eaddto\ListeProduits{#1 & \prix & #2 & \montant \cr} +} + + +\newcommand{\AfficheResultat}{% + \ListeProduits + + \FPeval{\TotalTVA}{\TotalHT * \TVA / 100} + \FPadd{\TotalTTC}{\TotalHT}{\TotalTVA} + \FPround{\TotalHT}{\TotalHT}{2} + \FPround{\TotalTVA}{\TotalTVA}{2} + \FPround{\TotalTTC}{\TotalTTC}{2} + \global\let\TotalHT\TotalHT + \global\let\TotalTVA\TotalTVA + \global\let\TotalTTC\TotalTTC + + + \cr + \hline + \textbf{Total} & & & \TotalHT +} + +\newcommand*\eaddto[2]{% version développée de \addto + \edef\tmp{#2}% + \expandafter\addto + \expandafter#1% + \expandafter{\tmp}% +} + +\newcommand{\ListeProduits}{} + + + + +%%%%%%%%%%%%%%%%%%%%% A MODIFIER DANS LA FACTURE %%%%%%%%%%%%%%%%%%%%% + +\def\FactureNum {<#= estim.Id.ToString() #>} % Numéro de facture +\def\FactureAcquittee {non} % Facture acquittée : oui/non +\def\FactureLieu {<#= from.CityAndState #>} % Lieu de l'édition de la facture +\def\FactureObjet {Facture : <#= estim.Title #>} % Objet du document +% Description de la facture +\def\FactureDescr {% + <#= estim.Description #> +} + +% Infos Client +\def\ClientNom{<#= to.Name #>} % Nom du client +\def\ClientAdresse{% % Adresse du client + <# if (!string.IsNullOrWhiteSpace(to.Address)) { #> + <#= to.Address #>\\ + <# } #> <# if (!string.IsNullOrWhiteSpace(to.ZipCode)) { #> + <#= to.ZipCode #> <# } #> <# if (!string.IsNullOrWhiteSpace(to.ZipCode)) { #> + <#= to.CityAndState #>\\ <# } #> +<# if (!string.IsNullOrWhiteSpace(to.Phone)) { #> + Téléphone fixe: <#= to.Phone #>\\ +<# } #> +<# if (!string.IsNullOrWhiteSpace(to.Mobile)) { #> + Mobile: <#= to.Mobile #>\\ +<# } #> + <# if (!string.IsNullOrWhiteSpace(eto)) { #> + E-mail: <#= eto #><# } #> +} + +% Liste des produits facturés : Désignation, prix + + <# foreach (Writting wr in estim.Lines) { #> +\AjouterService {<#=wr.Description#> <# if (!string.IsNullOrWhiteSpace(wr.ProductReference)) { #> + (<#=wr.ProductReference#>)<# } #>} {<#=wr.Count#>} {<#=wr.UnitaryCost#>} + <# } #> + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\geometry{verbose,tmargin=4em,bmargin=8em,lmargin=6em,rmargin=6em} +\setlength{\parindent}{0pt} +\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex} + +\thispagestyle{fancy} +\pagestyle{fancy} +\setlength{\parindent}{0pt} + +\renewcommand{\headrulewidth}{0pt} +\cfoot{ + <# if (!string.IsNullOrWhiteSpace(from.Name)) { #><#= from.Name #><# } #> + <# if (!string.IsNullOrWhiteSpace(from.Address)) { #> - <#= from.Address #><# } #> + <# if (!string.IsNullOrWhiteSpace(from.CityAndState)) { #> - <#= from.CityAndState #><# } #> \newline + \small{ + <# if (!string.IsNullOrWhiteSpace(efrom)) { #>E-mail: <#= efrom #><# } #> + <# if (!string.IsNullOrWhiteSpace(from.Mobile)) { #> - Téléphone mobile: <#= from.Mobile #><# } #> + <# if (!string.IsNullOrWhiteSpace(from.Phone)) { #> - Téléphone fixe: <#= from.Phone #><# } #> + } +} + +\begin{document} + +% Logo de la société +%\includegraphics{logo.jpg} + +% Nom et adresse de la société +<#= from.Name #>\\ +<#= from.Address #>\\ +<#=from.ZipCode #> <#=from.CityAndState#>\\ + +Facture n°\FactureNum + + +{\addtolength{\leftskip}{10.5cm} %in ERT + \textbf{\ClientNom} \\ + \ClientAdresse \\ + +} %in ERT + + +\hspace*{10.5cm} +\FactureLieu, le \today + +~\\~\\ + +\textbf{Objet : \FactureObjet \\} + +\textnormal{\FactureDescr} + +~\\ + +\begin{center} + \begin{tabular}{lrrr} + \textbf{Désignation ~~~~~~} & \textbf{Prix unitaire} & \textbf{Quantité} & \textbf{Montant (EUR)} \\ + \hline + \AfficheResultat{} + \end{tabular} +\end{center} + +\begin{flushright} +\textit{Auto entreprise en franchise de TVA}\\ + +\end{flushright} +~\\ + +\ifthenelse{\equal{\FactureAcquittee}{oui}}{ + Facture acquittée. +}{ + + À régler par chèque ou par virement bancaire : + + \begin{center} + \begin{tabular}{|c c c c|} + <# if (!string.IsNullOrWhiteSpace(from.BankCode) && !string.IsNullOrWhiteSpace(from.WicketCode) + && !string.IsNullOrWhiteSpace(from.AccountNumber) ) { #> + \hline \textbf{Code banque} & \textbf{Code guichet} & \textbf{N° de Compte} & \textbf{Clé RIB} \\ + <#= from.BankCode #> & <#= from.WicketCode #> & <#=from.AccountNumber #> & <#=from.BankedKey#> \\ + <# } + if (!string.IsNullOrWhiteSpace(from.IBAN) && !string.IsNullOrWhiteSpace(from.BIC)) { #> + \hline \textbf{IBAN N°} & \multicolumn{3}{|l|}{ <#= from.IBAN #> } \\ + \hline \textbf{Code BIC} & \multicolumn{3}{|l|}{ <#= from.BIC #> } + <# } #> \\ + \hline + \end{tabular} + \end{center} +} +\end{document} diff --git a/booking/templates/TexEstimInit.cs b/booking/templates/TexEstimInit.cs new file mode 100644 index 00000000..72a3f9fd --- /dev/null +++ b/booking/templates/TexEstimInit.cs @@ -0,0 +1,36 @@ +// +// TexEstimInit.cs +// +// Author: +// Paul Schneider +// +// Copyright (c) 2014 Paul Schneider +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +using System; + +namespace Yavsc.templates +{ + public partial class Estim + { + /// + /// Inits this instance. + /// + public void Init () + { + this.Initialize(); + } + } +} + diff --git a/booking/web.config b/booking/web.config new file mode 100644 index 00000000..00baa9d2 --- /dev/null +++ b/booking/web.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/totem-banner.xxs.xcf b/totem-banner.xxs.xcf deleted file mode 100644 index d07a0c0f4448080a2ff1c7e4f4ec5a59ab5ec6e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48261 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 =hkrLKM5O?6 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ⅇ9BxKak5F$D9#OQ_167;nj1F9M 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 diff --git a/web/ApiControllers/AccountController.cs b/web/ApiControllers/AccountController.cs index f2523aa8..397113b0 100644 --- a/web/ApiControllers/AccountController.cs +++ b/web/ApiControllers/AccountController.cs @@ -104,5 +104,13 @@ namespace Yavsc.ApiControllers if (user != null && ModelState.IsValid) Url.SendActivationMessage (user); } + + [ValidateAjax] + [Authorize(Roles="Admin")] + public void AddUserToRole(UserRole model) + { + Roles.AddUserToRole (model.UserName, model.Role); + } + } } diff --git a/web/ApiControllers/BlogsController.cs b/web/ApiControllers/BlogsController.cs index 81615953..5290f2c0 100644 --- a/web/ApiControllers/BlogsController.cs +++ b/web/ApiControllers/BlogsController.cs @@ -14,6 +14,9 @@ using System.Diagnostics; using Yavsc.Formatters; using Yavsc.Model; + + + namespace Yavsc.ApiControllers { ///

@@ -143,19 +146,45 @@ namespace Yavsc.ApiControllers /// /// Create the specified blog entry. /// - /// Bp. + /// Bp. [Authorize, HttpPost] - public long Create (BasePost bp) + public long Post (BlogEntry be) { - return BlogManager.Post (User.Identity.Name, bp.Title, "", bp.Visible, null); + if (be.Id == 0) + return BlogManager.Post (User.Identity.Name, be.Title, + be.Content, be.Visible,be.AllowedCircles ); + else + BlogManager.UpdatePost (be); + return 0; + } + /// + /// Blog entry rating. + /// + public class BlogEntryRating { + /// + /// Gets or sets the post identifier. + /// + /// The post identifier. + public long Id { get; set; } + /// + /// Gets or sets the rate. + /// + /// The rate. + public int Rate { get; set; } } - [Authorize, HttpPost] - public void Note (long id, int note) + /// + /// Rate the specified model. + /// + /// Model. + [Authorize(Roles="Moderator"), HttpPost, ValidateAjax] + public void Rate (BlogEntryRating model) { - if (note < 0 || note > 100) - throw new ArgumentException ("0<=note<=100"); - BlogManager.Note (id, note); + if (model.Rate < 0 || model.Rate > 100) + ModelState.AddModelError ("Rate", "0<=Rate<=100"); + else { + BlogManager.Rate (model.Id, model.Rate); + } } /// /// Searchs the file. @@ -176,7 +205,7 @@ namespace Yavsc.ApiControllers [Authorize, HttpPost, ValidateAjaxAttribute] public void SetPhoto(long id, [FromBody] string photo) { - BlogManager.Provider.UpdatePostPhoto (id, photo); + BlogManager.UpdatePostPhoto (id, photo); } /// diff --git a/web/ApiControllers/FrontOfficeController.cs b/web/ApiControllers/FrontOfficeController.cs index a2079981..2b13ba51 100644 --- a/web/ApiControllers/FrontOfficeController.cs +++ b/web/ApiControllers/FrontOfficeController.cs @@ -22,7 +22,7 @@ namespace Yavsc.ApiControllers /// /// Front office controller. /// - public class FrontOfficeController : ApiController + public class FrontOfficeController : YavscController { /// /// The wfmgr. diff --git a/web/ApiControllers/GCMController.cs b/web/ApiControllers/GCMController.cs index 69e992b8..722fee79 100644 --- a/web/ApiControllers/GCMController.cs +++ b/web/ApiControllers/GCMController.cs @@ -23,8 +23,14 @@ using System.Web.Http; namespace Yavsc.ApiControllers { + /// + /// GCM controller. + /// public class GCMController : ApiController { + /// + /// Initializes a new instance of the class. + /// public GCMController () { } diff --git a/web/ApiControllers/YavscController.cs b/web/ApiControllers/YavscController.cs index ab43ed44..76b9b471 100644 --- a/web/ApiControllers/YavscController.cs +++ b/web/ApiControllers/YavscController.cs @@ -25,15 +25,27 @@ using System.Web.Profile; namespace Yavsc.ApiControllers { + /// + /// Yavsc controller. + /// public class YavscController : ApiController { + /// + /// Initializes a new instance of the class. + /// public YavscController () { } + /// + /// Auth. + /// public class Auth { public string Id { get; set; } } - + /// + /// Allows the cookies. + /// + /// Model. public void AllowCookies (Auth model) { // TODO check Auth when existing @@ -43,7 +55,10 @@ namespace Yavsc.ApiControllers pr.Save (); } } - + /// + /// Defaults the response. + /// + /// The response. protected HttpResponseMessage DefaultResponse() { return ModelState.IsValid ? diff --git a/web/App_Data/Sql/instdbws.sql b/web/App_Data/Sql/instdbws.sql index 5ce9cdab..647ab8d6 100644 --- a/web/App_Data/Sql/instdbws.sql +++ b/web/App_Data/Sql/instdbws.sql @@ -123,6 +123,7 @@ CREATE TABLE profiledata gcalapi boolean NOT NULL DEFAULT false, gregid character varying(1024), -- Google Cloud Message registration identifier allowcookies boolean NOT NULL DEFAULT false, + uitheme character varying(64), CONSTRAINT fkprofiles2 FOREIGN KEY (uniqueid) REFERENCES profiles (uniqueid) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE @@ -176,7 +177,7 @@ CREATE TABLE blog visible boolean NOT NULL, _id bigserial NOT NULL, photo character varying(512), -- a photo url, supposed to be the main photo... - note integer NOT NULL DEFAULT 50, -- a global note for this entry, between 0 and 100 + rate integer NOT NULL DEFAULT 50, -- a global note for this entry, between 0 and 100 CONSTRAINT blog_pkey PRIMARY KEY (_id), CONSTRAINT bloguser FOREIGN KEY (applicationname, username) REFERENCES users (applicationname, username) MATCH SIMPLE diff --git a/web/App_Themes/dark/images/ui-bg_glass_20_555555_1x400.png b/web/App_Themes/dark/images/ui-bg_glass_20_555555_1x400.png index 6ffeab9d7c07c8d346e79ccb51be17abb3a89781..ea805f98cc480467559625dbee9c1b6bc165845e 100644 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq*&4&eH|GXHuiJ>Nn`~nC=POW zVpw-h<|UA$kn9oU%fL{j#=y|f!octgDAe$RfuYoZf#FpG1B2BJ1_tqhIlBUFfD%ke z-tI2{|BI|PJPYKxdAc};Se#Ct5P#U9%T4g=#2w1b752?7vdt>m%}<2cR{HZ8C-NQJ|BmVG&j}3~&vd+(115LU*92t^dF$(Rp5jF*ys9NG0QIe8al4_M) zlnSI6j0_A-bqx)4jVwb94Xq4Ktqjby4GgRd4E}q{uSC(1o1c=IR*73fpU84ipaup{ LS3j3^P6H1(btiIVPik{pF~y$1_s9BAa^H*b?0PW0yzrF9znhg3{`3j3=J&| z48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6`@5qUc2LE#W4*28OzZ<{^egRz@aPMi#mT zrd9?9+y7(;0d=aDxJHzuB$lLF<>sekrd2W+85m;MaBSg)bwCXap00i_>zopr0F@dp A{Qv*} delta 116 zcmeBTzQ!~`MTITN+uh|q7;r{>zr(=5z**oCSq!8-z}W3%w%x>(0r%1acITJ%W507^>757#dm_ z7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD?*AMbU}gv!qOP4GeV+%|i^0tc*;oj4X5w jOsxzIw*Scxn)qK_4ZG~Ig%{QV&1LX(^>bP0l+XkKb2%y@ delta 134 zcmeysw1;VeiV9njx4X-KFyM^%eusg9fwRCPvKUBvfU(=jY`cjybHhz_4GnaSEJF+p ztqe`A49v9+46FmdK II;Vst0Gc@`wg3PC diff --git a/web/App_Themes/dark/images/ui-bg_gloss-wave_25_333333_500x100.png b/web/App_Themes/dark/images/ui-bg_gloss-wave_25_333333_500x100.png index 670b844fa1d5e9ef651ecce6af3d7491cebf42c0..fa9479fd40dadb9662a98e7db8ec423edc3d6aa3 100644 GIT binary patch literal 3891 zcmb7{S6EY9w}zuqR6t}SM5!VhklsaV1c`~XP(l?@M5IKDln|msVN;|@2StiBk%V3p zAqEAcV`!oG4iN$b$fg8-;N1M@;#{1YHP>8gjAy>-hf3<>L#0QA2y?cwlhWHq-_H%41m$92k%1XM9W{I)IYje9Om^>yA)ULjV9R z3IMzg0RZ-nAH7}z0Q}?tfR#r8fZ96%0O*xoYoc-d;_PF6i0;wR6`Xt*0B}kBzOFVL zHL{vcL^uv|waBt2#XswCbS35dAPdHYl3e2RZ(9302%QzOW5lCFWG>$Lz&!#*arT=3 zxXjd}5QXMt{TcR@SC4^9pms?o?n@k)<-HxZxVZJ!aPJFBrVn9}AUT@onrQ^GLQ^Ju zh_Mva!P#dkYB09O&+2Pnu}4>%sVy?K4$saYd)ORV%A!23K6q{C+cwVO+`Ki>pe~uE__4u2Ll`&I>HtO+Stk)|YP)}n9y$kYTZfAD`hME$QPT0m$UeZ7|Inqx=2-5n=vHQ^rMEGY%kl+Aqwa5es*E<43c6BAYi^ z*c#;zk81QwL%NijP6v+k>?@d0#5eT4ozO3a^t~NW&U=U=U=?+hO}Um0QW0h-y$>^m z37o%;_R^53Uu3R9)!KlYpxA-A@Yn&I_`U(lXn!U2eYh~uS??eP37NPsX@kzOH=7}W zVs5m)arzD6x2f+;l8paL7Sg%$W_JdIO0iw;UW@;0x3zNa+xqY4jBz`BN{qI4TEHr5 zO{%H+J6A$kFC<8I8>K%5aTLPIHg`wQcl`7j2WA z9q>s#f3QaF<>*1>tnnu_lTEajUn`H_4O?}rXx^8U3*)$6sWI9+*WT*L9xFCww6Yl8PJQPsq4S6YF(9L{r-czYJWOsXZQ;{=2@u-D^F z9ZZOp%DDE;HP}1B5`|d3Kxjq{6KA|wFs_+8BVSE08u1qBIWQQh0pj)Yb{wBG1^hH$ zkC^WRBjWgH!}=34 z4M_fZ=eJwy;u~zj+zlc9{)Ihp{la1irR=0}Au7ZtL78>bg?mmFd*)~%@$Rf~?B3i= z#1swCrSgz-ZxQ&N$iT}@4&0XZm6#+0CtC@E>!Y&xe>3-H<}Pb&pZmt}1aqq|CPi^QA&k}DZj^J2K6ORgeeJprba;<+QEfYB>DlghsC`|K=1Kuh zmwaz7XjWq#-)m3~)4cWDrlTA;_Yu4j+&QDXj_cXy7+4GHE|A<@O<=9#ZPM06U4Q8i zT9%S#V~09Bj2y)j@BL{pe4XyOQi-#&SmrX#9`a{?DDZL^<3$gk+iA^Hyoq0lmP?hk zUG!u^9%v;9va{->lx}Aont3fS#ks=|#7>}O5vAJ&Z5<1PN+D}Mi4l_jNFCS&a4#|R zvVx`QA^n5!x8=nvQfXJ`I4Rk?KYWDtB|ys>dQPsQP$0&Cy=sZZR%{mfxrGmya9pA2 zsY{1@n$vB$ub%zD?Pqy*lK170M!U(mM6WPrPl6XXM!JFjV0i+i$m^E1aG1roU+l>s zTP@(dV?S*4<|2q{-i~1lP}ABg>sQ@Re*Q{R$t_YHl|Wr0Lj<|-9VLrEM%WHv6Xhf; ze+WZ~Q1r;f=A{+&Ad7l#41UFJM{+u%Jfr42za#k_g-%Z!a6&xqI)q48)N_{X{sFV+ zmx~WSM5=u6Q2(}qAbXP*izd}QXrQp5*28;NbT!mFx=zi)2RggwzXv^aj1L9z&SRkA zUtL@nzhd;g9m^3cXX@~zJ5Dma3E8)bakxtRIq_h)o)1xRghC}$9_<|d-LlVBKGXQm z!+~fOa7i?f{QGY2Q(*o4Oxmq&*lniF=p z9+vwT9g*%`3)!q4DQ=hlbYyvh2K#6@q(e2#q+w*+Fn)MplweW(HhoZaxIUbsyFpl_ z=ii8p4X_^;i7ZUT=7@W7Ws{@}23Iq$9b4wu8uPK2@qgXtJ!w0mfunNKwX?`cIo8}` zkW@`3GEsZuPpMf)kX3-S!;c+iZN!|>i`0Bm!=jyMNPGBQ2h?pEX2vny^YXdDy7#N!x>xu< z``dF+UIt2t=K4(U-_rb6thE?4ye^F`iVIu(_#!Q3bCk$NaZgCJkGbWOb?dJ6WIprs zdoBMi(ACog{7(cJJUCTK+3Z9H1i|5yAnUXTQp?^y0fnnfM$nK}MFJ9%7gVP~B99F3 zLGfa9);3~KfEnHRo`R4$Z#TurOs`bbWaB-Z@ z2In^4fYH;@$0^cN^`tqywsf!L+L9`*5tA71j*!eoEo1fo=>xd_Mvz$Q3y%nm_%QFC zD!1wgaAg+5Hm-tHP&V`&x5+cfo!|+beQ@+=`@!Mwz;^%I3iK>)Yi^q}a8~_g%&Yxd z7%AVk3-cgXJ!6*4*e_ugPH)q`8i)4kkOhkqEcDeYZ8p>miHN`sO)uV}xB=m#n8nxd z9BYJpby4GZLNdqF9JYf+`;-?L^>@0gOF*d;ieK$7g7l|^X^lkLb3NtYHAnFs zFXaxVQU z9}#Bvzf!IPHCSxkW-qhQ+K5VdK!Ui75qT_okx*s6NY@^PNBU(>0R0 z^9xj|iv_;oGhIRR`OdlXs$#0io=Oer{nslO$u%C|M18h=NXfa|8+;3FMSJhc&@bZ9E7|P3Gs;u3wzXcK(bj8z~rhV%mv`kCRL1EPUsy)`jTlXbz*0 z@e5Tu@#+OlqvCDUq`4OjGwYQ@VU~Vpm9(=8ZoDYM!L!w5%#W&ml5?$%)v*_1q10)K zZd(`+)LMzXf*0>x&=6I01;$RiY z)G^K)&xBD%gBnKoSpu7HOv<-;;kEht|8bmVkqoKs@8@acY(>wmaDi53XbZ={U_t#d1p0<*uFo#*x z?4-8&^C040(7?C|R1B z+=7oSfKx-HorFk^S@6*MH-}K=c`Z>Dv zuZ-dcA$^(Te*0FP93zYA?;0*vQsyg$_CjwRZ~|?z<=#0y=jev}7?&o#Cc86N%$A%l z%gKLLm^82l1+Cr`Vf!gD)#Cl-i~7U6D6)91&Mv0!s8o*1w-owl#p<(>6$Jx*{*6Ys z1YI>PsDvLJgZB4lQw?9v$87>jtTHL#A$z^eJdk_La4KYLLfb()&ev zs}7-buv3XPSwH2@aSor)Dl0jV-HsL7NI|Hp>kLi7Bp#Mslfx%?O0ex< zL99ccB0_o(A2b<|DQs_RbKGo8rL@)TPZi5c_2UK4-hI_{_f?TLw_amn!F@Q2Bh}8NItx<%u-2_6b~FlLrrW91`AU@&)kDsu=NBEp5+I~}3cT@D zU4eC6ir1eEiIADmdZH3#`6elsD*Adon+OmD7+b&4@&sP;UU({xys&=GtGNgu#7ri^ zZiGQSGT~0sKMSsJJyl=K9`dZAehr?kJ~Und4?Uyz9N`1-lz(0ik}wg3Sif>cj?LI6k76V}ZPK>@nPF}*JGcoZ zAlZUzx0%S~A$y6i*FfxSVb8o%!-08Fx+Fvo*8&DdG!?wCy1t?s|sP4i@*UCEJ7dhxQS+fqPd5qDYJFP$~Lop`sotmrIt1Ye0P@=0^ZKnhaH8p8Je zLk_50DU``OUvqp$^uxlV+_+Jz>G`1jl;Nu{Y=y}j+ZA*pRZ;}ueYseU0*>428H-6ym zuy8OuI*`ptY}q~;Y$q_5bj*owxDN}Ki#kn>)uRj+Z26wLH{%k*ks(({y~R}O2rQ%> z87u8h>z_>XE>#I%{=g4~PH0P$Uy_$=P^Hhl+g$z37@Iz>Ssb0ZMT|TvlW4y5B-H;{ zqIwbDM(T^Vo;8?v*;vce{#gF4Rg#iZl?qciN4)i8`ojVV@U+2{{#YlG8kI9 zaf_bl*-q-c?|bsa)EPdsD4)RgrfxG6!7WE3-qB+GZhw!2{V57jCfEJB42E+jOI_{V z69r}eE}?_ClO1*zwSVSm1>V;9&Rj`qpnns(YIWK7EWVkdO;@5tYT+lxUF~N&j3mT{ zOwo8g0oU$e?mDSJ_4n(#d?QNL%Fpok7e05aa;se__0~(#NtEv_(O&rLY^S_NZv6W5dy6 z0Ac=Q{fl?KopWGn!Dq9+oCb#Q}p>u!I?Q1V@%-WkvFO zaLMoQf{VOO$D8%OUL5aoyE@(4o*&cx+1DP5Y35w3MkqKk5;Cp2p?*dy1y$=g*`pQ> zgBLXLF%a|O;}Ip5W`BxjeXi^EF}f{9Sea97iGaU3r=Qlze_u1)#qiqZUCt8;h) zLby_U%58)A(+p%#UTP|q1AuTfc9q^+s9lryvF5w^-T`*0-)-Bp;!#hJvq&D!hsgx_}f^^7ggDw->0z{ zOHLkeNWWuj`E3E#G>8G!s1&I>I?2@dpCwOst@7i5I}8+&;1{=dR>QG72u2ga$7Js% zJ4vTwwvIYYG;M6x0Da#W#GIsFLrhTHN5xCR*WFgd2@_M%m=OfA`imxs7h z2xgsJTsG$^HyIs;IGp+w+1fPXl+T!OmNIm?no*FZ9a{J21<+&gsNxx)m@m@ zug?lRztIT2LM?NxR6o9Ftd;GslY&s)WzHA50#`#clrUU)fto?|^; zd3YZ;sssSl`l-BC*ZJ4Of>eXgyk_+6QB+xIuPgJ!;e1F}LqHf>a%^;^di8>6S8P^= z+bf(=Vrqt()w}g^^e-#)y6e2&R%eq|=~k8=0Z0NElm;$a70eoJNl{y?{w){~X^KSs zD|+YohbQ5k9 zEdR>@`|YMM;Ktyw3E@knOMlNKRZC%T(_%3Xwm*h%d=Mo~dB?L#-~i>LB_;IK;hf~* zgIRv*X{7|kOCoL>SE5A$sp~_xtaCrm2$J`4u~QzgcT5S%*hbIBTt=%WBa(W3Qs(YN zNrzd8Ha}PLsWJeHcr;^XE5!h7mYaA!@`owVW9R1jBz`Eir1zS=D;LlDoGTqRVYXcu zd}X(7CEE`v++)?gpjkr6Q$Zof{>*mEa+ak=pCy!60X6Vis}!oA{g0vU6P7upyBeZ! zVz`6X9E+A5#z&vPlrQ_+-doII^^6q!SYy9~F9O3?Z350mAjPD!W^__21Lrw~u;Yjj z2e<$hs=TU&4A$W^Zz0pIxi_NxYXEbdAqG8IU+S5d zoo|cq3S1A22GI!heF7}&3C{`L46yu1*lYSN=CjC$)ta^mN&iC>MKx%4SOS$s@Nn{1`8HtQR;;bQx? zbWa}t(~|SP7~901`jEkY`0#2T{&U7Q3F$l5A1Ki8dH61?&AMOW9gE?exN`-!r#!sY z%;>_Nn{1`8H|ArhC96C@%IoIkMO%z+0d4hS4Q5ODZ_L3d-KKJ#H?Ha9yq zH*>aQR&2-a^7Pb8&WMqip=M-Yo0wphkdT*@ucptHiD0*uo3zfEpM)UHx3vIVCg!06DH>`v3p{ literal 275 zcmeAS@N?(olHy`uVBq!ia0vp^j6j?s03;ZUuHXC*q?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z$O!dxaSV~ToSYyLQ81zLqX6?}V`gt{=GoHBzd6~?`LVsrV~dMt`^U-C zBrMsYZrtIQEHM9|g5_k#DzoMso#s6@&4--Xma4ElJ<6lyFL~vP#Fa}gni=vl6~&HZ z%~kH1(btiIVPik{pF~y$1_s9BAa^H*b?0PW0yzrF9znhg3{`3j3=J&| z48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6`@5qUc2LP2ncG28OzZ<{^egRz@aPMi#mT zrd9?9+y7(;0d=aDxJHzuB$lLF<>sekrd2W+85m;MaBSg)bwCXap00i_>zopr0Fl5h A_W%F@ delta 134 zcmeBRzQQy?MTITN+uh|q7;r{>zr(=5z**oCSq!8-z}W3%w%x>LD+h6cJumLZ0Q zR)(fl2IkrZ237_J|2^eb0+p$jxJHzuB$lLF<>sekrd2W+85m;M&?mB76sUp0)78&q Iol`;+0C@%{e*gdg diff --git a/web/App_Themes/dark/images/ui-icons_222222_256x240.png b/web/App_Themes/dark/images/ui-icons_222222_256x240.png index e9c8e16ac5e7f61c843fbac290ce30c5de7e40b6..8a188cb6e06598c670de4be1cd49cf16d886fece 100644 GIT binary patch literal 6837 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 literal 6922 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 Q^5g_ikWrPcku(qaFX2Wbe*gdg diff --git a/web/App_Themes/dark/images/ui-icons_4b8e0b_256x240.png b/web/App_Themes/dark/images/ui-icons_4b8e0b_256x240.png index 3b237fbaba92ab8bba2976173eb906ae71ac9474..512a87b789343451e91edb60845a8fda17175c3f 100644 GIT binary patch delta 167 zcmX@A{8M>?vO5Dyx}&cn1H;CC?mvmF3=9m6#X;^)4C~IxyaaL-l0AZa85pY67#JE_ z7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG%jM#VnH%|{qp`Mpha4GeV+%|i^0tc;AU zjDcKJD+7Z#Ey+fpX4Mkch?11Vl2ohQ{FKbJN(LhXLt|Y7pdynH14}DoGb>{YARDOR Mhq;#7`dMsjh*suAyOwp^25LrIn$P vwt<0_fq}uw%+)}JswJ)wB`Jv|saCo9DVb@N3`Pcq*fltQxhOvQgrFe+K~x|I diff --git a/web/App_Themes/dark/images/ui-icons_a83300_256x240.png b/web/App_Themes/dark/images/ui-icons_a83300_256x240.png index 4e7008e0bb81b1c8686039164844216ddd00350d..c3008c6afa46e0099a88addfbf002882f603333f 100644 GIT binary patch delta 167 zcmX@A{8M>?vO5Dyx}&cn1H;CC?mvmF3=9m6#X;^)4C~IxyaaL-l0AZa85pY67#JE_ z7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG%jM#VnH%|{qp`Mpha4GeV+%|i^0tc;AU zjDcKJD+7Z#Ey+fpX4Mkch?11Vl2ohQ{FKbJN(LhXLt|Y7pdynH14}DoGb>{YARDOR Mhq;#7`dMsjh*suAyOwp^25LrIn$P vwt<0_fq}uw%+)}JswJ)wB`Jv|saCo9DVb@N3`Pcq*fltQxhOvQgrFe+K~x|I diff --git a/web/App_Themes/dark/images/ui-icons_cccccc_256x240.png b/web/App_Themes/dark/images/ui-icons_cccccc_256x240.png index 9fdc41a9d0fd9a771f08b671fb69f1073fdffbc8..d5a2d82e01b594d71724f61e12a5a0c1cb446cdf 100644 GIT binary patch literal 7038 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 literal 6975 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 F{tqJ#L_h!l diff --git a/web/App_Themes/dark/images/ui-icons_ffffff_256x240.png b/web/App_Themes/dark/images/ui-icons_ffffff_256x240.png index 4d66f596e5967a460a37526e2130a55711eeca3c..6126c4e3ee091b13a3dedec98cfa98a84104f40c 100644 GIT binary patch delta 175 zcmbPjc;0Y=N(f83qpu?a!^VE@KZ&di3=E9LLGDfr>(0r%1acITJ%W507^>757#dm_ z7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-G$+Qd;gjJ8(nUTdzl@UHc-P4b1k#USrUc-w2&{x delta 105 zcmX?aINNZ7iUL!Tx4R3&|Mvbf`xzJ*I14-?i-EKU7`vU!w%b^7TU^Ri*T7iU&@jZ% j#LCpt%FsyLz`)ADz+h$O>dCnhxTGDwToj)?Pr?uYHcK5q diff --git a/web/App_Themes/images/star-939235_1280.xxs.jpg b/web/App_Themes/images/star-939235_1280.xxs.jpg index 9ddad28f0e83b2154561776e71eb39f77cae2e74..ac2772a267e99295d2a0d14a9b2bff0422a2b009 100644 GIT binary patch literal 6540 zcmb_;2{@Ep*#9#_y@ZS<*@|Io8Os=kY*{ATkhL-;#h4)>YegjqjqF3im?+DLqL-PF zLS-w>poOxfqAYC)X|a6IsJGs{*Y&^t-*tWWoH@^Vp5Jrd=YD?YKKHqodzW7VFgq(- zD*yt405;$cSRMc@0a@`4QX52Nr8Y{*Z#ZC!0pV*9gu%%{cIYb&`%Ae-go)s@uUG&S8SBYYw%|JSnI1c>kf z?a&w=hzI}`f$)exmRkTRuB%*ce+vZ4!@CBAB>#f|Txhu;5afXXP#6yk03e^ja2e}1 zmN>n+=>*6Zn%P0Ay7&O4H~?z)+u)Cp;h0oqjZ)2D0q?pVLA;&hCCr`h>k8YF#FDN^yHjqa zOVd8-B#qNbvGVxUN=9DivUds8Rg76SYF&&xU)ok|Ylh`jau`Eo?Nuv_kZ@CqBTJaA z(ptYTm#0%$k=>|I2w5MMn;JzaK*m)HC>M9!)dLvW&Se1>b_k0tzqKId+_A}tsF{a# z%w0t#5ijzpir-w_5wQhN!PD0=q;8<`iRPpfTq(0q6c0nj{8EM9fjEf+X2QEQ`*V}% z{OXYnl^?bxq&AmCJi)X` z9M=4jn~7;QtV83=oI^+vs4!$Qh2_Jb)&yw=W~2C#WToE|yK`OG!t-KmcIF9Y=fqi$ z4zun&l9$A;zy}XH>Sy1S(J%BN$s`7itFE^N!9*ihh!O{*2o%*Au!CMQkzJH9^xex2 z#p^2<0R6dd zVt`U%M=g718>2g%9hrw1Jg?hHCb?II1?fc|@~CO0+~?G3VCk?-)=w~t-cU=dSk{-mm;3*FZr-KZ-A zEzl|d@&Q;AQws91zW4n;2mgH*68?J9Da2<6-K3$Y`lIDNqCIVwO$ZxldA9X89ZKFj z4eTH>IKKKi_v3zl2A4+w0BOJ$zV+zENoP9~LI%WKx8CfiKN=ZCV(XP1EbSQU@|X$n zzE<*nLYn#@k)9Yv&$<5}6V|3r{cb$<-dR=V-#ge4JQTWhGSVQtQ!j(qT@s;UKZYSV zm2_A!u-sLo`EaA)5BVJOf$b_SpnBE2A_e!%aEMeq(m-N))OI46ZN+6i6ZEpWpuh)g zA_-iSY?Q*97_&drfyu68-D-Bo!uY2V&kVbRP#&|gbIFHz;rg^v&5}8c|K4*Qluu4e2r3B>v zoD?vTolByy?VOzS^y)uWr1l0+@Y6F5ZYbfTskNjIoc9B={ov@ZN6Tuq027buB$_kv z5(C;11QS+H>FBd)rv!gm)@m> zb7msOT#=9Q#O|7)>tGn*kq>Y=_o-I9%vL!o3?2^(Fn|Yutl^#=JU?NG2n;GJukCaI z1?Mp{2M-E95QMCO9*%FL`m5QwWHHom&rklC-O%;GQKB}O%p#3+S^ZX#!TkpruobT3QzD_H3x~c$k$nTeV-lF*p9Ut zs-2Q?D0$+SezV|ue*i{CsN~7P>-`pD-KC&3tn{UWDfdemKW1NX!OkUn@h#|!7NJ!Q z_r6hGzaVSS4I6;T>^m{JT~oSqB+^-=vfpv%bUoEm%`Q4cOm{Ho^WO+fVf~FVw(pr6 z3y80l0qb^Si>S#jp64%qah~1pHHU;tGZ*Lk&wicKs*5&;fA6|wTfrB-bgeZ-p2PUIJ1|Ii5%2J&U*bO7qL!WIt%vJDcz(EQ026C}*e<^7+|uRQx#SMyai_p<|a5gMX;_ZIZnTD(zj24wEU#RnU;@_Dn zHKc`pmJ{u7bN=Wd{O|-$U$96^Z&psUsj2JT3y`ACU}XY@f-L+)5CDikV4?@Wn1Y(E zFa+f?^zzDqa~`nkiHc4X-DRL?%#or?9??haNRuroPJA>R>Cl#1_N4A@)VdN>_pAw| zJ^ihB&TKKkKFZU|%F1%|221gWz>}k|Y;>If8G!S-f&Y~O*l9t);cY1Fda*a0CM9fR zQA)Q`$Y@VrtbQUMw}o&WejImA!==PTddt=;gt?ddrXMztKTtGHzI;qsXEZ!AlsZ}T zIuB~pn3kJ%3jC^#uvHZnfCADl{oEdFw?SXllYdIe%U>Id7i8fV4`7_YQBKbC+Fl{% zW(WS+Sli&;>gwuZLM%65JJrH-D~-7#S&ia_KSuw*J?m(ajI)ODlH$=bA}mbdy(Kkv z(A1tc)q2duC)v+mCOV}E;w&wsKO+J;nvI$$A(un^La_Wegr{$au}FozF3a5pByQT$ zdvrz?uANbwflX2j1}U|-m(hnuUP~r<@g~4^^@U((EV0dAZ_OJI2j_2)OAe5K2+AXE zf+b;0=62Dzi>Tj$qG?#1^!v}ELs{8FC%J&x7Qa>|rM)9Pofd3wZ|_csls&e`uYx~4et!FBf_NS268Ov!vhE z@(%4FX_Msijy*gxF3B$Rvp1^O89y>sRxFZM+4AjMPSN@x$vh`V@lUkid+i_R6cSU) zu2r!%mqx4N8E^EzQN`K{7->7L#0I|#Efu50PY5mOqWlvYYSW*;wPZ&NoJdp81w&}1 zh+j$Fu=n4x<*`D-N7$X?+si)G3zc%b@(P~&zZXr_LxJDL+bw7x~9t~-_QO=t7}?fG7ya2 z5~_bKO@`gc{di7V@E5AYsOeQ=A%OEqdk+j;rJ6QQZEnaf|7Tu}3N9^(u(Zc*!_K0Z zqxn$qUYYQmVt2T?x1Ho&f1ndK4K<90&t5t;c+gaf^DlH|3acE`lUEg}3Vc_3>i6wfjcQy}&v9&XQl1INB?+Qe zqng{Bh0gA~i*53|$a$TgxWd#Ax4C^5Z8zFbrV%3c_{pMwIoFeax6Vv8ytkw|e`$WW zePj4)H19F8=F$h<^5d+hMuRh}QH?Sieae>|w-y%fr{;(KVHTVs0CjYTc*MGW^GBWV zxBo%G6f7^Yl6<)*(a#(V0YqT(PH>d7c8K}!vlywFY zjy2$PRakmAJ;oM;K|3I*eNS-DehFlk;_e_>mo=G^We8T!Teq@Z74CgLnN3D1AZL53 zsN3nV17-=WiQ6dK5KFcTnP@cXE8)|NaGw`vU8$@29Re7unl>+}>_YV=wF< zez7pZ^#^QAn#P!EaFvm<0?5XKk1S~lJ3xm#f=0hHU66lAq}cjG*$V8f6a%=4fnqY} z+l%X1iDR!R!AZy@jMbw~qSRVaA#VhYc5(OTm(U@NMt&9a-G*aOoWGoi9<8>npS=Fx)?^AI&6z<>?5Yry@NWHZ=+jc`9_d! z$iBDT%9E)B0byE~3j=Gf-WBwQ8FDx?fpdCA!|s6>Z@tn`OnM26leJUWRM3F->1s}7}xNX5_F}s_@}}GOAC3hw$OGC zF+1=hu(>4`%!V8%Y3uExzz0?)^58j+1;qlE3m4(auR`f}$@Ft%9>U<$4c^~^tP+;l zBs1;V=ND*mKA%QW-p7bzbJ= zq~B^e%}+S^GQuYkER~fJ@S|n$(q;#tboQZ*4yti*fd(71hkkl8_#E6?R*1QL7D!R7 zO%u0<_N+N4?c)>8vUAp1I};`)wO!m9Y*?cBZf3*z_kR?P4h}#vO zD*pMYjxCki&E^Sb3&(Tp9r)o&mn>j;SJ;wm7Gm~IqQ+|CaiaVJgUA|;u7wcH@0bOC zgS(i0Vv0M*0HJU`yV{aKz=Ji7kg&N1Pq;$7{6l}Vw7S?vAKKbZEI5IXkXMjRkf<_@ z0D{dea3iJ4-gGJ%d=7hUNs!nBSM3~k*SxXgW=>oWpRR>0N@zXuZn-Y%rkIw0zND^s z0m{-TP=i?LCD5QFBqXzWs|06{`wV~l&}ON>So-jQwuI>>uOmXk-cr1h=0PTL3#`hK zVeicb-d|div~Gftq6*fm0E8C`;Q^y%C04i@5RURPcXpBwd6ZH0YFhjBRKc%O6lPz0 z>)OP;2eK(LN6K4ctnY|5^JF%$@5sKDuH*U9ea|AE`)azDXd*jhs&3Ki{x4dl{U9}X zAi1|h&~km;;#St&aBGr;%ImLsl8&upJjvA}y1sdiVBsC@nrYuxgHo2#AvMP*y2r}6 zF9X21pF}6gr|Q#*%qz`CrN_c=XdkQ89_pvGCdoOqQu;N+TQavPo!-HyYe;Nw2`1zo z*fm#eC|Tv*nquW8$VzR!aMzn;+w*1q7J5P{1%r{wIFm5!zVmiKXQm`ow&-rL4}wC9-b&z~WJL<} zs60hIZh18fxkuL%e1#dk`6$6fE+XsBXmd*)nfy!)jh?=%K%tm?4y03;0T|gUKlstD zfRpR$o)^4~0bj}s@XqtR1ZRdn%xdB%8 zFi#H|UMjtgsU>QvWihMr$h2(E@rsFr@J2Tywa^mOISNj~RBaiEU%0pM_vqcRWnh0t zJbUgG>O=J3qo`=dQSGqrcjozpvE^(n?BZp)&qQ?Br0L{~8PjKPmwZ*UdKNlAE(70| ze3y35Y+VMjXG~dhr}S;V-KMdd=T4y)?~R_SS?HR7vY53D5OsBRwX1Hoc1@udHZ5*i z^vq6P23!dX7*w*Cq}7Iag8Ngw6vBkpuiwe|vxVOUOeCU^v-ojVPk#BF?}Dar)4af7 zmTM;5Nj0`DDRH4yG<^%hS8)+Z%vJFWb z?H1+N(f9ia%{ifBO-M=ooZ&taM}gguhA3j>tw{uFF!UYnOM61c1Po9)n9ilGX{uFi zSSD#uLOB8{lE7QJ-8oK~BG0EG#SiaRU5>CuI|XxZlVr+K9}q}Y43gCEkQ zob&A~yR~w$z~Syz}J@AJZQm)X_^DQn9)oJHX_@TlwiA3a~?hX4Qo literal 4116 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 Z%PXW;I7?H_>uvZuO==@r>!Y;6e*t~|5oQ1Y diff --git a/web/App_Themes/images/ui-bg_glass_20_555555_1x400.png b/web/App_Themes/images/ui-bg_glass_20_555555_1x400.png index 6ffeab9d7c07c8d346e79ccb51be17abb3a89781..ea805f98cc480467559625dbee9c1b6bc165845e 100644 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq*&4&eH|GXHuiJ>Nn`~nC=POW zVpw-h<|UA$kn9oU%fL{j#=y|f!octgDAe$RfuYoZf#FpG1B2BJ1_tqhIlBUFfD%ke z-tI2{|BI|PJPYKxdAc};Se#Ct5P#U9%T4g=#2w1b752?7vdt>m%}<2cR{HZ8C-NQJ|BmVG&j}3~&vd+(115LU*92t^dF$(Rp5jF*ys9NG0QIe8al4_M) zlnSI6j0_A-bqx)4jVwb94Xq4Ktqjby4GgRd4E}q{uSC(1o1c=IR*73fpU84ipaup{ LS3j3^P6H1(btiIVPik{pF~y$1_s9BAa^H*b?0PW0yzrF9znhg3{`3j3=J&| z48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6`@5qUc2LE#W4*28OzZ<{^egRz@aPMi#mT zrd9?9+y7(;0d=aDxJHzuB$lLF<>sekrd2W+85m;MaBSg)bwCXap00i_>zopr0F@dp A{Qv*} delta 116 zcmeBTzQ!~`MTITN+uh|q7;r{>zr(=5z**oCSq!8-z}W3%w%x>(0r%1acITJ%W507^>757#dm_ z7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD?*AMbU}gv!qOP4GeV+%|i^0tc*;oj4X5w jOsxzIw*Scxn)qK_4ZG~Ig%{QV&1LX(^>bP0l+XkKb2%y@ delta 134 zcmeysw1;VeiV9njx4X-KFyM^%eusg9fwRCPvKUBvfU(=jY`cjybHhz_4GnaSEJF+p ztqe`A49v9+46FmdK II;Vst0Gc@`wg3PC diff --git a/web/App_Themes/images/ui-bg_gloss-wave_25_333333_500x100.png b/web/App_Themes/images/ui-bg_gloss-wave_25_333333_500x100.png index 670b844fa1d5e9ef651ecce6af3d7491cebf42c0..fa9479fd40dadb9662a98e7db8ec423edc3d6aa3 100644 GIT binary patch literal 3891 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 literal 275 zcmeAS@N?(olHy`uVBq!ia0vp^j6j?s03;ZUuHXC*q?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z$O!dxaSV~ToSYyLQ81zLqX6?}V`gt{=GoHBzd6~?`LVsrV~dMt`^U-C zBrMsYZrtIQEHM9|g5_k#DzoMso#s6@&4--Xma4ElJ<6lyFL~vP#Fa}gni=vl6~&HZ z%~kH1(btiIVPik{pF~y$1_s9BAa^H*b?0PW0yzrF9znhg3{`3j3=J&| z48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6`@5qUc2LP2ncG28OzZ<{^egRz@aPMi#mT zrd9?9+y7(;0d=aDxJHzuB$lLF<>sekrd2W+85m;MaBSg)bwCXap00i_>zopr0Fl5h A_W%F@ delta 134 zcmeBRzQQy?MTITN+uh|q7;r{>zr(=5z**oCSq!8-z}W3%w%x>LD+h6cJumLZ0Q zR)(fl2IkrZ237_J|2^eb0+p$jxJHzuB$lLF<>sekrd2W+85m;M&?mB76sUp0)78&q Iol`;+0C@%{e*gdg diff --git a/web/App_Themes/images/ui-icons_222222_256x240.png b/web/App_Themes/images/ui-icons_222222_256x240.png index e9c8e16ac5e7f61c843fbac290ce30c5de7e40b6..8a188cb6e06598c670de4be1cd49cf16d886fece 100644 GIT binary patch literal 6837 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 literal 6922 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 Q^5g_ikWrPcku(qaFX2Wbe*gdg diff --git a/web/App_Themes/images/ui-icons_4b8e0b_256x240.png b/web/App_Themes/images/ui-icons_4b8e0b_256x240.png index 3b237fbaba92ab8bba2976173eb906ae71ac9474..512a87b789343451e91edb60845a8fda17175c3f 100644 GIT binary patch delta 167 zcmX@A{8M>?vO5Dyx}&cn1H;CC?mvmF3=9m6#X;^)4C~IxyaaL-l0AZa85pY67#JE_ z7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG%jM#VnH%|{qp`Mpha4GeV+%|i^0tc;AU zjDcKJD+7Z#Ey+fpX4Mkch?11Vl2ohQ{FKbJN(LhXLt|Y7pdynH14}DoGb>{YARDOR Mhq;#7`dMsjh*suAyOwp^25LrIn$P vwt<0_fq}uw%+)}JswJ)wB`Jv|saCo9DVb@N3`Pcq*fltQxhOvQgrFe+K~x|I diff --git a/web/App_Themes/images/ui-icons_a83300_256x240.png b/web/App_Themes/images/ui-icons_a83300_256x240.png index 4e7008e0bb81b1c8686039164844216ddd00350d..c3008c6afa46e0099a88addfbf002882f603333f 100644 GIT binary patch delta 167 zcmX@A{8M>?vO5Dyx}&cn1H;CC?mvmF3=9m6#X;^)4C~IxyaaL-l0AZa85pY67#JE_ z7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG%jM#VnH%|{qp`Mpha4GeV+%|i^0tc;AU zjDcKJD+7Z#Ey+fpX4Mkch?11Vl2ohQ{FKbJN(LhXLt|Y7pdynH14}DoGb>{YARDOR Mhq;#7`dMsjh*suAyOwp^25LrIn$P vwt<0_fq}uw%+)}JswJ)wB`Jv|saCo9DVb@N3`Pcq*fltQxhOvQgrFe+K~x|I diff --git a/web/App_Themes/images/ui-icons_cccccc_256x240.png b/web/App_Themes/images/ui-icons_cccccc_256x240.png index 9fdc41a9d0fd9a771f08b671fb69f1073fdffbc8..d5a2d82e01b594d71724f61e12a5a0c1cb446cdf 100644 GIT binary patch literal 7038 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 literal 6975 zcmZ{JbyO72*Z1zilCpq=2-4l1k^&+fg0zH$lqf7Ev5TmSNH;9qUDB|yf`rncEDh4# z&BDv~_q^wQpWk_&-ybt~X3o7cXYQO^pZTJvqeenRO9TJ_NYtOH7ytmkyCGno0Qas} z&G1mXBOqH%H5EX=lvR20-Hh<zcWNgc_S(M3t4F~H zg)D2WzlyIFzRx|!n72#%OWvHQ{_EGwsRGl-(a+3B7J`vRgr3O@2rQZ_G$Q90b-9{0_?QNM11ArLJ$W5@7fHAW8{mX!QsL*M!6Sc{Q_=tIfOfXKC(u-)S~-(46~aqRBcAg za_Zj0V$I|C5&Dq}fYO72 z^5?8$jVwRPly2)k#YR1mDBL5A*u-oZ&bL^%_YPLs*XD}WHkoZMAC9ezFq#;NNUbB3 z=uV+?h{<7ZReKm7jhj}LxW;zf(n1!mkR660qB#BWwT5-)A~>6uR?a_Hb=!r#~!Sr87#8(Be+yRi z^0W=w!Q?__=At)CX*VPdPwb0=lZDp@C7lCoz#688C`9=U`4-MAZtumS)ai1tK(WTM z920w$_`JPUraWw6SV=&efMoS$(j^!#_1biofv{^1XO%h?%vHOLH_eo|DJT!JKvo|8 zsrE4##sM?ZmSgJ+U#HW4blO!^r`oG%))Z+y_97sGdQO^d^>-?k*#qo3gR?u=tMff# z2AyBcL^K5}Z*br>#hcgRYbg)syZSRyuG_!F5v5v-)zB6#RcygU_Ei;|NK(1cDb=^M zXt9)b2>kVKHVhG=MQeNgL9XCMniW`i-3urBQ$B-c);3CJ99zZD64_JI&iQULERGpE ztBjtg#Fe-+hW~o@2k_6G#ESnfi9Y-n;_@7(wO;LoopeTTr zrf;&Dk57EIfU|NO9@8$I8Y_cl)?A3J|lim6kId^2s5A*@!XARMnDK^N|Yx0RJ#u+647k9%=p^hjALwY`L+_poHQ zJ-_Hv8^mkxWzcWAzWgm5WBRhryuV1Q72GTxd`ydi4RS*-r7X?)tdbXW*`nJ$zD!u* z@L}Ud$yxiJnBnuElc!!+)V?+u`h$$}83-PkiN4!pc2)O(r1+ z@H`j}3;>W*{Go_@3JH1@8rq#sy^!>Qj`Sbf`}aq1xi^|-3G{;j=(J9Gk_vRkYkkh> zPFxi3N32Sq(HwfU2jHhLW&u@B#l!V5cq#~D!uWx4v`v+WHkwj)n)>k zO{$)+>jSH&Hs;@4d23S>`V-m&T)JETEEhdXm5BPRV|bRhOE~TcNFNKHe3>9eTO!4; z!rHSdksoD4_SbofSi{0-*4PVnHMp9s!U3qsu4+}4k?}R%Behmmbm%Ls50=qDl7JD}NyC?7!s?2=Ph1qbY~+)U5w2(pUD zLIIGT@h4A_vv?pP#}qo*p4*yaTBlY5L%i#pn_S-Ayy{~BUl`1EsEI+o@H1_Rc>Zuf zvh&)&PS(yNpL01jP5`Q~vQQCoYL)PSYGb?su*iv=oUf|u%W zcNbY{@y>8qU86$h(6abNA$Yr3pYK)R(0#3`SNAgc(XVv>dt7;{4k#E+_*~o;4~yBrH5b=oSOH^-|Ylf36y+~pZ3UPTQmD0jW;!w zW|Ytnap-hBwG^k&HFM6aAIhcYHu?X z;Z?v+o;*0Rcq&q^QTz1I5 zpz0mbSBqF05}l$%+-rK^8?1^Gs6(VA%(=k>CsD#R%vEStXy}brZcPkoe`<H@c*{vGthYu+PwMV=>{v|2r>&Z#KKNLtmc&%-!Xw3mFT zEvy>nds$IV^-)-?!x-QTL6(ie!R(06q0X}WkyJnFlKTXAky@h`QafwFe$F%*XPLj) zh!udLLh*O)9p!h_g<>@y0oy>)vglbWoUhwMlKUDJ%NpH{2BmECgVTPGvuJJls82g_ zhlG0`4LQw77h)U|P3w6Fk9y9#&gV_CgD6^&(G#!7Rsqa=63y{CF3B*%^RY!C35-F2 z{yYBrm!ho^?Lh0#b)kCLh|<_!sSFU z5NaoPARwXSD~QQ++vh6DUGQ-!(7S@RK57t5&CK7HB!o-F@$;m!T-}C0$g@&SHp=mX z2$lN|@G9f`8vkpT>%UA}pN!G2v3xIo>W;a_$@*%X^V$a6FZ$O}zHdznYee7J$OzdI zof|El5vP7Jgpq&vfJO1iby)Y$_4uhf+zJuGdlZE<>H$$i|DJ6%*G4B=H3VVutdE}No20z>UxPP=ZINQD=8GN&~U7a&{+v;N!2~zuBgvT zbDZB)i%WR z1(Af@yRE`ah(J#R42f*|L2^yeUhw7J@Tv$ikdv4<@FBg>u+=IFU?&b;@Fy`bHngW2Wg&w{e)7A1+DR_JlBYq@gS?QWI5h)T-xiZn5?j zys(7!z4mpjjvD|DJ}ceW(xRJ@fnsbB0+!yj$d;Rw`9-?^BdSCq2z_)F>{D?YAI>ec z?z^x`k1#=QddUiWtt(cF_ljsLvrA@(%Ok5*_U^Tb!^gr`akFu=2A;vXN2VKRju%ko z0sY8PAcJiRcu>{i{u+S!hbJHb)Sv(Y)o@xA<<0j~HyqNx`TKaru*!^c+c+xd9lfP^ zVJN5{3P!}sV*3J;IWGuw56LrDYdJou(CNu_ie6H++UJ!XIS$Pc46O!`JuWLK51Zl~ z0+~sdo2B6-LypP2baPh9gD+zy>?4mJMHn^AG3p^UIz)A-0istcpQkKXyyJ~;lUZjv z>t;F=uDJ`@r`Qz2+TFS{Ip1pxVPHDX*Xqv6qigww7U(y~_3dE9aCczC{nE1;Y_8*N zY8rnf@P~fd9#k}kvb^!C@2XRUWa68GAMvxS_rx^1%Hx#N)){OZx~DMVKNS!;Cb23j zBV{9|@@Ke)?tr}|7RD7}>zMXsN*Jg9n(6AxX1=u}2UE-zn znDnlBiQ{L6Tft;6BOuGap}In(dX!3gq<1xr`Tx&s|EYIUIRP<~jk#G+gXg;^kd<}U zz$-qMYAyih7(~jnsde)JxS6BcJ9&O(T5jBlttPRpV*<5*=P8sP@b@L1UrQpA&Yiok zyVjK5km?`_fJd7*eu^f2tIXUk9iTdgWZoOn%bPanr`}~Ai~F2c8O%9p7tsz#bjo)a zvSk*?X1iKCkslIq6Yz}H?#j`CuG#M{ySC0%Sdl7p^HLfUMdi)UFMD;Ft&0p*Jwf)p zlRh*<%|6P&oHc7G*ZA#-8=By^G*K#Ta($~HSfLXUSbjDznSzJ83sr+hN@m+!-W~TC z`p_Ilt~nw_94Xk_gz0|N0F_ie2ojhJYO{lU?LdnmuzO^qa!h-BXrd>%dt;FpCb6 zZSkm{BbZ`K#D{v_&)+QQqg}`%jUd(TjZh}QIs(Ea9^JU~&bSzOd`9~4t6W8QJi7{Q z7IC%u?(dF8nJ;+Bph2z>KSZrG;F|JTL;cS`=uoT=4=eanll$**x#nG;?<-LEnGc6R zFV4JNtAU`AHRkp|H*5;pJ2-*|&zNsHn-oQS^eQ(Q4Jyzrf1h1myjpOi;+6-Zi9E~R zYGva*sc|dr0*ZsLCHJ30_B~~Xa7OI)akhuUTHYk+fRhBnxn~|_X!%q*!3Fp6H_lqE z()xp_aC8sfElz~AIybgxpmT&TsR8b<#o+>BdcDFM?|VzZButx8z4gX#Aa7asGXd9J zTeDg_bxzftqWv`*YK!+127G@Dw|AA6P%7lbu_*O0;d#5~XL=pu$If-1qVj)Me~5NO#+O(Dj= zjQG!SjGy0qur(o#W7^;P$gy9oC8f`;3C7=x-B{F(m*l?1z%Lq)*6J=?xm}O-q?~hl z|2hqv1x9R>zYVL5aU*F-|7y_l+T%e5#u#KC7J(Frb&Eah*lDF?KT2cCUN*j#_-0QiVnPVo0>s(*&bxV{^hUqTCqHdwf_h<)iTsf=U^Ki%J5u5fUj_%_cFCXGuiHN@9f zM0L*H20>pn74h4!+P{(ix>QOYzAgj!hO@PbSN$&-l^>LE#iBrdY()k`GZfid^N9M zXFk5NY)w4{A69>0EjxgFsJ~#;mWn_8v^;T~J4g(NoOPu;^4P?Pg5N`NKM1tXfIRPo zpcTI*r0J>Isp`q$-<>1ZXPzWzQ!DjUd9lQ_lZ|~{&q!Idjw`6te6$T=2KbXK%&=0{ zNh{hP{TlB25x2wWTN5X@62AEj>ie^BEP*P}q!ja0y^XrGpKMvfgw^`9k=f_N*aj))V&eia)=%CIv;UBWE6#m0x20!C$rk?>L)$4TJoYA_}KY?uN%&wjj!XwS}b;$WkP(80&Et1#7ZM5M_S=?R?mqmr9?` zl6JF|@_^nZxt#&6PVX}$Tv92xL8~Tcn{(BD*)!6WU~+~)_#Nmd)yn_x7-|XtGl)~9*&cy- ze%U!wzjhqd;68jDc30uLt^vpMr#t#jkaK&dB&jbS#sFr%&-1)yTqr$gXoi|+{R|qr znr%}Wb76XqYZr6d<@UUgYF=e|!>3TKZ!SF-_Kb_M>o;j}*u-!M(SCSpL+N9a38m9R9DApVdsRv9)bL9z zMczc|i2Jf%FjkHn9&+-PY(H@y=J)YMg!Sh}dXxrX@V+TKY(09yf#a$u*qb$p?eq*6 z6@=T5vsQFNP*nN%=fu6(;w?#r?*gVEh+OYewdHrWt}1m32tA149t)TNwQFEN^i=Al$~atwp&`Y`K{ zZ6O<(eegDfih*4}05{x-OLfpqAbak-jxbtHy*EyA`lwxC_SHHQ5nQ_t@_05E^dI2x zNGvaaB1oy|K{Pn)M#j9ClKFXHu9 zRMiA(X9KmDdF5e$M*t#1!s7fwqWr?bhQeYp;?gq0BD_LEGD1Q^#ZIOFCgA37=j7n` zp9PeTM;_b>JpAW`=k5+r9~%#QfRdfNtv!>vn~kHrfxV5L-@BjovUf}XbyXdeiYL|| F{tqJ#L_h!l diff --git a/web/App_Themes/images/ui-icons_ffffff_256x240.png b/web/App_Themes/images/ui-icons_ffffff_256x240.png index 4d66f596e5967a460a37526e2130a55711eeca3c..6126c4e3ee091b13a3dedec98cfa98a84104f40c 100644 GIT binary patch delta 175 zcmbPjc;0Y=N(f83qpu?a!^VE@KZ&di3=E9LLGDfr>(0r%1acITJ%W507^>757#dm_ z7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-G$+Qd;gjJ8(nUTdzl@UHc-P4b1k#USrUc-w2&{x delta 105 zcmX?aINNZ7iUL!Tx4R3&|Mvbf`xzJ*I14-?i-EKU7`vU!w%b^7TU^Ri*T7iU&@jZ% j#LCpt%FsyLz`)ADz+h$O>dCnhxTGDwToj)?Pr?uYHcK5q diff --git a/web/App_Themes/style.css b/web/App_Themes/style.css index 4277faf2..e5c25f52 100644 --- a/web/App_Themes/style.css +++ b/web/App_Themes/style.css @@ -1,14 +1,12 @@ body { - background-color: black; - color: #D0FFD0; font-family: 'Arial', cursive; padding: 0; margin: 0; } -.tagname { color: #D0FFD0; } + .tagname+.tagname:before { content: ', '; } -.tagname:hover { background-color: red; cursor:pointer; } +.tagname:hover { cursor:pointer; } /* Start by setting display:none to make this hidden. Then we position it in relation to the viewport window @@ -36,6 +34,7 @@ body { left: 0; right: 0; width: 100%; + max-height: 100%; overflow-y: auto; overflow-x: hidden; } @@ -49,67 +48,21 @@ body.loading .modal { } .iconsmall { max-height: 1.3em; max-width: 1.3em; } -input, textarea, checkbox { - color: #FFFFA0; - background-color: black; - } - .photo { width: 100%; } .blogbanner { float: left; top:0; } .subtitle { font-size:small; font-style: italic; } -header { -transition: margin 2s, padding 2s; - padding: 0; - margin: 0; - padding-top: 2em; - padding-bottom:2em; - display: block; - background: url("/App_Themes/images/star-939235_1280.jpg") -3em -3em no-repeat fixed; - } -header h1, header a { -transition:padding 2s; -background-color: rgba(0,0,0,.5); -margin:0; padding:1em; -} -nav { -transition: margin 2s, padding 2s; - margin: 2em; - padding: 2em; - display: block; - border-radius:1em; - background: url("/App_Themes/images/helix-nebula-1400x1400.s.jpg") 50% 10% repeat fixed ; - justify-content: space-around; -} + nav li { display: inline-block; } -main { -transition: margin 2s, padding 2s; - margin: 2em; - padding: 2em; - display: block; - border-radius:1em; - background: url("/App_Themes/images/p8-av4.png") 50% 20em no-repeat fixed ; - } -footer { -transition: margin 2s, padding 2s; - background: url("/App_Themes/images/helix-nebula-1400x1400.s.jpg") 50% 90% repeat fixed ; - margin: 0; - margin-top: 2em; - padding: 2em; - display: block; - clear: both; - font-size: smaller; - justify-content: space-around; -} footer a { border-radius:1em; padding:1em; } + legend { border-radius:5px; padding:.5em; - background-color: rgba(0,0,32,.5); } #copyr { text-align: center; display: block; background-color: rgba(20,20,20,.8); } footer p { display:inline-block; } @@ -126,179 +79,30 @@ fieldset { border-radius:5px; border: solid 1px #000060; } -main video, main img { - max-width:100%; - max-height:75%; - padding: 1em; - } - -aside { - display: inline-block; - float: right; - max-width: 40em; -} - -.postpreview { - display: inline-block; - max-width: 40em; - padding: 1em; - background-color: rgba(0,0,32,0.8); - border-radius:1em; -} -.postpreview video, .postpreview img { - max-width: 100%; - padding: 1em; - } -.post { - display:block; - padding: 1em; - background-color: rgba(0,0,32,0.8); - color: #eee; - border-radius:1em; - } -.hiddenpost { background-color: rgba(16,16,16,0.5); } -.fullwidth { width: 100%; } - -textarea.fullwidth { min-height:10em; } - -.thanks { - max-width: 10%; - text-align: center; - font-size:smaller; - display:inline; - bottom:0; -} -.panel { -display:inline-block; - } - -.panel,.bshpanel, aside { - background-color: rgba(20,20,20,.8); - border-radius: 1em; - padding: 1em; -} -.spanel { - max-width: 18em; - display: inline-block; - padding: .3em; -} -.xspanel { - max-width:13em; - display: inline-block; - padding:.2em; -} -.xxspanel { - max-width:7em; - display: inline-block; - padding:.1em; -} -.hint { - display: inline; - font-style: italic; - font-size: smaller; -} -.hint::before { -content: "("; -} -.hint::after { -content: ")"; -} - - -.usertitleref { - border-radius: 1em; - background-color:rgba(0,0,32,0.6); - font-family: 'Arial', cursive; - padding: 1em; -} -label { - font-size: medium; -} -.editable { - margin: .5em; - min-height:1em; - border-radius: 1em; - border: dashed rgb(020,20,256) 2px; -} - -.notification { - font-size: large; - background-color: rgba(64,64,0,0.5); - border: solid green 1px; - padding: 1em; - border-radius:1em; - margin:1em; - padding:1em; - } -.dirty { - background-color: rgba(128,128,0,0.5); -} -.error, #error { - color: #f88; - font-size: large; - background-color: rgba(256,0,0,0.5); -} -.validation-summary-errors{ - color: #f88; - background-color: rgba(256,0,0,0.5); -} - -.hidden { display:none; } - -ul.preview li:nth-child(-n+10) { - display:inline; - font-size:xx-small; -} - -ul.preview li:nth-child(n) { - display:none; -} - -.validation-summary-errors{ - color: #f88; -} - -a { - text-decoration: none; -} - .actionlink, .menuitem, a { display:inline-block; - color: yellow; border-radius:1em; - border: solid black 1px; - background-color: rgba(20,20,20,.8); + border-style: solid; + border-width:1px; cursor: pointer; font-family: 'Arial', cursive; padding: 1em; } input, select, textarea { - color: white; - background-color:rgba(0,0,64,0.8); - border: solid 1px rgb(128,128,128); border-radius:1em; - font-family: 'Arial', cursive; -} + border-style: solid; + border-width:1px; - a:hover { - background-color:rgba(30,0,124,0.9); - border: solid green 1px; -} -a:active { - background-color:rgba(124,0,32,0.9); + font-family: 'Arial', cursive; } - input:hover, textarea:hover { - color: white; - background-color:rgba(64,64,64,0.8); -} .code { font-family: "monospace"; - background-color: rgba(0,0,256,0.1); border-radius:25px; white-space: pre-wrap; } + #avatar { float: left; margin:1em; @@ -320,18 +124,31 @@ a:active { padding:5px; margin:5px; background-color: rgba(0,0,40,.8); } - -.input-validation-error { border: solid 1px red; } -.field-validation-error { color: red; } +.skillname { +padding:.5em; +margin:.25em; + max-width: 24em; + text-align: center; +display: inline-block; + background-color: rgba(16,16,64,0.8); + border-radius:1em; + border: solid 1px #aaf; + } +.input-validation-error { border: solid 1px red; background-color: rgba(180,256,256,.5); } +.field-validation-error { color: red; background-color: rgba(180,256,256,.5); } .c2 { font-size: small; font-style: italic; } .c3 { font-size: x-small; font-style: italic; } + +.rate { width:5em; cursor: pointer; text-align:center; } +h2 select { font-size: large; } + @media print { body {background-color:white;color:black;} .control, .actionlink, .menuitem, nav { display:none;} } - .bshpanel { display:block; } + .bshpanel { display: inline-block; } .bsh { display: none; } .c3 { display:initial; } .c3-alt { display:none; } @@ -373,6 +190,8 @@ header h1, header a , .actionlink, .menuitem, a { padding:.5em;} border-radius:.5em; margin:.5em; padding:.5em; + max-height: 100%; + overflow-y: auto; } .menuitem { display: block; @@ -423,3 +242,4 @@ header h1, header a { padding:.2em;} .c2-alt { display:initial; } } + diff --git a/web/ChangeLog b/web/ChangeLog index d6285fc3..aa32038d 100644 --- a/web/ChangeLog +++ b/web/ChangeLog @@ -1,945 +1,665 @@ -2015-11-04 Paul Schneider +2015-11-17 Paul Schneider - * Web.csproj: - * AppAdmin.master: - * AddRole.aspx: - * OAuth2.cs: - * RoleList.aspx: - * UsersInRole.aspx: - * AdminController.cs: - * AddMemberToRole.ascx: - -2015-11-04 Paul Schneider + * RateControl.ascx: A rate control for the Front Office - * instdbws.sql: - * BlogsController.cs: Implements the note + * UserCard.ascx: WIP UserCard - * robots.txt: - * Web.csproj: - * Web.config: - * Catalog.xml: - * Global.asax.cs: - * pgsql.xcf: - * pgsql.jpeg: - * p8-av4.png: - * logoDev.png: - * logoDev.xcf: - * apache_pb.gif: - * style.css: - * theme.css: - * debian-pb.gif: - * apache_pbw.gif: - * apache_pby.gif: - * Book.aspx: - * jquery-ui.css: - * Login.aspx: - * debian-powered.png: - * FhHRx.gif: - * TagControl.ascx: - * pgsql.png: - * jquery-ui.min.css: - * theme.css: - * noavatar.png: - * p8-av4.s.jpg: - * p8-av4.xxs.jpg: - * apache_pbw.png: - * debian-logo.png: - * Mono-powered.png: - * helix-nebula-1400x1400.l.jpg: - * star-939235_1280.jpg: - * animated-overlay.gif: - * star-939235_1280.s.jpg: - * sign-in-with-google.png: - * star-939235_1280.xxs.jpg: - * sign-in-with-google-s.png: - * helix-nebula-1400x1400.jpg: - * helix-nebula-1400x1400.s.jpg: - * helix-nebula-1400x1400.xxs.jpg: + * FrontOfficeController.cs: restricts the Skills view to + Admins, + uses the new PerformerProfile object -2015-11-03 Paul Schneider + * Edit.aspx: + * Title.aspx: + * Profile.aspx: + * Skills.aspx: + * Estimate.aspx: the `aside` html node is better placed at the + top of the content, + overall when it's floating. - * YavscController.cs: TODO ... + * PostActions.ascx: code formatting -2015-11-03 Paul Schneider + * UserSkills.aspx: Uses the new `PerformerProfile` object - * YavscController.cs: Fixes the cookies agreement - * HomeController.cs: Finalizes the cookie agreement system. + * Web.csproj: adds a skill view, + WIP UserCard - * YavscHelpers.cs: Adds a "click_action_name" field, to give a - text to the notification dimissing button. +2015-11-17 Paul Schneider + * yavsc.skills.js: Implements skills Ajax methods - * App.master: Uses the new field from Notification + * RateControl.ascx: + * RateSkillControl.ascx: + * RateUserSkillControl.ascx: refactorization - * Web.config: No VB code to compile + * UserSkills.aspx: Implements a site user's skills view - * Web.csproj: moves Sql files to Sql folder + * BlogsController.cs: Refactorization, with skill and + userskills ratings - * instdbws.sql: permits profile records with no users record - associated to, - and so, anonymous profiles creation. + * GCMController.cs: xml doc -2015-11-01 Paul Schneider + * SkillController.cs: implements a skill controller - * CalAuth.aspx: A view ... still unused + * Skills.sql: defines the skill data model - * style.css: css overflow-y auto, the container making it - modal has no margin. + * style.css: mainly adds the `rate` css class - * GoogleController.cs: Fixes again the calendar usage, - uses Google API key and client credentials found in - configuration file + * style.css: adds rate & skill name style - * ApiClient.cs: Google API key and client credentials are now - found in configuration file + * AuthorizeAttribute.cs: + * OAuth2.cs: + * FormatterException.cs: xmldoc - * CalendarApi.cs: Let the controller build the credential - string from thr profile object. + * FrontOfficeController.cs: implements an user's skills method - * OAuth2.cs: The OAuth2 Client only needs a client id and - secret + * App.master: code formatting - * yavsc.js: Fixes some css flipping + * yavsc.rate.js: Makes it a JQuery module - * ValidateAjaxAttribute.cs: A FIXME + * EventPub.aspx: no more ImgLocator ... I don't remember why. - * Web.config: Google key, client id and secret come from - application settings + * Skills.aspx: Implements a site wize skills view - * Web.csproj: a page in more + * Web.config: imports the skill model name space -2015-11-01 Paul Schneider + * Web.config: SkillProvider section and code formatting - * Book-next.aspx: pollution +2015-11-14 Paul Schneider - * instdbws.sql: The conversion to a valid .Net DateTime - requires a credible date time as source value, the null one is - not supported. + * SkillController.cs: WIP Skill interface - * style.css: Fixes the new notification style + * Skills.sql: WIP skills - * AccountController.cs: Fixes the profile edition. - Now using the anti forgery key at login time + * star.gif: + * rateit.css: + * delete.gif: + * AuthorizeAttribute.cs: + * AddRole.aspx: + * jquery.rateit.js: + * jquery.validate.js: + * jquery.rateit.min.js: + * HomeController.cs: + * jquery.validate.min.js: + * BlogsController.cs: + * GoogleHelpers.cs: + * GoogleController.cs: + * jquery.validate-vsdoc.js: + * jquery.rateit.min.js.map: + * FrontOfficeController.cs: - * Book.aspx: - * CalendarApi.cs: - * GoogleController.cs: WIP booking + * Skills.aspx: + * RateControl.ascx: WIP lists skils - * HomeController.cs: code prettying + * Contact.template.aspx: a default contact page - * Global.asax.cs: Limits the usage of titles in a route to the - blog controller + * Contact.totem.aspx: the totem contact page - * OAuth2.cs: Profile values may be of type DBNull ... + * BlogsController.cs: Uniformize the creation/modificaton + action profiles: + * no more `Create` action + * The Post action creates or updates. - * T.cs: All translated strings will be Html encoded, as - expected from an html helper + * FrontOfficeController.cs: The FE controller is a Yavsc one. - * YavscHelpers.cs: A new method to build a javascript - string... + * instdbws.sql: + * RateControl.ascx: refactoring - * App.master: - * AppAdmin.master: Notification.body is now a js string - literal + * yavsc.rate.js: the default treatment in case of error is the + same. - * NoLogin.master: sync with the true master + * Circles.aspx: + * Index.aspx: + * UsersInRole.aspx: MAS disapeared - * Login.aspx: Permits the anti forgery key usage + * Index.aspx: + * Basket.aspx: + * Command.aspx: + * Estimate.aspx: MAS disparition - * Profile.aspx: Fixes the username modification + * Contact.aspx: My information - * Estimate.aspx: refactoring + * Index.aspx: This site could talk about Yavsc - * Web.config: Fixes a later commit on the catalog name space + * RestrictedArea.aspx: A customized restricted area wall, with + a security hole? - * Web.csproj: An ajax helper to notify + * Web.csproj: fixes the `Deploy` compilation target - * ChangeLog: should not be indexed + * WebDeploy.targets: My `DirectorySepartionChar` is a slash + ... It's prettier to me. - * ValidateAjaxAttribute.cs: Fixes usage of HtmlFieldPrefix + * packages.config: We don't need jQuery.RateIt ... it's + perhaps awesome, I not yet know nor for a while. -2015-10-31 Paul Schneider +2015-11-11 Paul Schneider - * Web.csproj: - * Web.config: - * T.cs: - * Global.asax.cs: * App.master: - * Book.aspx: - * YavscHelpers.cs: - * OAuth2.cs: - * Profile.aspx: - * YavscAjaxHelper.cs: - * Book-next.aspx: - * CalendarApi.cs: - * HomeController.cs: - * GoogleController.cs: - * Estimate.aspx: - * AccountController.cs: - -2015-10-30 Paul Schneider + * NoLogin.master: + * AppAdmin.master: master pages sync - * packages.config: - * App.master: - * datepair.js: - * Book.aspx: - * datepair.min.js: - * jquery.datepair.js: - * jquery-ui-1.11.4.js: - * jquery.timepicker.js: - * jquery-1.11.3.min.js: - * jquery.datepair.min.js: - * WebCatalogExtensions.cs: - * GoogleController.cs: - * jquery.timepicker.min.js: - * jquery.timepicker.css: - * FrontOfficeController.cs: - * ui-icons_ffffff_256x240.png: - * ui-icons_cccccc_256x240.png: - * ui-icons_a83300_256x240.png: - * ui-icons_4b8e0b_256x240.png: - * ui-icons_222222_256x240.png: - * ui-bg_glass_40_ffc73d_1x400.png: - * ui-bg_glass_40_0078a3_1x400.png: - * ui-bg_glass_20_555555_1x400.png: - * ui-icons_222222_256x240.png: - * ui-icons_4b8e0b_256x240.png: - * ui-icons_a83300_256x240.png: - * ui-icons_cccccc_256x240.png: - * ui-icons_ffffff_256x240.png: - * ui-bg_glass_40_ffc73d_1x400.png: - * ui-bg_glass_40_0078a3_1x400.png: - * ui-bg_glass_20_555555_1x400.png: - * ui-bg_inset-soft_25_000000_1x100.png: - * ui-bg_inset-soft_30_f58400_1x100.png: - * ui-bg_gloss-wave_25_333333_500x100.png: - * ui-bg_highlight-soft_80_eeeeee_1x100.png: - * ui-bg_inset-soft_30_f58400_1x100.png: - * ui-bg_inset-soft_25_000000_1x100.png: - * ui-bg_gloss-wave_25_333333_500x100.png: - * ui-bg_highlight-soft_80_eeeeee_1x100.png: +2015-11-11 Paul Schneider - * FrontOfficeController.cs: refactoring: a dedicated name - space for the catalog + * style.css: + * style.css: removes the themable code, + it goes to the dark theme. - * ChooseADate.aspx: WIP + * yavsc.rate.js: rates using the web API - * Web.csproj: date pairing : includes the javascript modules + * UserCard.ascx: WIP UserCard -2015-10-29 Paul Schneider + * RateControl.ascx: + * RateControl.ascx.cs: implements a rate control - * Index.aspx: Gives this page a title + * BlogsController.cs: implements a rating API on Blog spot - * AdminController.cs: Trying to fix this Index : /Admin ... a - 404 + * AccountController.cs: Saves the user's theme at profile + edition - * FileSystemController.cs: Refactoring the name of the files - manager class + * AdminController.cs: Enrols users - * Index.aspx: Fixes the file system access + * HomeController.cs: there's still no artiste here. - * RemoveRole.aspx: Role removal form, had not a canonical - name! + * App.master: Uses a page theme - * Web.csproj: a page was renamed + * yavsc.circles.js: cleaning -2015-10-28 Paul Schneider + * yavsc.js: some enhancement - * yavsc.circles.js: + * yavsc.tags.js: adds a new line :-) -2015-10-28 Paul Schneider + * Profile.aspx: offers the theme choice - * BlogsController.cs: - * AccountController.cs: - * CalendarController.cs: refactoring : the Yavsc controller - name + * AddUserToRole.ascx: Implements the enrolment - * instdbws.sql: a new profile value : a boolean, - `AllowCookies` :'{ + * Admin.aspx: code formatting - * style.css: a class to display notification + * UserList.aspx: lists user's roles and link to enrol - * HomeController.cs: Notifies users this site uses cookies - (what for an information!) - If authenticated, at dimissing this notification, the user's - profile is updated, - and he'll not mess up anymore with the info. + * UsersInRole.aspx: a more relevant title, and a list as html + `UL` node - * App.master: - * YavscHelpers.cs: adds usage of click_action value at - displaying a notification. + * PostActions.ascx: adds on control on rate - * yavsc.js: Implements the notification `click_action` + * Index.aspx: Fixes the latest merge + * Web.csproj: references the rating control - * Web.config: * enables anonymous profiles - * adds a new `allowcookies` profile property + * ChooseMedia.aspx: useless + * Web.config: configures the `UITheme` profile property - * Web.csproj: Yavsc controller refactoring +2015-11-08 Paul Schneider - * Web.config: code prettying + * helix-nebula.l.jpg: + * helix-nebula.s.jpg: + * helix-nebula.xs.jpg: helix nebula in new sizes - * YavscController.cs: Gives Yavsc a concrete base controller + * Web.config: hides my info on Google -2015-10-27 Paul Schneider + * facebook.png: a facebook icon, in case of - * AppAdmin.master: A new master page for Administration +2015-11-06 Paul Schneider + * ChangeLog: * Web.csproj: * Web.config: - * Auth.aspx: - * Auth.aspx: - * Book.aspx: - * Book.aspx: + * pgsql.xcf: + * p8-av4.png: + * pgsql.jpeg: + * logo-1.jpg: + * App.master: + * logoDev.png: + * logoDev.xcf: + * datepair.js: + * debian-pb.gif: + * apache_pb.gif: + * apache_pbw.gif: + * apache_pby.gif: + * Index.aspx: + * Index.aspx: + * Title.aspx: * Book.aspx: - * Restore.aspx: * Auth.aspx: + * AppAdmin.master: + * datepair.min.js: + * Index.aspx: + * Book.aspx: * TagPanel.ascx: + * CalAuth.aspx: + * instdbws.sql: * date.js: - * UserList.aspx: - * Restored.aspx: - * RoleList.aspx: - * plural.js: + * TagPanel.ascx: + * debian-powered.png: + * YavscAjaxHelper.cs: + * logo.jpg: + * UserPosts.aspx: + * jquery.datepair.js: + * FhHRx.gif: * number.js: + * plural.js: + * pgsql.png: + * totem.jpg: + * TagControl.ascx: + * RemoveRole.aspx: + * twiter.png: + * logo.s.png: + * jquery-1.11.3.min.js: + * UsersInRole.aspx: + * logo-1.png: + * PostActions.ascx: * message.js: - * ChooseADate.aspx: - * ChooseADate.aspx: + * logo.xs.png: * currency.js: - * ErrorMessage.aspx: - * globalize.js: * ChooseADate.aspx: + * facebook.png: + * noavatar.png: + * logo.xxs.png: + * p8-av4.s.jpg: + * jquery.datepair.min.js: + * globalize.js: + * totemprod.png: + * p8-av4.xxs.png: + * apache_pbw.png: + * AddMemberToRole.ascx: + * p8-av4.xxs.jpg: * ChooseCalendar.aspx: - * ErrorMessage.aspx: - * ChooseCalendar.aspx: + * YavscController.cs: + * debian-logo.png: * relative-time.js: - * ChooseCalendar.aspx: + * totem-banner.png: + * Mono-powered.png: * OtherWebException.aspx: + * concert.clear.jpg: + * helix-nebula-1400x1400.l.jpg: + * totem-banner.xs.jpg: + * star-939235_1280.jpg: + * totem-banner.xxs.jpg: + * star-939235_1280.s.jpg: + * drummer-652345_1280.jpg: + * sign-in-with-google.png: + * star-939235_1280.xs.jpg: + * musician-923526_1280.jpg: + * musician-923526_1.nb.jpg: + * star-939235_1280.xxs.jpg: + * sign-in-with-google-s.png: + * an-pierle-876094_1280.jpg: + * musician-923526_1.nbb.jpg: + * drummer-652345_1280.s.jpg: + * helix-nebula-1400x1400.jpg: + * musician-923526_1280.s.jpg: + * drummer-652345_1280.xxs.jpg: + * musician-923526_1.nb.xs.jpg: + * helix-nebula-1400x1400.s.jpg: + * live-concert-388160_1280.jpg: + * musician-923526_1.nb.xxs.jpg: + * musician-923526_1.nbb.xs.jpg: + * musician-923526_1280.xxs.jpg: + * musician-923526_1.nbb.xxs.jpg: * globalize.cultures.js: - * globalize.culture.et.js: - * globalize.culture.it.js: - * globalize.culture.oc.js: - * globalize.culture.sq.js: - * globalize.culture.sr.js: - * globalize.culture.or.js: - * globalize.culture.sk.js: - * globalize.culture.es.js: - * globalize.culture.ii.js: - * globalize.culture.sl.js: - * globalize.culture.pa.js: - * globalize.culture.is.js: - * globalize.culture.no.js: + * helix-nebula-1400x1400.xxs.jpg: + * live-concert-388160_1280.s.jpg: * globalize.culture.nl.js: - * globalize.culture.te.js: - * globalize.culture.ta.js: - * globalize.culture.ka.js: - * globalize.culture.tg.js: - * globalize.culture.ja.js: - * globalize.culture.iu.js: - * globalize.culture.sv.js: - * globalize.culture.nn.js: - * globalize.culture.th.js: - * globalize.culture.kk.js: - * globalize.culture.sw.js: - * globalize.culture.eu.js: - * globalize.culture.fy.js: - * globalize.culture.ru.js: - * globalize.culture.ga.js: + * globalize.culture.ko.js: + * globalize.culture.zh.js: * globalize.culture.hr.js: - * globalize.culture.gd.js: - * globalize.culture.ro.js: - * globalize.culture.he.js: - * globalize.culture.gu.js: - * globalize.culture.rm.js: - * globalize.culture.pt.js: - * globalize.culture.hi.js: - * globalize.culture.gl.js: - * globalize.culture.ps.js: + * globalize.culture.th.js: + * globalize.culture.ml.js: + * globalize.culture.mk.js: * globalize.culture.id.js: - * globalize.culture.se.js: - * globalize.culture.fi.js: - * globalize.culture.ig.js: - * globalize.culture.fa.js: - * globalize.culture.si.js: * globalize.culture.pl.js: - * globalize.culture.fr.js: - * globalize.culture.rw.js: - * globalize.culture.sa.js: - * globalize.culture.hu.js: * globalize.culture.hy.js: - * globalize.culture.fo.js: - * globalize.culture.kl.js: - * globalize.culture.ml.js: - * globalize.culture.mk.js: - * globalize.culture.bs.js: - * globalize.culture.mr.js: - * globalize.culture.ca.js: - * globalize.culture.mi.js: - * globalize.culture.lt.js: - * globalize.culture.cy.js: - * globalize.culture.yo.js: - * globalize.culture.cs.js: - * globalize.culture.lv.js: - * globalize.culture.co.js: - * globalize.culture.zh.js: - * globalize.culture.az.js: - * globalize.culture.as.js: - * globalize.culture.mn.js: - * globalize.culture.ar.js: - * globalize.culture.am.js: - * globalize.culture.af.js: - * globalize.culture.ba.js: - * globalize.culture.br.js: - * globalize.culture.zu.js: - * globalize.culture.bo.js: - * globalize.culture.bn.js: - * globalize.culture.bg.js: - * globalize.culture.be.js: - * globalize.culture.da.js: - * globalize.culture.ko.js: - * globalize.culture.nb.js: - * globalize.culture.ug.js: - * globalize.culture.uk.js: - * globalize.culture.ky.js: - * globalize.culture.el.js: - * globalize.culture.tt.js: * globalize.culture.tk.js: - * globalize.culture.km.js: - * globalize.culture.tn.js: - * globalize.culture.ne.js: - * globalize.culture.tr.js: - * globalize.culture.kn.js: - * globalize.culture.mt.js: - * globalize.culture.lb.js: - * globalize.culture.de.js: - * globalize.culture.vi.js: + * globalize.culture.hu.js: + * globalize.culture.zu.js: * globalize.culture.lo.js: - * globalize.culture.wo.js: - * globalize.culture.ms.js: - * globalize.culture.xh.js: - * globalize.culture.ha.js: + * globalize.culture.no.js: + * globalize.culture.lb.js: + * globalize.culture.tt.js: + * globalize.culture.ha.js: + * globalize.culture.ky.js: + * globalize.culture.sr.js: + * globalize.culture.hi.js: + * globalize.culture.nn.js: + * globalize.culture.ps.js: + * globalize.culture.lt.js: + * globalize.culture.he.js: + * globalize.culture.ig.js: + * globalize.culture.ug.js: + * globalize.culture.ja.js: * globalize.culture.uz.js: + * globalize.culture.ka.js: + * globalize.culture.kn.js: + * globalize.culture.lv.js: * globalize.culture.ur.js: + * globalize.culture.kk.js: + * globalize.culture.sq.js: + * globalize.culture.kl.js: + * globalize.culture.uk.js: + * globalize.culture.km.js: + * globalize.culture.oc.js: + * globalize.culture.pa.js: + * globalize.culture.ii.js: + * globalize.culture.yo.js: + * globalize.culture.is.js: + * globalize.culture.mi.js: + * globalize.culture.xh.js: + * globalize.culture.iu.js: + * globalize.culture.vi.js: + * globalize.culture.tn.js: + * globalize.culture.wo.js: + * globalize.culture.or.js: + * globalize.culture.it.js: + * globalize.culture.gu.js: + * globalize.culture.nb.js: * globalize.culture.dv.js: - * globalize.culture.qut.js: + * globalize.culture.el.js: + * live-concert-388160_1280.xxs.jpg: + * globalize.culture.sa.js: + * globalize.culture.af.js: + * globalize.culture.ro.js: + * globalize.culture.ru.js: + * globalize.culture.mt.js: + * globalize.culture.rw.js: + * globalize.culture.am.js: + * globalize.culture.sw.js: + * globalize.culture.de.js: + * globalize.culture.bo.js: + * globalize.culture.bn.js: + * globalize.culture.bg.js: + * globalize.culture.be.js: + * globalize.culture.ba.js: + * globalize.culture.az.js: + * globalize.culture.br.js: + * globalize.culture.da.js: + * globalize.culture.cy.js: + * globalize.culture.cs.js: + * globalize.culture.co.js: + * globalize.culture.ca.js: + * globalize.culture.bs.js: + * globalize.culture.ms.js: + * globalize.culture.fr.js: + * globalize.culture.mn.js: + * globalize.culture.ar.js: + * globalize.culture.te.js: + * globalize.culture.pt.js: + * globalize.culture.fo.js: + * globalize.culture.fy.js: + * globalize.culture.as.js: + * globalize.culture.tg.js: + * globalize.culture.gl.js: + * globalize.culture.gd.js: + * globalize.culture.sl.js: + * globalize.culture.ga.js: + * globalize.culture.sk.js: + * globalize.culture.ta.js: + * globalize.culture.et.js: + * globalize.culture.ne.js: + * globalize.culture.es.js: + * globalize.culture.se.js: + * globalize.culture.rm.js: + * globalize.culture.eu.js: + * globalize.culture.tr.js: + * globalize.culture.sv.js: + * globalize.culture.fi.js: + * globalize.culture.mr.js: + * globalize.culture.fa.js: + * globalize.culture.si.js: + * globalize.culture.syr.js: * globalize.culture.moh.js: - * globalize.culture.prs.js: - * globalize.culture.kok.js: - * globalize.culture.nso.js: - * globalize.culture.hsb.js: - * globalize.culture.tzm.js: - * globalize.culture.sah.js: + * globalize.culture.sma.js: + * globalize.culture.gsw.js: + * globalize.culture.arn.js: * globalize.culture.fil.js: + * globalize.culture.qut.js: + * globalize.culture.quz.js: + * globalize.culture.sah.js: + * globalize.culture.dsb.js: + * globalize.culture.hsb.js: * globalize.culture.sms.js: + * globalize.culture.kok.js: * globalize.culture.smn.js: + * globalize.culture.prs.js: + * globalize.culture.nso.js: * globalize.culture.smj.js: - * globalize.culture.dsb.js: - * globalize.culture.syr.js: - * globalize.culture.quz.js: - * globalize.culture.arn.js: - * globalize.culture.gsw.js: - * globalize.culture.sma.js: + * globalize.culture.tzm.js: + * globalize.culture.se-NO.js: + * globalize.culture.se-SE.js: + * globalize.culture.se-FI.js: + * globalize.culture.sv-SE.js: + * globalize.culture.sv-FI.js: + * globalize.culture.sl-SI.js: + * globalize.culture.sq-AL.js: + * globalize.culture.sk-SK.js: + * globalize.culture.si-LK.js: + * globalize.culture.sw-KE.js: + * globalize.culture.ar-LY.js: + * globalize.culture.ar-MA.js: + * globalize.culture.ar-OM.js: + * globalize.culture.ar-QA.js: + * globalize.culture.ar-SA.js: + * globalize.culture.ar-SY.js: + * globalize.culture.ar-TN.js: + * globalize.culture.ar-YE.js: + * globalize.culture.as-IN.js: + * globalize.culture.ar-LB.js: + * globalize.culture.af-ZA.js: + * globalize.culture.am-ET.js: + * globalize.culture.ar-AE.js: + * globalize.culture.ar-BH.js: + * globalize.culture.ar-DZ.js: + * globalize.culture.ar-EG.js: + * globalize.culture.ar-IQ.js: + * globalize.culture.ar-JO.js: + * globalize.culture.ar-KW.js: + * globalize.culture.pa-IN.js: * globalize.culture.uk-UA.js: - * globalize.culture.ne-NP.js: + * globalize.culture.ug-CN.js: * globalize.culture.tt-RU.js: - * globalize.culture.ur-PK.js: * globalize.culture.tr-TR.js: - * globalize.culture.nl-BE.js: * globalize.culture.tn-ZA.js: - * globalize.culture.ug-CN.js: - * globalize.culture.nb-NO.js: - * globalize.culture.zh-HK.js: - * globalize.culture.zh-MO.js: - * globalize.culture.zh-SG.js: - * globalize.culture.mr-IN.js: - * globalize.culture.zh-TW.js: - * globalize.culture.zu-ZA.js: - * globalize.culture.ms-BN.js: - * globalize.culture.vi-VN.js: - * globalize.culture.mt-MT.js: - * globalize.culture.wo-SN.js: - * globalize.culture.xh-ZA.js: - * globalize.culture.yo-NG.js: - * globalize.culture.ms-MY.js: - * globalize.culture.zh-CN.js: * globalize.culture.tk-TM.js: - * globalize.culture.ps-AF.js: - * globalize.culture.se-FI.js: - * globalize.culture.se-NO.js: - * globalize.culture.se-SE.js: - * globalize.culture.pl-PL.js: - * globalize.culture.si-LK.js: - * globalize.culture.sa-IN.js: - * globalize.culture.rm-CH.js: - * globalize.culture.ro-RO.js: - * globalize.culture.pt-PT.js: - * globalize.culture.pt-BR.js: - * globalize.culture.ru-RU.js: - * globalize.culture.rw-RW.js: - * globalize.culture.sk-SK.js: - * globalize.culture.sw-KE.js: - * globalize.culture.nn-NO.js: - * globalize.culture.ta-IN.js: - * globalize.culture.te-IN.js: * globalize.culture.th-TH.js: - * globalize.culture.nl-NL.js: - * globalize.culture.sv-SE.js: - * globalize.culture.sl-SI.js: - * globalize.culture.pa-IN.js: - * globalize.culture.or-IN.js: - * globalize.culture.oc-FR.js: - * globalize.culture.sq-AL.js: - * globalize.culture.sv-FI.js: - * globalize.culture.lo-LA.js: + * globalize.culture.te-IN.js: + * globalize.culture.ta-IN.js: + * globalize.culture.ur-PK.js: + * globalize.culture.zu-ZA.js: + * globalize.culture.zh-TW.js: + * globalize.culture.zh-SG.js: + * globalize.culture.zh-MO.js: + * globalize.culture.zh-HK.js: + * globalize.culture.zh-CN.js: + * globalize.culture.yo-NG.js: + * globalize.culture.xh-ZA.js: + * globalize.culture.wo-SN.js: + * globalize.culture.vi-VN.js: + * globalize.culture.hu-HU.js: * globalize.culture.es-DO.js: + * globalize.culture.is-IS.js: + * globalize.culture.it-CH.js: + * globalize.culture.it-IT.js: + * globalize.culture.es-CR.js: + * globalize.culture.es-CO.js: * globalize.culture.es-EC.js: - * globalize.culture.es-ES.js: + * globalize.culture.hy-AM.js: + * globalize.culture.id-ID.js: * globalize.culture.es-GT.js: + * globalize.culture.es-ES.js: + * globalize.culture.ig-NG.js: + * globalize.culture.ii-CN.js: + * globalize.culture.es-CL.js: + * globalize.culture.kl-GL.js: + * globalize.culture.km-KH.js: + * globalize.culture.kn-IN.js: + * globalize.culture.ko-KR.js: + * globalize.culture.en-TT.js: + * globalize.culture.ky-KG.js: + * globalize.culture.kk-KZ.js: + * globalize.culture.es-BO.js: + * globalize.culture.ja-JP.js: + * globalize.culture.ka-GE.js: + * globalize.culture.es-AR.js: + * globalize.culture.en-ZW.js: + * globalize.culture.en-ZA.js: + * globalize.culture.en-US.js: * globalize.culture.es-HN.js: - * globalize.culture.es-MX.js: + * globalize.culture.fr-CA.js: + * globalize.culture.fr-CH.js: + * globalize.culture.fr-FR.js: + * globalize.culture.es-SV.js: + * globalize.culture.fr-LU.js: + * globalize.culture.fr-MC.js: + * globalize.culture.fr-BE.js: + * globalize.culture.es-VE.js: + * globalize.culture.et-EE.js: + * globalize.culture.eu-ES.js: + * globalize.culture.fa-IR.js: + * globalize.culture.fi-FI.js: + * globalize.culture.es-US.js: + * globalize.culture.fo-FO.js: + * globalize.culture.fy-NL.js: * globalize.culture.es-NI.js: - * globalize.culture.es-CR.js: - * globalize.culture.en-ZA.js: - * globalize.culture.en-ZW.js: - * globalize.culture.es-AR.js: - * globalize.culture.as-IN.js: - * globalize.culture.es-BO.js: - * globalize.culture.es-CL.js: - * globalize.culture.es-CO.js: - * globalize.culture.ar-YE.js: + * globalize.culture.he-IL.js: + * globalize.culture.hi-IN.js: + * globalize.culture.hr-BA.js: + * globalize.culture.hr-HR.js: + * globalize.culture.es-MX.js: * globalize.culture.es-PA.js: - * globalize.culture.es-PE.js: - * globalize.culture.es-PR.js: + * globalize.culture.ga-IE.js: + * globalize.culture.gd-GB.js: + * globalize.culture.gl-ES.js: * globalize.culture.es-PY.js: - * globalize.culture.es-SV.js: - * globalize.culture.es-US.js: - * globalize.culture.es-UY.js: - * globalize.culture.ar-LB.js: - * globalize.culture.ar-TN.js: - * globalize.culture.ar-SY.js: - * globalize.culture.ar-SA.js: - * globalize.culture.ar-QA.js: - * globalize.culture.ar-OM.js: - * globalize.culture.ar-MA.js: - * globalize.culture.ar-LY.js: - * globalize.culture.en-US.js: - * globalize.culture.de-CH.js: - * globalize.culture.de-DE.js: - * globalize.culture.de-LI.js: + * globalize.culture.gu-IN.js: + * globalize.culture.es-PR.js: + * globalize.culture.es-PE.js: + * globalize.culture.en-SG.js: + * globalize.culture.ms-MY.js: + * globalize.culture.cy-GB.js: + * globalize.culture.pl-PL.js: + * globalize.culture.cs-CZ.js: + * globalize.culture.co-FR.js: + * globalize.culture.ca-ES.js: + * globalize.culture.or-IN.js: * globalize.culture.de-LU.js: - * globalize.culture.dv-MV.js: - * globalize.culture.bn-IN.js: - * globalize.culture.bn-BD.js: + * globalize.culture.de-LI.js: + * globalize.culture.de-DE.js: + * globalize.culture.de-CH.js: * globalize.culture.de-AT.js: - * globalize.culture.ca-ES.js: + * globalize.culture.da-DK.js: + * globalize.culture.oc-FR.js: * globalize.culture.br-FR.js: + * globalize.culture.ba-RU.js: + * globalize.culture.rm-CH.js: + * globalize.culture.ro-RO.js: + * globalize.culture.ru-RU.js: + * globalize.culture.rw-RW.js: + * globalize.culture.sa-IN.js: + * globalize.culture.be-BY.js: + * globalize.culture.ps-AF.js: * globalize.culture.bo-CN.js: - * globalize.culture.co-FR.js: - * globalize.culture.cs-CZ.js: - * globalize.culture.cy-GB.js: - * globalize.culture.da-DK.js: + * globalize.culture.bn-IN.js: + * globalize.culture.pt-BR.js: + * globalize.culture.pt-PT.js: + * globalize.culture.bn-BD.js: * globalize.culture.bg-BG.js: - * globalize.culture.en-IN.js: - * globalize.culture.en-JM.js: - * globalize.culture.en-MY.js: + * globalize.culture.es-UY.js: * globalize.culture.en-NZ.js: - * globalize.culture.en-PH.js: - * globalize.culture.en-SG.js: - * globalize.culture.en-TT.js: - * globalize.culture.ba-RU.js: - * globalize.culture.be-BY.js: - * globalize.culture.el-GR.js: - * globalize.culture.en-AU.js: - * globalize.culture.en-BZ.js: - * globalize.culture.en-CA.js: - * globalize.culture.en-GB.js: + * globalize.culture.mn-MN.js: + * globalize.culture.en-MY.js: + * globalize.culture.en-JM.js: + * globalize.culture.en-IN.js: * globalize.culture.en-IE.js: - * globalize.culture.es-VE.js: - * globalize.culture.it-CH.js: - * globalize.culture.it-IT.js: - * globalize.culture.ar-IQ.js: - * globalize.culture.ar-EG.js: - * globalize.culture.ar-DZ.js: - * globalize.culture.ar-BH.js: - * globalize.culture.ja-JP.js: - * globalize.culture.is-IS.js: - * globalize.culture.hi-IN.js: - * globalize.culture.hr-BA.js: - * globalize.culture.hr-HR.js: - * globalize.culture.hu-HU.js: - * globalize.culture.hy-AM.js: - * globalize.culture.id-ID.js: - * globalize.culture.ig-NG.js: - * globalize.culture.ii-CN.js: - * globalize.culture.ka-GE.js: + * globalize.culture.ml-IN.js: + * globalize.culture.en-PH.js: + * globalize.culture.lb-LU.js: + * globalize.culture.lo-LA.js: + * globalize.culture.lt-LT.js: * globalize.culture.lv-LV.js: * globalize.culture.mi-NZ.js: * globalize.culture.mk-MK.js: - * globalize.culture.ml-IN.js: - * globalize.culture.ar-AE.js: - * globalize.culture.mn-MN.js: - * globalize.culture.am-ET.js: - * globalize.culture.lt-LT.js: - * globalize.culture.kk-KZ.js: - * globalize.culture.kl-GL.js: - * globalize.culture.km-KH.js: - * globalize.culture.kn-IN.js: - * globalize.culture.ko-KR.js: - * globalize.culture.ky-KG.js: - * globalize.culture.lb-LU.js: - * globalize.culture.af-ZA.js: - * globalize.culture.gd-GB.js: - * globalize.culture.gu-IN.js: - * globalize.culture.fa-IR.js: - * globalize.culture.gl-ES.js: - * globalize.culture.fr-FR.js: - * globalize.culture.ga-IE.js: - * globalize.culture.fy-NL.js: - * globalize.culture.fi-FI.js: - * globalize.culture.fr-MC.js: - * globalize.culture.ar-KW.js: - * globalize.culture.he-IL.js: - * globalize.culture.et-EE.js: - * globalize.culture.eu-ES.js: - * globalize.culture.fo-FO.js: - * globalize.culture.fr-LU.js: - * globalize.culture.fr-BE.js: - * globalize.culture.ar-JO.js: - * globalize.culture.fr-CH.js: - * globalize.culture.fr-CA.js: - * globalize.culture.sms-FI.js: - * globalize.culture.fil-PH.js: - * globalize.culture.smn-FI.js: - * globalize.culture.moh-CA.js: - * globalize.culture.sma-NO.js: - * globalize.culture.sma-SE.js: - * globalize.culture.zh-CHT.js: + * globalize.culture.en-GB.js: + * globalize.culture.nl-BE.js: + * globalize.culture.nl-NL.js: + * globalize.culture.nn-NO.js: + * globalize.culture.en-AU.js: + * globalize.culture.el-GR.js: + * globalize.culture.dv-MV.js: + * globalize.culture.ne-NP.js: + * globalize.culture.mr-IN.js: + * globalize.culture.ms-BN.js: + * globalize.culture.en-CA.js: + * globalize.culture.mt-MT.js: + * globalize.culture.en-BZ.js: + * globalize.culture.nb-NO.js: + * globalize.culture.en-029.js: + * globalize.culture.dsb-DE.js: * globalize.culture.zh-CHS.js: * globalize.culture.smj-NO.js: - * globalize.culture.qut-GT.js: * globalize.culture.smj-SE.js: - * globalize.culture.dsb-DE.js: * globalize.culture.prs-AF.js: - * globalize.culture.quz-PE.js: - * globalize.culture.quz-EC.js: - * globalize.culture.hsb-DE.js: + * globalize.culture.smn-FI.js: + * globalize.culture.sms-FI.js: + * globalize.culture.kok-IN.js: + * globalize.culture.nso-ZA.js: * globalize.culture.syr-SY.js: + * globalize.culture.moh-CA.js: + * globalize.culture.zh-CHT.js: * globalize.culture.quz-BO.js: + * globalize.culture.quz-EC.js: + * globalize.culture.quz-PE.js: * globalize.culture.gsw-FR.js: - * globalize.culture.kok-IN.js: - * globalize.culture.sah-RU.js: - * globalize.culture.en-029.js: - * globalize.culture.nso-ZA.js: * globalize.culture.arn-CL.js: - * globalize.culture.az-Cyrl.js: + * globalize.culture.fil-PH.js: + * globalize.culture.qut-GT.js: + * globalize.culture.hsb-DE.js: + * globalize.culture.sma-SE.js: + * globalize.culture.sma-NO.js: + * globalize.culture.sah-RU.js: * globalize.culture.az-Latn.js: - * globalize.culture.bs-Cyrl.js: - * globalize.culture.iu-Latn.js: * globalize.culture.sr-Latn.js: - * globalize.culture.tg-Cyrl.js: - * globalize.culture.iu-Cans.js: + * globalize.culture.bs-Cyrl.js: * globalize.culture.sr-Cyrl.js: - * globalize.culture.uz-Cyrl.js: - * globalize.culture.mn-Cyrl.js: * globalize.culture.bs-Latn.js: - * globalize.culture.mn-Mong.js: - * globalize.culture.zh-Hant.js: + * globalize.culture.tg-Cyrl.js: + * globalize.culture.uz-Cyrl.js: + * globalize.culture.iu-Latn.js: + * globalize.culture.iu-Cans.js: * globalize.culture.zh-Hans.js: + * globalize.culture.zh-Hant.js: + * globalize.culture.az-Cyrl.js: + * globalize.culture.mn-Mong.js: * globalize.culture.uz-Latn.js: + * globalize.culture.mn-Cyrl.js: * globalize.culture.ha-Latn.js: * globalize.culture.tzm-Latn.js: - * globalize.culture.sr-Cyrl-CS.js: - * globalize.culture.sr-Cyrl-ME.js: + * globalize.culture.az-Cyrl-AZ.js: + * globalize.culture.iu-Latn-CA.js: * globalize.culture.bs-Latn-BA.js: - * globalize.culture.sr-Cyrl-BA.js: * globalize.culture.ha-Latn-NG.js: * globalize.culture.iu-Cans-CA.js: - * globalize.culture.iu-Latn-CA.js: - * globalize.culture.sr-Cyrl-RS.js: * globalize.culture.bs-Cyrl-BA.js: - * globalize.culture.uz-Latn-UZ.js: - * globalize.culture.uz-Cyrl-UZ.js: - * globalize.culture.az-Latn-AZ.js: - * globalize.culture.az-Cyrl-AZ.js: - * globalize.culture.sr-Latn-BA.js: - * globalize.culture.sr-Latn-CS.js: - * globalize.culture.sr-Latn-ME.js: * globalize.culture.mn-Mong-CN.js: + * globalize.culture.az-Latn-AZ.js: * globalize.culture.sr-Latn-RS.js: + * globalize.culture.sr-Latn-ME.js: + * globalize.culture.sr-Latn-CS.js: + * globalize.culture.sr-Latn-BA.js: + * globalize.culture.uz-Latn-UZ.js: + * globalize.culture.sr-Cyrl-BA.js: + * globalize.culture.sr-Cyrl-CS.js: + * globalize.culture.sr-Cyrl-ME.js: + * globalize.culture.sr-Cyrl-RS.js: + * globalize.culture.uz-Cyrl-UZ.js: * globalize.culture.tg-Cyrl-TJ.js: * globalize.culture.tzm-Latn-DZ.js: - * style.css: list in the nav are in line by default. - - * AdminController.cs: refactoring - - * App.master: Restores hallo editing by solving again - jQuery-ui ref - - * Login.aspx: Prettier code at building Urls - - * Admin.aspx: - * Index.aspx: - * AddRole.aspx: - * Backups.aspx: - * RemoveUser.aspx: - * RemoveRole..aspx: - * CreateBackup.aspx: - * BackupCreated.aspx: new master page - -2015-10-25 Paul Schneider + * style.css: nothing to see - * style.css: backgrounds don't really need - transitions. + * BlogsController.cs: Removes the `ValidateEdit` method, + and gives Admins the Blogger role ... a commit to blame in a + near future - * App.master: Removes a duplicated link to contact form + * yavsc.js: comes from yavsc - * parallax.js: enforce responsivity on small devices - supporting orientation capture. - -2015-10-24 Paul Schneider - - * p8-av4.s.jpg: - * star-939235_1280.s.jpg: - * star-939235_1280.xxs.jpg: a smaller background - - * style.css: adds animation - - * AdminController.cs: Fixes the call to Notify - - * Global.asax.cs: restores a dropped route - - * App.master: Drops script references to jQuery-ui and - Prettify from - the master page. They are heavy. - - * parallax.js: specifies the capture to false at calling the - event listener registration - - * UserPost.aspx: Fixes this page - - * Index.aspx: adds the `panel` class to embed those default - home page links - - * Web.csproj: dropping the imported parallax.js script - - * animated-overlay.gif: may be used in a futur work :-) - -2015-10-23 Paul Schneider - - * AdminController.cs: Notification when Admin group is created - -2015-10-22 Paul Schneider - - * parralax.js: YastepInDaPresentation - move backgrounds with mouse postion and mobile orientation - -2015-10-22 Paul Schneider - - * style.css: no max width for panels, it's too abstract for a - notion - - * RemovePost.aspx: Adds a Title and fixes the action call - - * Contact.aspx: cleans the code from totem - -2015-10-20 Paul Schneider - - * Web.config: - * Web.csproj: - * totem.jpg: - * App.master: - * twiter.png: - * facebook.png: - * totemprod.png: - * style.css: - * p8-av4.xxs.png: - * totem-banner.png: - * YavscHelpers.cs: - * concert.clear.jpg: - * totem-banner.xs.jpg: - * totem-banner.xxs.jpg: - * drummer-652345_1280.jpg: - * musician-923526_1.nb.jpg: - * musician-923526_1280.jpg: - * drummer-652345_1280.s.jpg: - * an-pierle-876094_1280.jpg: - * musician-923526_1.nbb.jpg: - * musician-923526_1280.s.jpg: - * drummer-652345_1280.xxs.jpg: - * musician-923526_1.nb.xs.jpg: - * live-concert-388160_1280.jpg: - * musician-923526_1280.xxs.jpg: - * musician-923526_1.nb.xxs.jpg: - * musician-923526_1.nbb.xs.jpg: - * musician-923526_1.nbb.xxs.jpg: - * live-concert-388160_1280.s.jpg: - * live-concert-388160_1280.xxs.jpg: - -2015-10-19 Paul Schneider - - * Web.csproj: - * packages.config: - * App.master: - * doxy.css: - * Edit.aspx: - * desert.css: - * sunburst.css: - * YavscHelpers.cs: - * prettify.css: - * lang-r.js: - * lang-n.js: - * UserPosts.aspx: - * lang-hs.js: - * lang-rd.js: - * lang-ml.js: - * lang-go.js: - * lang-vb.js: - * lang-xq.js: - * lang-tex.js: - * lang-tcl.js: - * lang-sql.js: - * lang-clj.js: - * lang-lua.js: - * PostActions.ascx: - * prettify.js: - * lang-css.js: - * HomeController.cs: - * lang-lisp.js: - * lang-wiki.js: - * lang-vhdl.js: - * lang-llvm.js: - * lang-dart.js: - * lang-yaml.js: - * lang-basic.js: - * lang-proto.js: - * lang-scala.js: - * lang-mumps.js: - * lang-erlang.js: - * lang-matlab.js: - * sons-of-obsidian.css: - * lang-pascal.js: - * lang-apollo.js: - * run_prettify.js: - - * TagPanel.ascx: displays the photo - - * IValueProvider.cs: useless, unused - - * TestExec.cs: pollution - -2015-10-18 Paul Schneider - - * BlogsController.cs: Fixes the redirection after bill edition - -2015-10-18 Paul Schneider - - * App.master: - * Title.aspx: Reset the Home page link - - * TagPanel.ascx: implements html links from tag related titles - -2015-10-17 Paul Schneider - - * Web.csproj: - * Index.aspx: - * Error.aspx: - * TagPanel.ascx: - * UserPost.aspx: - * PageLinks.ascx: - * HomeController.cs: - * BlogsController.cs: - -2015-10-17 Paul Schneider - - * Web.csproj: - * Global.asax.cs: - * yavsc.js: - * App.master: - * style.css: - * NoLogin.master: - * YavscHelpers.cs: - * TagPanel.ascx: - * UserPosts.aspx: - * AccountController.cs: - * FrontOfficeController.cs: - -2015-10-17 Paul Schneider - - * Web.csproj: - * Global.asax.cs: - * yavsc.js: - * App.master: - * style.css: - * Edit.aspx: - * Title.aspx: - * Index.aspx: - * YavscHelpers.cs: - * UserPost.aspx: - * UserPosts.aspx: - * PageLinks.ascx: - * TagControl.ascx: - * PostActions.ascx: - * HomeController.cs: - * AdminController.cs: - * BlogsController.cs: - * GoogleController.cs: - * Estimate.aspx: - * AccountController.cs: - * BlogsController.cs: - * knockout-jqAutocomplete.js: - * knockout-jqAutocomplete.min.js: - -2015-10-13 Paul Schneider - - * Index.aspx: - * Title.aspx: - * yavsc.scrollnotif.js: - * AccountController.cs: refactoring - - * yavsc.tags.js: Implements a js call - to the tag & untag methods - - * PostActions.ascx: a better html structure - - * yavsc.circles.js: - * AccountController.cs: code formatting - - * BlogsController.cs: Untag a post - - * style.css: yastyle, yet a better one. - - * BlogsController.cs: View the Title after edition - - * App.master: - * UserPosts.aspx: a nicer html structure - - * yavsc.js: Fixes notice & dimiss js - - * Login.aspx: refactoring - - * Edit.aspx: better html - - * UserPost.aspx: A promess to be allowed to tag. - - * Web.csproj: Adds yavsc.tags.js and yavsc.scrollnotifs.js to - the project decription. - -2015-10-10 Paul Schneider - - * Web.csproj: - * Global.asax.cs: - * App.master: - * Index.aspx: - * NoLogin.master: - * Title.aspx: - * Index.aspx: - * YavscHelpers.cs: - * Profile.aspx: - * UserPosts.aspx: - * HomeController.cs: - * BlogsController.cs: - * GoogleController.cs: - * AccountController.cs: + * Edit.aspx: refactoring `ValidateEdit` diff --git a/web/Controllers/AccountController.cs b/web/Controllers/AccountController.cs index 53ed2338..75592258 100644 --- a/web/Controllers/AccountController.cs +++ b/web/Controllers/AccountController.cs @@ -335,6 +335,7 @@ namespace Yavsc.Controllers prf.SetPropertyValue ("AccountNumber", model.AccountNumber); prf.SetPropertyValue ("BankedKey", model.BankedKey); prf.SetPropertyValue ("gcalid", model.GoogleCalendar); + prf.SetPropertyValue ("UITheme", model.UITheme); prf.Save (); if (editsTheUserName) { diff --git a/web/Controllers/AdminController.cs b/web/Controllers/AdminController.cs index 57ed5c5b..d292d4da 100644 --- a/web/Controllers/AdminController.cs +++ b/web/Controllers/AdminController.cs @@ -25,9 +25,9 @@ namespace Yavsc.Controllers public ActionResult Index() { // FIXME do this in a new installation script. - if (!Roles.RoleExists (roleName)) { - Roles.CreateRole (roleName); - YavscHelpers.Notify (ViewData, roleName + " " + LocalizedText.role_created); + if (!Roles.RoleExists (_adminRoleName)) { + Roles.CreateRole (_adminRoleName); + YavscHelpers.Notify (ViewData, _adminRoleName + " " + LocalizedText.role_created); } return View (); } @@ -156,6 +156,13 @@ namespace Yavsc.Controllers Roles.RemoveUserFromRole(username,rolename); return Redirect(returnUrl); } + + [Authorize(Roles="Admin")] + public ActionResult AddUserToRole(string username, string rolename, string returnUrl) + { + Roles.AddUsersToRole(new string[] { username } ,rolename); + return Redirect(returnUrl); + } /// /// Removes the user. /// @@ -223,7 +230,27 @@ namespace Yavsc.Controllers MembershipUserCollection c = Membership.GetAllUsers (); return View (c); } + [Authorize()] + public ActionResult UsersInRole (string rolename) + { + if (rolename == null) + rolename = "Admin"; + ViewData ["RoleName"] = rolename; + ViewData ["Roles"] = Roles.GetAllRoles (); + ViewData ["UsersInRole"] = Roles.GetUsersInRole (rolename); + return View (); + } + [Authorize()] + public ActionResult UserRoles (string username) + { + ViewData ["AllRoles"] = Roles.GetAllRoles (); + if (username == null) + username = User.Identity.Name; + ViewData ["UserName"] = username; + ViewData ["UsersRoles"] = Roles.GetRolesForUser (username); + return View (); + } /// /// a form to add a role /// @@ -257,7 +284,7 @@ namespace Yavsc.Controllers return View (Roles.GetAllRoles ()); } - private const string roleName = "Admin"; + private const string _adminRoleName = "Admin"; /// /// Assing the Admin role to the specified user in model. @@ -267,7 +294,7 @@ namespace Yavsc.Controllers public ActionResult Admin (NewAdminModel model) { // ASSERT (Roles.RoleExists (adminRoleName)) - string [] admins = Roles.GetUsersInRole (roleName); + string [] admins = Roles.GetUsersInRole (_adminRoleName); string currentUser = Membership.GetUser ().UserName; List users = new List (); foreach (MembershipUser u in Membership.GetAllUsers ()) { @@ -279,22 +306,21 @@ namespace Yavsc.Controllers ViewData ["admins"] = admins; ViewData ["useritems"] = users; if (ModelState.IsValid) { - Roles.AddUserToRole (model.UserName, roleName); - YavscHelpers.Notify(ViewData, model.UserName + " "+LocalizedText.was_added_to_the_role+" '" + roleName + "'"); + Roles.AddUserToRole (model.UserName, _adminRoleName); + YavscHelpers.Notify(ViewData, model.UserName + " "+LocalizedText.was_added_to_the_role+" '" + _adminRoleName + "'"); } else { if (admins.Length > 0) { if (! admins.Contains (Membership.GetUser ().UserName)) { ModelState.Remove("UserName"); ModelState.AddModelError("UserName",LocalizedText.younotadmin+"!"); - return View ("Index"); } } else { // No admin, gives the Admin Role to the current user - Roles.AddUserToRole (currentUser, roleName); + Roles.AddUserToRole (currentUser, _adminRoleName); admins = new string[] { currentUser }; YavscHelpers.Notify(ViewData, string.Format ( LocalizedText.was_added_to_the_empty_role, - currentUser, roleName)); + currentUser, _adminRoleName)); } } return View (model); diff --git a/web/Controllers/BlogsController.cs b/web/Controllers/BlogsController.cs index 2306ce3c..3cb34daa 100644 --- a/web/Controllers/BlogsController.cs +++ b/web/Controllers/BlogsController.cs @@ -77,7 +77,6 @@ namespace Yavsc.Controllers /// Title. /// Page index. /// Page size. - /// [HttpGet] public ActionResult Title (string title, int pageIndex = 0, int pageSize = 10) { @@ -89,7 +88,7 @@ namespace Yavsc.Controllers BlogManager.FindPost (username, title, sf, pageIndex, pageSize, out recordCount); var utc = new UTBlogEntryCollection (title); utc.AddRange (c); - ViewData ["RecordCount"] = recordCount; + ViewData ["ResultCount"] = recordCount; ViewData ["PageIndex"] = pageIndex; ViewData ["PageSize"] = pageSize; return View ("Title", utc); @@ -135,8 +134,10 @@ namespace Yavsc.Controllers ViewData ["BlogTitle"] = bupr.BlogTitle; ViewData ["Avatar"] = bupr.avatar; } - ViewData ["RecordCount"] = recordcount; UUBlogEntryCollection uuc = new UUBlogEntryCollection (user, c); + ViewData ["ResultCount"] = recordcount; + ViewData ["PageIndex"] = pageIndex; + ViewData ["PageSize"] = pageSize; return View ("UserPosts", uuc); } @@ -279,8 +280,8 @@ namespace Yavsc.Controllers /// /// The edit. /// Model. - [Authorize(Roles="Blogger")] - public ActionResult ValidateEdit (BlogEntry model) + [Authorize(Roles="")] + public ActionResult Edit (BlogEntry model) { ViewData ["SiteName"] = sitename; ViewData ["Author"] = Membership.GetUser ().UserName; @@ -289,13 +290,13 @@ namespace Yavsc.Controllers // ensures rights to update BlogManager.GetForEditing (model.Id, true); BlogManager.UpdatePost (model.Id, model.Title, model.Content, model.Visible, model.AllowedCircles); + YavscHelpers.Notify (ViewData, LocalizedText.BillUpdated); - } - else + } else { model.Id = BlogManager.Post (model.Author, model.Title, model.Content, model.Visible, model.AllowedCircles); - if (model.Photo != null) - BlogManager.UpdatePostPhoto (model.Id, model.Photo); - return RedirectToAction ("Title", new { title = model.Title }); + YavscHelpers.Notify (ViewData, LocalizedText.BillCreated); + } + BlogManager.UpdatePostPhoto (model.Id, model.Photo); } ViewData ["AllowedCircles"] = CircleManager.DefaultProvider.List ( @@ -312,7 +313,7 @@ namespace Yavsc.Controllers /// /// Identifier. [Authorize(Roles="Blogger")] - public ActionResult Edit (long postid) + public ActionResult EditId (long postid) { BlogEntry e = BlogManager.GetForEditing (postid); @@ -333,7 +334,7 @@ namespace Yavsc.Controllers Text = x.Title, Selected = e.AllowedCircles.Contains (x.Id) }); - return View (e); + return View ("Edit",e); } /// diff --git a/web/Controllers/FrontOfficeController.cs b/web/Controllers/FrontOfficeController.cs index 1886e892..b4322b68 100644 --- a/web/Controllers/FrontOfficeController.cs +++ b/web/Controllers/FrontOfficeController.cs @@ -16,6 +16,7 @@ using Yavsc.Model.Calendar; using System.Configuration; using Yavsc.Helpers; using Yavsc.Model.FrontOffice.Catalog; +using Yavsc.Model.Skill; namespace Yavsc.Controllers { @@ -85,12 +86,12 @@ namespace Yavsc.Controllers /// Estimate the specified id. /// /// Identifier. - public ActionResult Get (long id) + public ActionResult Get (long estimid) { - Estimate f = wfmgr.GetEstimate (id); + Estimate f = wfmgr.GetEstimate (estimid); if (f == null) { ModelState.AddModelError ("Id", "Wrong Id"); - return View (new Estimate () { Id=id } ); + return View (new Estimate () { Id=estimid } ); } return View (f); } @@ -162,11 +163,11 @@ namespace Yavsc.Controllers /// Catalog this instance. /// [AcceptVerbs ("GET")] - public ActionResult Brand (string id) + public ActionResult Brand (string brandid) { Catalog c = CatalogManager.GetCatalog (); - ViewData ["BrandName"] = id; - return View (c.GetBrand (id)); + ViewData ["BrandName"] = brandid; + return View (c.GetBrand (brandid)); } /// @@ -200,10 +201,10 @@ namespace Yavsc.Controllers /// Pc. /// Preference. [AcceptVerbs ("GET")] - public ActionResult Product (string id, string pc, string pref) + public ActionResult Product (string brandid, string pc, string pref) { Product p = null; - ViewData ["BrandName"] = id; + ViewData ["BrandName"] = brandid; ViewData ["ProdCatRef"] = pc; ViewData ["ProdRef"] = pref; Catalog cat = CatalogManager.GetCatalog (); @@ -212,7 +213,7 @@ namespace Yavsc.Controllers ViewData ["RefType"] = "Catalog"; return View ("ReferenceNotFound"); } - Brand b = cat.GetBrand (id); + Brand b = cat.GetBrand (brandid); if (b == null) { ViewData ["RefType"] = "Brand"; return View ("ReferenceNotFound"); @@ -259,5 +260,50 @@ namespace Yavsc.Controllers } } + /// + /// Booking the specified model. + /// + /// Model. + public ActionResult Booking (BookingQuery model) + { + return View (); + } + + /// + /// Skills the specified model. + /// + [Authorize(Roles="Admin")] + public ActionResult Skills (string search) + { + if (search == null) + search = "%"; + var skills = SkillManager.FindSkill (search); + return View (skills); + } + + /// + /// Display and should + /// offer Ajax edition of + /// user's skills. + /// + /// the User Skills Profile. + [Authorize()] + public ActionResult UserSkills (PerformerProfile usp) + { + if (usp.UserName == null) + // this is not a call to update, + // and this can not concern another user + // than the current logged one. + usp = new PerformerProfile( User.Identity.Name ); + // if (usp.UserName was null) { + usp = SkillManager.GetUserSkills (usp.UserName); + var skills = SkillManager.FindSkill ("%"); + ViewData ["SiteSkills"] = skills; + // TODO or not to do, handle a skills profile update, + // actually performed via the Web API :-° + // } else if (ModelState.IsValid) {} + return View (usp); + } + } } diff --git a/web/Controllers/GoogleController.cs b/web/Controllers/GoogleController.cs index 332016d0..7d8ae296 100644 --- a/web/Controllers/GoogleController.cs +++ b/web/Controllers/GoogleController.cs @@ -78,12 +78,9 @@ namespace Yavsc.Controllers if (string.IsNullOrWhiteSpace (returnUrl)) returnUrl = "/"; Session ["returnUrl"] = returnUrl; - OAuth2 oa = new OAuth2 (AuthGRU,clientId,clientSecret); - oa.Login (Response, SetSessionSate ()); + string state = SetSessionSate (); + Response.Login (state, AuthGRU); } - private string clientId = ConfigurationManager.AppSettings ["GOOGLE_CLIENT_ID"]; - private string clientSecret = ConfigurationManager.AppSettings ["GOOGLE_CLIENT_SECRET"]; - private string clientApiKey = ConfigurationManager.AppSettings ["GOOGLE_API_KEY"]; /// /// Gets the cal auth. /// @@ -93,8 +90,7 @@ namespace Yavsc.Controllers if (string.IsNullOrWhiteSpace (returnUrl)) returnUrl = "/"; Session ["returnUrl"] = returnUrl; - OAuth2 oa = new OAuth2 (CalendarGRU,clientId,clientSecret); - oa.GetCalendarScope (Response, SetSessionSate ()); + Response.CalLogin (SetSessionSate (), CalendarGRU); } /// @@ -107,8 +103,7 @@ namespace Yavsc.Controllers public ActionResult CalAuth () { string msg; - OAuth2 oa = new OAuth2 (CalendarGRU,clientId,clientSecret); - + OAuth2 oa = GoogleHelpers.CreateOAuth2 (CalendarGRU); AuthToken gat = oa.GetToken (Request, (string) Session ["state"], out msg); if (gat == null) { YavscHelpers.Notify(ViewData, msg); @@ -127,6 +122,7 @@ namespace Yavsc.Controllers /// It should be called immediatly after getting the token from Google, in /// order to save a descent value as expiration date. /// + /// pr. /// Gat. private void SaveToken (ProfileBase pr, AuthToken gat) { @@ -145,7 +141,7 @@ namespace Yavsc.Controllers public ActionResult Auth () { string msg; - OAuth2 oa = new OAuth2 (AuthGRU,clientId,clientSecret); + OAuth2 oa = GoogleHelpers.CreateOAuth2 (AuthGRU); AuthToken gat = oa.GetToken (Request, (string)Session ["state"], out msg); if (gat == null) { YavscHelpers.Notify(ViewData, msg); @@ -268,9 +264,7 @@ namespace Yavsc.Controllers returnUrl = Url.Action ("ChooseCalendar") // "ChooseCalendar?returnUrl="+HttpUtility.UrlEncode(returnUrl) }); } - string cred = OAuth2.GetFreshGoogleCredential (HttpContext.Profile); - CalendarApi c = new CalendarApi (clientApiKey); - CalendarList cl = c.GetCalendars (cred); + CalendarList cl = GoogleHelpers.GetCalendars(HttpContext.Profile); ViewData ["returnUrl"] = Session ["chooseCalReturnUrl"]; return View (cl); } @@ -301,7 +295,7 @@ namespace Yavsc.Controllers [Authorize,HttpGet] public ActionResult Book () { - var model = new BookQuery (); + var model = new BookingQuery (); model.StartDate = DateTime.Now; model.EndDate = model.StartDate.AddDays(2); model.StartHour = DateTime.Now.ToString("HH:mm"); @@ -315,49 +309,35 @@ namespace Yavsc.Controllers /// The query. /// Model. [Authorize,HttpPost] - public ActionResult Book (BookQuery model) + public ActionResult Book (BookingQuery model) { + DateTime mindate = DateTime.Now; + if (model.StartDate.Date < mindate.Date){ + ModelState.AddModelError ("StartDate", LocalizedText.FillInAFutureDate); + } + if (model.EndDate < model.StartDate) + ModelState.AddModelError ("EndDate", LocalizedText.StartDateAfterEndDate); + if (ModelState.IsValid) { - DateTime mindate = DateTime.Now; - if (model.StartDate.Date < mindate.Date){ - ModelState.AddModelError ("StartDate", LocalizedText.FillInAFutureDate); - } - if (model.EndDate < model.StartDate) - ModelState.AddModelError ("EndDate", LocalizedText.StartDateAfterEndDate); - - var muc = Membership.FindUsersByName (model.Person); - if (muc.Count == 0) { - ModelState.AddModelError ("Person", LocalizedText.Non_existent_user); - } - if (!Roles.IsUserInRole (model.Role)) { - ModelState.AddModelError ("Role", LocalizedText.UserNotInThisRole); - } - ProfileBase upr = ProfileBase.Create (model.Person); - var gcalid = upr.GetPropertyValue ("gcalid"); - if (gcalid is DBNull) - ModelState.AddModelError ("Person", LocalizedText.No_calendar_for_this_user); - if (ModelState.IsValid) { - string calid = (string) gcalid; - DateTime maxdate = model.EndDate; - CalendarApi c = new CalendarApi (clientApiKey); - CalendarEventList events; - try { - string creds = OAuth2.GetFreshGoogleCredential (upr); - events = c.GetCalendar (calid, mindate, maxdate, creds); - YavscHelpers.Notify (ViewData, "Google calendar API call success"); - } catch (WebException ex) { - string response; - using (var stream = ex.Response.GetResponseStream()) - using (var reader = new StreamReader(stream)) - { - response = reader.ReadToEnd(); + foreach (string rolename in model.Roles) { + foreach (string username in Roles.GetUsersInRole(rolename)) { + try { + var pr = ProfileBase.Create(username); + var events = pr.GetEvents(model.StartDate,model.EndDate); + } catch (WebException ex) { + string response; + using (var stream = ex.Response.GetResponseStream()) + using (var reader = new StreamReader(stream)) + { + response = reader.ReadToEnd(); + } + YavscHelpers.Notify (ViewData, + string.Format( + "Google calendar API exception {0} : {1}
{2}
", + ex.Status.ToString(), + ex.Message, + response)); } - YavscHelpers.Notify (ViewData, - string.Format( - "Google calendar API exception {0} : {1}
{2}
", - ex.Status.ToString(), - ex.Message, - response)); } } } diff --git a/web/Controllers/HomeController.cs b/web/Controllers/HomeController.cs index 80e58123..9b3b5624 100644 --- a/web/Controllers/HomeController.cs +++ b/web/Controllers/HomeController.cs @@ -14,8 +14,6 @@ using Yavsc.Helpers; using Yavsc; using System.Web.Mvc; using Yavsc.Model.Blogs; -using System.Web.Security; -using System.Web.Profile; namespace Yavsc.Controllers { @@ -24,6 +22,21 @@ namespace Yavsc.Controllers ///
public class HomeController : Controller { + // Site name + private static string name = null; + + /// + /// Gets or sets the site name. + /// + /// The name. + [Obsolete("Use YavscHelpers.SiteName insteed.")] + public static string Name { + get { + if (name == null) + name = WebConfigurationManager.AppSettings ["Name"]; + return name; + } + } /// /// Lists the referenced assemblies. @@ -72,23 +85,14 @@ namespace Yavsc.Controllers /// public ActionResult Index () { - if (Session.IsNewSession) { - string uid = (!Request.IsAuthenticated) ? Request.AnonymousID : User.Identity.Name; - ProfileBase pr = - ProfileBase.Create (uid); - bool ac = (bool) pr.GetPropertyValue ("allowcookies"); - if (!ac) - YavscHelpers.Notify (ViewData, LocalizedText.ThisSiteUsesCookies, - "function(){Yavsc.ajax(\"/Yavsc/AllowCookies\", { id:'"+uid+"' });}", - LocalizedText.I_understood); - } - - foreach (string tagname in new string[] {"Accueil","Événements","Mentions légales"}) + foreach (string tagname in new string[] {"Accueil","Yavsc","Événements","Mentions légales"}) { TagInfo ti = BlogManager.GetTagInfo (tagname); // TODO specialyze BlogEntry creating a PhotoEntry ViewData [tagname] = ti; + } + return View (); } /// diff --git a/web/Formatters/FormatterException.cs b/web/Formatters/FormatterException.cs index 08725d8e..13963aae 100644 --- a/web/Formatters/FormatterException.cs +++ b/web/Formatters/FormatterException.cs @@ -50,8 +50,15 @@ namespace Yavsc.Formatters public FormatterException(string message,Exception innerException):base(message,innerException) { } - + /// + /// Gets or sets the output. + /// + /// The output. public string Output { get; set; } + /// + /// Gets or sets the error. + /// + /// The error. public string Error { get; set; } } } diff --git a/web/Helpers/Google/OAuth2.cs b/web/Helpers/Google/OAuth2.cs index 03da8e16..aae35812 100644 --- a/web/Helpers/Google/OAuth2.cs +++ b/web/Helpers/Google/OAuth2.cs @@ -58,6 +58,8 @@ namespace Yavsc.Helpers.Google /// Initializes a new instance of the class. /// /// Redirect URI. + /// Client identifier. + /// Client secret. public OAuth2 (string redirectUri, string clientId, string clientSecret) { RedirectUri = redirectUri; diff --git a/web/Models/App.master b/web/Models/App.master index 0e2e44c2..912738d1 100644 --- a/web/Models/App.master +++ b/web/Models/App.master @@ -2,12 +2,16 @@ <% ViewState["orgtitle"] = Html.Translate(Page.Title); %> -<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; %> +<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; +Page.Theme = (string) Profile.UITheme; +Page.StyleSheetTheme = (string) Profile.UITheme; %> - + + +" /> " /> " /> " /> @@ -15,14 +19,16 @@ " /> - + +<%=Ajax.GlobalizationScript()%> + @@ -74,8 +80,6 @@ else {%> Yavsc.notice(<%=note.body%>, <%=note.click_action%>, <%=note.click_acti - -
© 2015 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 diff --git a/web/Models/AppAdmin.master b/web/Models/AppAdmin.master index ce7c3e06..77f74720 100644 --- a/web/Models/AppAdmin.master +++ b/web/Models/AppAdmin.master @@ -2,12 +2,19 @@ <% ViewState["orgtitle"] = Html.Translate(Page.Title); %> -<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; %> +<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; +Page.Theme = (string) Profile.UITheme; +Page.StyleSheetTheme = (string) Profile.UITheme; + +%> - + + + +" /> " /> " /> " /> @@ -23,6 +30,7 @@ var apiBaseUrl = '<%=Url.Content(Yavsc.WebApiConfig.UrlPrefixRelative)%>'; + @@ -85,8 +93,6 @@ else {%> Yavsc.notice(<%=note.body%>, <%=note.click_action%>); <% } %> - -
© 2015 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 diff --git a/web/Models/NoLogin.master b/web/Models/NoLogin.master index 0e88e45a..0bb0b48a 100644 --- a/web/Models/NoLogin.master +++ b/web/Models/NoLogin.master @@ -2,12 +2,19 @@ <% ViewState["orgtitle"] = Html.Translate(Page.Title); %> -<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; %> +<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; +Page.Theme = (string) Profile.UITheme; +Page.StyleSheetTheme = (string) Profile.UITheme; + +%> - + + + +" /> " /> " /> " /> @@ -15,14 +22,14 @@ " /> - + +<%=Ajax.GlobalizationScript()%> - @@ -41,7 +48,7 @@ var apiBaseUrl = '<%=Url.Content(Yavsc.WebApiConfig.UrlPrefixRelative)%>'; $(document).ready(function(){ <% foreach (Notification note in (IEnumerable) ViewData ["Notifications"] ) { if (note.click_action == null) {%> Yavsc.notice(<%=note.body%>); <% } -else {%> Yavsc.notice(<%=note.body%>, <%=note.click_action%>); <% } %> +else {%> Yavsc.notice(<%=note.body%>, <%=note.click_action%>, <%=note.click_action_name%>); <% } %> <% } %> }); @@ -51,8 +58,6 @@ else {%> Yavsc.notice(<%=note.body%>, <%=note.click_action%>); <% } %> - -
© 2015 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 diff --git a/web/Scripts/yavsc.circles.js b/web/Scripts/yavsc.circles.js index 6ae47636..ad67f7fa 100644 --- a/web/Scripts/yavsc.circles.js +++ b/web/Scripts/yavsc.circles.js @@ -1,9 +1,6 @@  - -var errspanid="msg"; var CirclesApiUrl = apiBaseUrl + "/Circle"; - function editNewCircle() { if ($('#fncirc').hasClass('hidden')) $('#fncirc').removeClass('hidden') $('#lgdnvcirc').html("Creation d'un cercle"); diff --git a/web/Scripts/yavsc.js b/web/Scripts/yavsc.js index 52b72288..1d936c32 100644 --- a/web/Scripts/yavsc.js +++ b/web/Scripts/yavsc.js @@ -1,7 +1,7 @@ -var Yavsc = (function(apiBaseUrl){ + +var Yavsc = (function(apiBaseUrl){ var self = {}; - self.dumpprops = function (obj) { var str = ""; for(var k in obj) @@ -30,9 +30,11 @@ self.dimiss = function () { $(this).parent().remove(); }; -self.ajax = function (method,data,callback) { +self.ajax = function (method,data,callback,badInputCallback,errorCallBack) { +if (!badInputCallback) badInputCallback=Yavsc.onAjaxBadInput; +if (!errorCallBack) errorCallBack=Yavsc.onAjaxError; $.ajax({ - url: self.apiBaseUrl+method, + url: self.apiBaseUrl+'/'+method, type: "POST", data: data, success: function (response) { @@ -74,7 +76,7 @@ self.onAjaxBadInput = function (data) var errspanid = "Err_" + value.key; var errspan = document.getElementById(errspanid); if (errspan==null) - alert('enoent '+errspanid); + Yavsc.notice('ENOTANODE: '+errspanid); else errspan.innerHTML=value.errors.join("
"); }); diff --git a/web/Scripts/yavsc.tags.js b/web/Scripts/yavsc.tags.js index 0ace63a8..a5b6b31e 100644 --- a/web/Scripts/yavsc.tags.js +++ b/web/Scripts/yavsc.tags.js @@ -1,4 +1,5 @@ -var Tags = (function(apiBaseUrl){ + +var Tags = (function(apiBaseUrl){ var self = {}; self.blogsApiUrl = (apiBaseUrl || '/api') + "/Blogs"; diff --git a/web/Views/Account/Circles.aspx b/web/Views/Account/Circles.aspx index e1f46398..1f156549 100644 --- a/web/Views/Account/Circles.aspx +++ b/web/Views/Account/Circles.aspx @@ -34,8 +34,7 @@ $(function(){ $("#tbc").stupidtable(); }); - - + +
Interface utilisateur +<%= Html.LabelFor(model => model.UITheme) %> : +<%= Html.TextBox("UITheme") %> +<%= Html.ValidationMessage("UITheme", "*") %> +
<% } %> - - +
diff --git a/web/Views/Admin/AddMemberToRole.ascx b/web/Views/Admin/AddMemberToRole.ascx deleted file mode 100644 index bc63fa55..00000000 --- a/web/Views/Admin/AddMemberToRole.ascx +++ /dev/null @@ -1,11 +0,0 @@ -<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> - -<%= Html.ValidationSummary() %> -<% using(Ajax.BeginForm()) - { %> -Nom du rôle : -<%= Html.TextBox( "RoleName" ) %> -<%= Html.ValidationMessage("RoleName", "*") %>
- -<% } %> - diff --git a/web/Views/Admin/AddRole.aspx b/web/Views/Admin/AddRole.aspx index ded10055..3f9f40da 100644 --- a/web/Views/Admin/AddRole.aspx +++ b/web/Views/Admin/AddRole.aspx @@ -9,7 +9,6 @@ Nom du rôle : <%= Html.ValidationMessage("RoleName", "*") %>
<% } %> -<%= Html.Partial("AddMemberToRole")%> diff --git a/web/Views/Admin/Admin.aspx b/web/Views/Admin/Admin.aspx index aec1c763..6dba1dcd 100644 --- a/web/Views/Admin/Admin.aspx +++ b/web/Views/Admin/Admin.aspx @@ -6,7 +6,10 @@ <% foreach (string u in (string[])ViewData["admins"]) { %> <% } %> diff --git a/web/Views/Admin/RoleList.aspx b/web/Views/Admin/RoleList.aspx index 613ffead..8706a1dc 100644 --- a/web/Views/Admin/RoleList.aspx +++ b/web/Views/Admin/RoleList.aspx @@ -1,4 +1,6 @@ <%@ Page Title="Liste des rôles" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Models/AppAdmin.master" %> + + Roles:
    diff --git a/web/Views/Admin/UserList.aspx b/web/Views/Admin/UserList.aspx index c329ef09..a4a875e6 100644 --- a/web/Views/Admin/UserList.aspx +++ b/web/Views/Admin/UserList.aspx @@ -6,8 +6,18 @@
      <%foreach (MembershipUser user in Model){ %>
    • <%=user.UserName%> (created <%=user.CreationDate.ToString("D")%>) <%=user.Email%> <%=(user.IsApproved)?"":"("+LocalizedText.Not_Approuved+")"%> <%=user.IsOnline?LocalizedText.Online:LocalizedText.Offline%> +
      + Roles : +
        + <%foreach (string role in Roles.GetRolesForUser(user.UserName)){ %> +
      • <%=role%>
      • + <% } %> +
      + + <% if (Roles.IsUserInRole("Admin")) { %> - <%= Html.ActionLink(LocalizedText.Remove,"RemoveUser", new { username = user.UserName }, new { @class="actionlink" } ) %> + <%= Html.ActionLink(LocalizedText.Remove,"RemoveUser", new { username = user.UserName, returnUrl = Request.Url.PathAndQuery }, new { @class="actionlink" } ) %> + <%= Html.ActionLink("Blogger","AddUserToRole", new { username = user.UserName, rolename="Blogger", returnUrl = Request.Url.PathAndQuery }, new { @class="actionlink" } ) %> <% } %>
    • <% }%>
    diff --git a/web/Views/Admin/UsersInRole.aspx b/web/Views/Admin/UsersInRole.aspx index a294ef8c..d2ffc7e0 100644 --- a/web/Views/Admin/UsersInRole.aspx +++ b/web/Views/Admin/UsersInRole.aspx @@ -1,11 +1,21 @@ -<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> - - - - - - -
    +<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" Title="UsersInRoleOf" MasterPageFile="~/Models/AppAdmin.master" %> + +<% ViewState["orgtitle"] = string.Format ( LocalizedText.UsersInRole , Html.Encode(ViewData["RoleName"])); %> +<% Page.Title = ViewState["orgtitle"] + " - " + YavscHelpers.SiteName; %> -
    - + + +

    "> +<%=ViewState["orgtitle"]%> +- "><%= YavscHelpers.SiteName %> +

    +
    + +
      + <%foreach (string username in (string[]) ViewData["UsersInRole"]){ %> +
    • <%= username %>
    • + <% } %> +
    + +<%= Html.Partial("AddUserToRole") %> +
    \ No newline at end of file diff --git a/web/Views/BackOffice/Index.aspx b/web/Views/BackOffice/Index.aspx index 66563f3f..3f7b78be 100644 --- a/web/Views/BackOffice/Index.aspx +++ b/web/Views/BackOffice/Index.aspx @@ -1,9 +1,9 @@ <%@ Page Title="Back office" Language="C#" MasterPageFile="~/Models/App.master" Inherits="System.Web.Mvc.ViewPage" %> - -
    • <%= Html.ActionLink("Catalog","Catalog","FrontOffice" ) %> -
    + +
  • <%= Html.ActionLink(LocalizedText.Skill,"Skills","FrontOffice" ) %>
  • +
diff --git a/web/Views/Blogs/Edit.aspx b/web/Views/Blogs/Edit.aspx index db3aba3e..5b0a9dc7 100644 --- a/web/Views/Blogs/Edit.aspx +++ b/web/Views/Blogs/Edit.aspx @@ -12,8 +12,13 @@ + -<% using(Html.BeginForm("ValidateEdit","Blogs")) { %> +<% using(Html.BeginForm("Edit","Blogs")) { %>
Contrôle d'accès au Billet <%= Html.LabelFor(model => model.Visible) %> : <%= Html.CheckBox( "Visible" ) %> @@ -234,10 +239,5 @@ var data = new FormData($('#frmajax').get()[0]); - \ No newline at end of file diff --git a/web/Views/Blogs/Index.aspx b/web/Views/Blogs/Index.aspx index 67e0ad52..49eb8665 100644 --- a/web/Views/Blogs/Index.aspx +++ b/web/Views/Blogs/Index.aspx @@ -8,6 +8,9 @@

" class="usertitleref"><%=Html.Encode(g.Key)%>

<% foreach (var p in g) { %>
+<% if (p.Photo != null ) { %> +<%=p.Photo%> +<% } %>

<%= Html.Markdown(p.Intro,"/bfiles/"+p.Id+"/") %>

<%= Html.Partial("PostActions",p)%>
<% } %> diff --git a/web/Views/Blogs/PostActions.ascx b/web/Views/Blogs/PostActions.ascx index befcdb55..6c2d12e3 100644 --- a/web/Views/Blogs/PostActions.ascx +++ b/web/Views/Blogs/PostActions.ascx @@ -1,11 +1,14 @@ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> -
-<%= u %> <%= Html.ActionLink("Remove","RemoveFromRole",new { username = u, rolename="Admin", returnUrl = Request.Url.PathAndQuery })%> +<%= u %> +<%= Html.ActionLink("Remove","RemoveFromRole", +new { username = u, rolename="Admin", returnUrl = Request.Url.PathAndQuery })%> +
- - - - - - - - - - -<% int lc=0; - if (Model.Lines!=null) - foreach (Writting wr in Model.Lines) { lc++; %> -row" id="wr<%=wr.Id%>"> - - - - - - -<% } %> - -
<%=Yavsc.Model.LocalizedText.Description%><%=Yavsc.Model.LocalizedText.Product_reference%><%=Yavsc.Model.LocalizedText.Count%><%=Yavsc.Model.LocalizedText.Unitary_cost%>
<%=wr.Description%><%=wr.ProductReference%><%=wr.Count%><%=wr.UnitaryCost%> - -
-<% } %> -<% } %> - - - - - - -
/// true if visible; otherwise, false. public bool Visible { get; set ; } + + /// + /// Gets or sets the rate. + /// + /// The rate. + public int Rate { get; set; } + + /// + /// Gets or sets the circles allowed to read this ticket. + /// An empty collection specifies a public post. + /// + /// The circles. + [Display(Name="Cercles autorisés")] + public long[] AllowedCircles { get; set; } + + /// + /// Gets or sets the tags. + /// + /// The tags. + public string [] Tags { get; set ; } } } diff --git a/yavscModel/Blogs/BasePostInfo.cs b/yavscModel/Blogs/BasePostInfo.cs index f4f34749..f1d45b4c 100644 --- a/yavscModel/Blogs/BasePostInfo.cs +++ b/yavscModel/Blogs/BasePostInfo.cs @@ -37,6 +37,21 @@ namespace Yavsc.Model.Blogs /// The intro. ///
public string Intro; + + public bool Truncated; + public BasePostInfo (BlogEntry be) + { + Title = be.Title; + Id = be.Id; + Posted = be.Posted; + Modified = be.Modified; + Intro = MarkdownHelper.MarkdownIntro (be.Content, out Truncated); + Photo = be.Photo; + Tags = be.Tags; + AllowedCircles = be.AllowedCircles; + Visible = be.Visible; + Rate = be.Rate; + } } } diff --git a/yavscModel/Blogs/BlogEntry.cs b/yavscModel/Blogs/BlogEntry.cs index e46809e0..b6644e4e 100644 --- a/yavscModel/Blogs/BlogEntry.cs +++ b/yavscModel/Blogs/BlogEntry.cs @@ -11,22 +11,7 @@ namespace Yavsc.Model.Blogs /// Blog entry. ///
public class BlogEntry : BasePost { - - - /// - /// Gets or sets the circles allowed to read this ticket. - /// An empty collection specifies a public post. - /// - /// The circles. - [Display(Name="Cercles autorisés")] - public long[] AllowedCircles { get; set; } - - /// - /// Gets or sets the tags. - /// - /// The tags. - public string [] Tags { get; set ; } - + string content; /// @@ -34,7 +19,6 @@ namespace Yavsc.Model.Blogs /// /// The content. [DisplayName("Corps du billet")] - [Required(ErrorMessage = "S'il vous plait, saisissez un texte.")] public string Content { get { return content; diff --git a/yavscModel/Blogs/BlogEntryCollection.cs b/yavscModel/Blogs/BlogEntryCollection.cs index 02b48d17..1f8bc3e3 100644 --- a/yavscModel/Blogs/BlogEntryCollection.cs +++ b/yavscModel/Blogs/BlogEntryCollection.cs @@ -84,19 +84,10 @@ namespace Yavsc.Model.Blogs /// public IEnumerable> GroupByTitle () { - bool truncated; return from be in this orderby be.Posted descending - group - new BasePostInfo { Author = be.Author, Id = be.Id, - Posted = be.Posted, Modified = be.Modified, - Intro = MarkdownHelper.MarkdownIntro (be.Content, out truncated), - Visible = be.Visible, - Photo = be.Photo - } - by be.Title - into titlegroup - select titlegroup; + group new BasePostInfo(be) by be.Title + into titlegroup select titlegroup; } /// @@ -105,19 +96,10 @@ namespace Yavsc.Model.Blogs /// The by user. public IEnumerable> GroupByUser () { - bool truncated; return from be in this orderby be.Posted descending group - new BasePostInfo { - Title = be.Title, - Id = be.Id, - Posted = be.Posted, - Modified = be.Modified, - Intro = MarkdownHelper.MarkdownIntro (be.Content, out truncated), - Photo = be.Photo, - Visible = be.Visible - } + new BasePostInfo (be) by be.Author into usergroup select usergroup; diff --git a/yavscModel/Blogs/BlogHelper.cs b/yavscModel/Blogs/BlogHelper.cs deleted file mode 100644 index 21579cc4..00000000 --- a/yavscModel/Blogs/BlogHelper.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Configuration; -using System.Reflection; -using System.Collections.Specialized; - -namespace Yavsc.Model.Blogs -{ - /// - /// Blog helper. - /// - public static class BlogHelper - { - /// - /// Gets the provider. - /// - /// The provider. - public static BlogProvider GetProvider () - { - DataProviderConfigurationSection config = ConfigurationManager.GetSection ("system.web/blog") as DataProviderConfigurationSection; - if (config == null) - throw new ConfigurationErrorsException("The configuration bloc for the blog provider was not found"); - ProviderSettings celt = - config.Providers[config.DefaultProvider]; - if (config == null) - throw new ConfigurationErrorsException("The default blog provider was not found"); - ConstructorInfo ci = Type.GetType (celt.Type).GetConstructor (Type.EmptyTypes); - BlogProvider bp = ci.Invoke (Type.EmptyTypes) as BlogProvider; - bp.Initialize (celt.Name, celt.Parameters); - return bp; - } - - - } - -} diff --git a/yavscModel/Blogs/BlogManager.cs b/yavscModel/Blogs/BlogManager.cs index 5242baaf..f4b3dceb 100644 --- a/yavscModel/Blogs/BlogManager.cs +++ b/yavscModel/Blogs/BlogManager.cs @@ -12,11 +12,21 @@ using System.Collections.Generic; namespace Yavsc.Model.Blogs { + /// /// Blog manager. /// - public static class BlogManager + public static class BlogManager { + static BlogProvider provider = null; + static BlogProvider Provider { + get { + if (provider == null) + provider = ManagerHelper.GetDefaultProvider + ("system.web/blog"); + return provider; + } + } /// /// Removes the comment. /// @@ -39,20 +49,6 @@ namespace Yavsc.Model.Blogs Provider.Comment (from, postid, content); } - static BlogProvider provider; - - /// - /// Gets the provider. - /// - /// The provider. - public static BlogProvider Provider { - get { - if (provider == null) - provider = BlogHelper.GetProvider (); - return provider; - } - } - /// /// Gets the post. /// @@ -99,7 +95,10 @@ namespace Yavsc.Model.Blogs { Provider.UpdatePost (postid, title, content, visible, cids); } - + public static void UpdatePost (BlogEntry be) + { + Provider.UpdatePost (be); + } /// /// Updates the post photo. /// @@ -152,9 +151,22 @@ namespace Yavsc.Model.Blogs } Provider.RemoveTitle (username, title); } - public static TagInfo GetTagInfo(string tagname) + /// + /// Gets the tag info. + /// + /// The tag info. + /// Tagname. + /// Page index. + /// Page size. + public static TagInfo GetTagInfo(string tagname, int pageIndex=0, int pageSize=50) { - return Provider.GetTagInfo (tagname); + var res = new TagInfo (tagname); + int recordCount = 0; + var posts = Provider.FindPost (null,tagname,FindBlogEntryFlags.MatchTag,pageIndex,pageSize,out recordCount); + res.Titles = posts.GroupByTitle ().ToArray(); + res.Name = tagname; + // out int recordCount , + return res; } /// /// Lasts the posts. @@ -192,9 +204,9 @@ namespace Yavsc.Model.Blogs } - public static void Note (long postid, int note) + public static void Rate (long postid, int rate) { - Provider.Note (postid, note); + Provider.Rate (postid, rate); } /// diff --git a/yavscModel/Blogs/BlogProvider.cs b/yavscModel/Blogs/BlogProvider.cs index ce929ef5..f7f4b921 100644 --- a/yavscModel/Blogs/BlogProvider.cs +++ b/yavscModel/Blogs/BlogProvider.cs @@ -19,12 +19,6 @@ namespace Yavsc.Model.Blogs /// Postid. public abstract BlogEntry GetPost (long postid); - /// - /// Gets the tag info. - /// - /// The tag info. - /// Tagname. - public abstract TagInfo GetTagInfo (string tagname); /// /// Gets the post collection from a given user and at a given title. @@ -50,8 +44,8 @@ namespace Yavsc.Model.Blogs /// Note the specified postid and note. /// /// Postid. - /// Note. - public abstract void Note (long postid, int note); + /// rate. + public abstract void Rate (long postid, int rate); /// @@ -65,6 +59,13 @@ namespace Yavsc.Model.Blogs /// Allowed circles. public abstract void UpdatePost (long postid, string title, string content, bool visible, long[] allowedCircles); + /// + /// Updates the post. + /// + /// Be. + public abstract void UpdatePost ( BlogEntry be ); + + /// /// Finds a post. /// diff --git a/yavscModel/Blogs/TagInfo.cs b/yavscModel/Blogs/TagInfo.cs index 03f67629..cd38c15d 100644 --- a/yavscModel/Blogs/TagInfo.cs +++ b/yavscModel/Blogs/TagInfo.cs @@ -20,13 +20,14 @@ // along with this program. If not, see . using System; using System.Collections.Generic; +using System.Linq; namespace Yavsc.Model.Blogs { /// /// Tag info. /// - public abstract class TagInfo + public class TagInfo { string name; /// @@ -54,7 +55,7 @@ namespace Yavsc.Model.Blogs /// Gets the titles. /// /// The titles. - public abstract IEnumerable Titles { get; } + public IEnumerable> Titles { get; set; } } } diff --git a/yavscModel/Calendar/BookQuery.cs b/yavscModel/Calendar/BookQuery.cs deleted file mode 100644 index 35dd7d21..00000000 --- a/yavscModel/Calendar/BookQuery.cs +++ /dev/null @@ -1,86 +0,0 @@ -// -// AskForADate.cs -// -// Author: -// Paul Schneider -// -// Copyright (c) 2014 Paul Schneider -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see . -using System; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -namespace Yavsc.Model.Calendar -{ - /// - /// Ask for A date. - /// - public class BookQuery - { - - /// - /// Gets or sets the prefered date. - /// - /// The prefered date. - [DataType(DataType.Date)] - [DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)] - [Display(ResourceType=typeof(LocalizedText),Name="StartDate")] - public DateTime StartDate { get; set; } - - /// - /// Gets or sets the minimum time. - /// - /// The minimum time. - [RegularExpression("\\d\\d:\\d\\d")] - [Display(ResourceType=typeof(LocalizedText),Name="StartHour")] - [Required(ErrorMessage= "S'il vous plait, saisissez une heure de début d'intervention")] - public string StartHour { get; set; } - - /// - /// Gets or sets the max date. - /// - /// The max date. - [Display(Name="EndDate",ResourceType=typeof(LocalizedText))] - [DataType(DataType.Date)] - [DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}", ApplyFormatInEditMode = true)] - public DateTime EndDate { get; set; } - - - /// - /// Gets or sets the minimal duration. - /// - /// The duration. - [RegularExpression("\\d\\d:\\d\\d")] - [Required(ErrorMessage= "S'il vous plait, saisissez une heure de fin d'intervention")] - [Display(Name="EndHour",ResourceType=typeof(LocalizedText))] - public string EndHour { get; set; } - - /// - /// Gets or sets the person. - /// - /// The name of the user. - [Display(Name="Person",ResourceType=typeof(LocalizedText))] - public string Person { get; set; } - - /// - /// Gets or sets the role. - /// - /// The role. - [Required(ErrorMessage= "S'il vous plait, saisissez le type d'intervention souhaité")] - [Display(Name="Role",ResourceType=typeof(LocalizedText))] - public string Role { get; set; } - } -} - diff --git a/yavscModel/Calendar/ICalendarManager.cs b/yavscModel/Calendar/ICalendarManager.cs index 59baf6e4..e82f762f 100644 --- a/yavscModel/Calendar/ICalendarManager.cs +++ b/yavscModel/Calendar/ICalendarManager.cs @@ -25,8 +25,22 @@ using System.Collections.Generic; namespace Yavsc.Model.Calendar { + /// + /// I calendar manager. + /// public interface ICalendarManager { - IFreeDateSet GetFreeDates(string username, BookQuery req); + /// + /// Gets the free dates. + /// + /// The free dates. + /// Username. + /// Req. + IFreeDateSet GetFreeDates(string username, BookingQuery req); + /// + /// Book the specified username and ev. + /// + /// Username. + /// Ev. bool Book(string username, YaEvent ev); } } diff --git a/yavscModel/Calendar/YaEvent.cs b/yavscModel/Calendar/YaEvent.cs index 41f3b1f1..dd3571c7 100644 --- a/yavscModel/Calendar/YaEvent.cs +++ b/yavscModel/Calendar/YaEvent.cs @@ -26,6 +26,9 @@ using Yavsc.Model; namespace Yavsc.Model.Calendar { + /// + /// Base event. + /// public class BaseEvent { /// diff --git a/yavscModel/ChangeLog b/yavscModel/ChangeLog index 805d95be..707d87fe 100644 --- a/yavscModel/ChangeLog +++ b/yavscModel/ChangeLog @@ -1,235 +1,139 @@ -2015-11-04 Paul Schneider +2015-11-17 Paul Schneider - * LocalizedText.resx: - * LocalizedText.fr.resx: - * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: texts relatives to message or - notification sending + * PerformerProfile.cs: implements a performer profile -2015-11-04 Paul Schneider - - * BlogManager.cs: - * BlogProvider.cs: + * SkillManager.cs: + * SkillProvider.cs: Makes the method rendering skills return a + PerformerProfile object -2015-11-03 Paul Schneider + * YavscModel.csproj: replaces the UserSkillProfile with the + PerformerProfile class - * LocalizedText.resx: - * LocalizedText.fr.resx: - * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: I understood ... + * UserSkillProfile.cs: obsolete - * Notification.cs: a new click action name. +2015-11-17 Paul Schneider -2015-11-01 Paul Schneider + * IRating.cs: + * IComment.cs: + * IIdentified.cs: + * IAuthorized.cs: + * BasePost.cs: + * SkillProvider.cs: refactorization + * Skill.cs: * YavscModel.csproj: - * PostInfoByUser.cs: - * PostInfoByTitle.cs: - * BlogEntryCollection.cs: refactoring - -2015-11-01 Paul Schneider + * UserSkill.cs: + * SkillManager.cs: + * UserSkillRating.cs: + * UserSkillProfile.cs: + * UserSkillReference.cs: + * UserSkillDeclaration.cs: implements the skill data model - * ProfileEdition.cs: Fixes the username modification + * UserSkillComment.cs: defines an user's skill comment - * LocalizedText.resx: - * LocalizedText.fr.resx: - * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: WIP booking + * GDate.cs: + * YaEvent.cs: + * BlogProvider.cs: + * CircleBase.cs: + * Profile.cs: + * CalendarListEntry.cs: + * CalendarEventList.cs: + * UserRole.cs: + * ICalendarManager.cs: + * ProfileEdition.cs: + * LostPasswordModel.cs: + * RegisterClientModel.cs: xmldoc - * ChangePasswordModel.cs: - * RegisterClientModel.cs: A regexp for user name + * LocalizedText.fr.resx: skills - * LoginModel.cs: A regexp for user name and password + * Manager.cs: throws an exception when implementation for the + provider was not found - * Profile.cs: A regexp for user name, and profile usage fixes + * IDataProvider.cs: xml doc - * UserManager.cs: Checks for username availability before - trying to modify it +2015-11-14 Paul Schneider - * YavscModel.csproj: `ProfileEdition` class addition + * IRated.cs: + * BlogHelper.cs: + * ICalendarManager.cs: - * ChangeLog: useless here + * Manager.cs: + * BookingQuery.cs: refactoring - * BookQuery.cs: Start, end hour and role are required + * Manager.cs: + * YavscModel.csproj: + * SkillRating.cs: + * SkillManager.cs: + * SkillRating.cs: + * SkillProvider.cs: + * SkillManager.cs: + * SkillProvider.cs: + * SkillDeclaration.cs: + * SkillDeclaration.cs: VIP skills - * OtherWebException.cs: useless + * BasePost.cs: One may rate a bill -2015-10-31 Paul Schneider + * BlogProvider.cs: The blogs provider takes BlogEntry objects + at Update time * LocalizedText.resx: - * OtherWebException.cs: - * BookQuery.cs: * LocalizedText.fr.resx: * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: + * LocalizedText.fr.Designer.cs: WIP skills -2015-10-30 Paul Schneider + * Profile.cs: prettifies the code, as long as the profile + providers does'nt return any more DBNull values - * LocalizedText.resx: - * LocalizedText.fr.resx: - * BookQuery.cs: - * LocalizedText.Designer.cs: - * Link.cs: - * Note.cs: - * Euro.cs: - * Text.cs: - * Unit.cs: - * LocalizedText.fr.Designer.cs: - * Brand.cs: - * Label.cs: - * Scalar.cs: - * Period.cs: - * Option.cs: - * Product.cs: - * Catalog.cs: - * Service.cs: - * SaleForm.cs: - * Currency.cs: - * CheckBox.cs: - * FormInput.cs: - * TextInput.cs: - * SelectItem.cs: - * FilesInput.cs: - * RadioButton.cs: - * StockStatus.cs: - * SelectInput.cs: - * FormElement.cs: - * ProductImage.cs: - * CatalogHelper.cs: - * CatalogManager.cs: - * CatalogProvider.cs: - * PhysicalProduct.cs: - * ProductCategory.cs: - * CatalogProviderConfigurationElement.cs: - * CatalogProvidersConfigurationSection.cs: - * CatalogProvidersConfigurationCollection.cs: - - * WorkFlowManager.cs: - * IContentProvider.cs: - * Price.cs: - * PriceOnItemCount.cs: refactoring: a dedicated name space for - the catalog - -2015-10-29 Paul Schneider - - * YavscModel.csproj: - * Commande.cs: - * WebFileSystemManager.cs: Refactoring the name of the files - manager class + * BlogManager.cs: Uses the static ManagerHelper class to get + the configuration -2015-10-28 Paul Schneider +2015-11-11 Paul Schneider - * ICalendarManager.cs: WIP booking TODO a calendar provider + * BasePost.cs: all BasePost contains a rate - * YaEvent.cs: - * IFreeDateSet.cs: WIP booking - - * LocalizedText.resx: - * LocalizedText.fr.resx: - * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: implements the message "uses - cookies" + * BasePostInfo.cs: due to base implementation - * YavscModel.csproj: refactoring + * BlogEntry.cs: code formatting - * Notification.cs: The Yavsc otification will start as a - Google one ... - many properties are not yet used, but all seems usefull. + * BlogManager.cs: Fixes the taginfo object delivering -2015-10-27 Paul Schneider + * BlogProvider.cs: refactoring - * YavscModel.csproj: * LocalizedText.resx: - * FreeDate.cs: - * BookQuery.cs: * LocalizedText.fr.resx: * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: + * LocalizedText.fr.Designer.cs: localizes UserInRole -2015-10-23 Paul Schneider + * LoginModel.cs: enables spaces in the legacy login names - * LocalizedText.resx: - * LocalizedText.fr.resx: - * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: Existant DB message + * Profile.cs: gives users the theme choice -2015-10-22 Paul Schneider +2015-11-08 Paul Schneider - * LocalizedText.resx: - * LocalizedText.fr.resx: - * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: Localizes a "bill removal" + * LoginModel.cs: + * UserNameBase.cs: enables legacy login with spaces in the + user name -2015-10-19 Paul Schneider + * ProfileEdition.cs: xml doc - * YavscModel.csproj: - * MarkdownHelper.cs: - -2015-10-17 Paul Schneider - - * BlogManager.cs: - -2015-10-17 Paul Schneider +2015-11-06 Paul Schneider + * ChangeLog: * TagInfo.cs: - * YavscModel.csproj: * BasePost.cs: * LocalizedText.resx: - * LocalizedText.fr.resx: + * BlogEntry.cs: + * BlogManager.cs: * BlogProvider.cs: + * BookQuery.cs: * BasePostInfo.cs: - * PostInfoByUser.cs: - * PostInfoByTitle.cs: - * LocalizedText.Designer.cs: - * LocalizedText.fr.Designer.cs: - * BlogEntryCollection.cs: - -2015-10-17 Paul Schneider - - * PostTag.cs: - * BasePost.cs: - * YavscModel.csproj: - * BlogEntry.cs: - * LocalizedText.resx: - * Automate.cs: * LocalizedText.fr.resx: + * IFreeDateSet.cs: * LocalizedText.Designer.cs: + * Notification.cs: + * LocalizedText.fr.Designer.cs: + * ICalendarManager.cs: * BlogEntryCollection.cs: - * LocalizedText.fr.Designer.cs: - -2015-10-13 Paul Schneider - - * BasePost.cs: refactoring: - allows the "PostActions" user control to use a common base - object as post reference - - * YavscModel.csproj: - * BlogEntry.cs: - * BlogEntryCollection.cs: refactoring - - * BlogManager.cs: Makes the blog manager expose of the new - `UnTag` method - - * BlogProvider.cs: introduces a method to `untag` - - * FindBlogEntryFlags.cs: Find post entry by tag - - * LocalizedText.resx: - * LocalizedText.Designer.cs: new translations: - "Tag" - - "Edit" - - - * LocalizedText.fr.resx: - * LocalizedText.fr.Designer.cs: nouvelles traductions: - "Tag" - - "Edit" - - * Profile.cs: a nicer stack trace at buggy usage - -2015-10-10 Paul Schneider - - * YavscModel.csproj: - * BlogManager.cs: - * UTBlogEntryCollection.cs: - * UUTBlogEntryCollection.cs: - * FileSystemManager.cs: + * ProfileEdition.cs: + * WebFileSystemManager.cs: diff --git a/yavscModel/Circles/CircleBase.cs b/yavscModel/Circles/CircleBase.cs index aba8f55f..1f2175a9 100644 --- a/yavscModel/Circles/CircleBase.cs +++ b/yavscModel/Circles/CircleBase.cs @@ -22,8 +22,14 @@ using System; namespace Yavsc.Model.Circles { + /// + /// Circle base. + /// public class CircleBase { + /// + /// Initializes a new instance of the class. + /// public CircleBase () { } diff --git a/yavscModel/Google/CalendarEventList.cs b/yavscModel/Google/CalendarEventList.cs index 45067363..84f284f9 100644 --- a/yavscModel/Google/CalendarEventList.cs +++ b/yavscModel/Google/CalendarEventList.cs @@ -23,13 +23,22 @@ using System; namespace Yavsc.Model.Google { - + /// + /// Calendar event list. + /// public class CalendarEventList { - + /// + /// The next page token. + /// public string nextPageToken; + /// + /// The next sync token. + /// public string nextSyncToken; - + /// + /// The items. + /// public Resource [] items ; } } diff --git a/yavscModel/Google/CalendarListEntry.cs b/yavscModel/Google/CalendarListEntry.cs index 72e1912b..89265756 100644 --- a/yavscModel/Google/CalendarListEntry.cs +++ b/yavscModel/Google/CalendarListEntry.cs @@ -115,7 +115,9 @@ namespace Yavsc.Model.Google */ } - + /// + /// Reminder. + /// public class Reminder { /// /// Gets or sets the method. diff --git a/yavscModel/Google/GDate.cs b/yavscModel/Google/GDate.cs index 81904ea7..c66fd1be 100644 --- a/yavscModel/Google/GDate.cs +++ b/yavscModel/Google/GDate.cs @@ -23,9 +23,21 @@ using System; namespace Yavsc.Model.Google { + /// + /// G date. + /// public class GDate { + /// + /// The date. + /// public DateTime date; + /// + /// The datetime. + /// public DateTime datetime; + /// + /// The time zone. + /// public string timeZone; } diff --git a/yavscModel/LocalizedText.Designer.cs b/yavscModel/LocalizedText.Designer.cs index 73987ef2..e862efa3 100644 --- a/yavscModel/LocalizedText.Designer.cs +++ b/yavscModel/LocalizedText.Designer.cs @@ -244,6 +244,12 @@ namespace Yavsc.Model { } } + public static string Skills { + get { + return ResourceManager.GetString("Skills", resourceCulture); + } + } + public static string Title { get { return ResourceManager.GetString("Title", resourceCulture); @@ -286,6 +292,12 @@ namespace Yavsc.Model { } } + public static string PhotoUpdated { + get { + return ResourceManager.GetString("PhotoUpdated", resourceCulture); + } + } + public static string Register { get { return ResourceManager.GetString("Register", resourceCulture); @@ -364,6 +376,12 @@ namespace Yavsc.Model { } } + public static string was_added_to_the_role { + get { + return ResourceManager.GetString("was_added_to_the_role", resourceCulture); + } + } + public static string Offline { get { return ResourceManager.GetString("Offline", resourceCulture); @@ -406,9 +424,9 @@ namespace Yavsc.Model { } } - public static string View_source { + public static string Skill { get { - return ResourceManager.GetString("View_source", resourceCulture); + return ResourceManager.GetString("Skill", resourceCulture); } } @@ -436,6 +454,12 @@ namespace Yavsc.Model { } } + public static string View_source { + get { + return ResourceManager.GetString("View_source", resourceCulture); + } + } + public static string EndDate { get { return ResourceManager.GetString("EndDate", resourceCulture); @@ -472,9 +496,9 @@ namespace Yavsc.Model { } } - public static string was_added_to_the_role { + public static string BillUpdated { get { - return ResourceManager.GetString("was_added_to_the_role", resourceCulture); + return ResourceManager.GetString("BillUpdated", resourceCulture); } } @@ -532,6 +556,12 @@ namespace Yavsc.Model { } } + public static string BillCreated { + get { + return ResourceManager.GetString("BillCreated", resourceCulture); + } + } + public static string StartDate { get { return ResourceManager.GetString("StartDate", resourceCulture); @@ -544,6 +574,12 @@ namespace Yavsc.Model { } } + public static string UsersInRole { + get { + return ResourceManager.GetString("UsersInRole", resourceCulture); + } + } + public static string DB { get { return ResourceManager.GetString("DB", resourceCulture); diff --git a/yavscModel/LocalizedText.fr.Designer.cs b/yavscModel/LocalizedText.fr.Designer.cs index 4eea0cad..c393f315 100644 --- a/yavscModel/LocalizedText.fr.Designer.cs +++ b/yavscModel/LocalizedText.fr.Designer.cs @@ -100,12 +100,6 @@ namespace Yavsc.Model { } } - public static string Not_Approuved { - get { - return ResourceManager.GetString("Not Approuved", resourceCulture); - } - } - public static string UserNotInThisRole { get { return ResourceManager.GetString("UserNotInThisRole", resourceCulture); @@ -172,6 +166,12 @@ namespace Yavsc.Model { } } + public static string Remember_me { + get { + return ResourceManager.GetString("Remember_me", resourceCulture); + } + } + public static string access_denied { get { return ResourceManager.GetString("access_denied", resourceCulture); @@ -238,6 +238,12 @@ namespace Yavsc.Model { } } + public static string Skills { + get { + return ResourceManager.GetString("Skills", resourceCulture); + } + } + public static string Title { get { return ResourceManager.GetString("Title", resourceCulture); @@ -352,6 +358,12 @@ namespace Yavsc.Model { } } + public static string was_added_to_the_role { + get { + return ResourceManager.GetString("was_added_to_the_role", resourceCulture); + } + } + public static string Offline { get { return ResourceManager.GetString("Offline", resourceCulture); @@ -394,9 +406,9 @@ namespace Yavsc.Model { } } - public static string View_source { + public static string Skill { get { - return ResourceManager.GetString("View_source", resourceCulture); + return ResourceManager.GetString("Skill", resourceCulture); } } @@ -424,6 +436,12 @@ namespace Yavsc.Model { } } + public static string View_source { + get { + return ResourceManager.GetString("View_source", resourceCulture); + } + } + public static string EndDate { get { return ResourceManager.GetString("EndDate", resourceCulture); @@ -442,9 +460,9 @@ namespace Yavsc.Model { } } - public static string Remember_me { + public static string Not_Approuved { get { - return ResourceManager.GetString("Remember_me", resourceCulture); + return ResourceManager.GetString("Not Approuved", resourceCulture); } } @@ -460,9 +478,9 @@ namespace Yavsc.Model { } } - public static string was_added_to_the_role { + public static string BillUpdated { get { - return ResourceManager.GetString("was_added_to_the_role", resourceCulture); + return ResourceManager.GetString("BillUpdated", resourceCulture); } } @@ -520,6 +538,12 @@ namespace Yavsc.Model { } } + public static string BillCreated { + get { + return ResourceManager.GetString("BillCreated", resourceCulture); + } + } + public static string StartDate { get { return ResourceManager.GetString("StartDate", resourceCulture); @@ -532,6 +556,12 @@ namespace Yavsc.Model { } } + public static string UsersInRole { + get { + return ResourceManager.GetString("UsersInRole", resourceCulture); + } + } + public static string DB { get { return ResourceManager.GetString("DB", resourceCulture); diff --git a/yavscModel/LocalizedText.fr.resx b/yavscModel/LocalizedText.fr.resx index 4806efc9..668e8573 100644 --- a/yavscModel/LocalizedText.fr.resx +++ b/yavscModel/LocalizedText.fr.resx @@ -19,6 +19,8 @@ lui présentant votre demande. Vous devriez être contacté très rapidement. Édition d'un billet Suppression d'un billet + Billet créé + Billet mis à jour Consultant Nombre Chiffre @@ -79,6 +81,8 @@ Se souvenir du mot de passe Supprimer Rôle créé + Compétence + Compétences Date de début La date de fin doit être postérieure à la date de début. Heure de début @@ -90,10 +94,11 @@ Coût unitaire Liste des utilisateurs Nom d'utilisateur + Liste des utilisateurs assumant le rôle "{0}" Le rôle demandé n'est pas assumé par ce préstataire Voir le texte source du billet a été ajouté au rôle - Il n'y avait pas 'utilisateur dans le rôle '{1}'. Vous ({0}) avez été ajouté au rôle '{1}'. + Il n'y avait pas d'utilisateur dans le rôle "{1}". Vous ({0}) avez été ajouté au rôle "{1}". Bienvenue {0} à été notifié de votre demande, vous devriez être contacté rapidement Au vu de son calendrier, diff --git a/yavscModel/LocalizedText.resx b/yavscModel/LocalizedText.resx index 02aa3ca3..bae9ef10 100644 --- a/yavscModel/LocalizedText.resx +++ b/yavscModel/LocalizedText.resx @@ -12,12 +12,16 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + additionally Access denied An instant message has been sent to {0}, showing to him your query. You should be contacted very soon. Bill edition Bill removal + Bill created + Bill updated + Photo updated Create Count Ciffer @@ -81,6 +85,8 @@ Remove Role role created + Skill + Skills Start date Submit Start hour @@ -93,10 +99,11 @@ Unitary_cost User List User name + List of users assuming the role "{0}" This user is not in this role View source was added to the role - There was no user in the '{1}' role. You ({0}) was just added as firt user in the '{1}' role. + There was no user in the "{1}" role. You ({0}) was just added as firt user in the "{1}" role. Welcome {0} has been notified of your query, you should be fast contacted regarding his calendar, diff --git a/yavscModel/RolesAndMembers/LoginModel.cs b/yavscModel/RolesAndMembers/LoginModel.cs index 13f8e76c..5bd401a4 100644 --- a/yavscModel/RolesAndMembers/LoginModel.cs +++ b/yavscModel/RolesAndMembers/LoginModel.cs @@ -14,8 +14,8 @@ namespace Yavsc.Model.RolesAndMembers /// /// The name of the user. [DisplayName("Nom d'utilisateur"), - Required(ErrorMessage = "S'il vous plait, entrez un nom d'utilisateur ([a-z]|[A-Z]|[-_.~]|[0-9])+"), - RegularExpression("([a-z]|[A-Z]|[0-9])+")] + Required(ErrorMessage = "S'il vous plait, entrez un nom d'utilisateur ([a-z]|[A-Z]|[-_.~]|[0-9]|\\s)+"), + RegularExpression("([a-z]|[A-Z]|[0-9]|\\s)+")] public string UserName { get; set; } /// diff --git a/yavscModel/RolesAndMembers/LostPasswordModel.cs b/yavscModel/RolesAndMembers/LostPasswordModel.cs index 70e05136..c98fce04 100644 --- a/yavscModel/RolesAndMembers/LostPasswordModel.cs +++ b/yavscModel/RolesAndMembers/LostPasswordModel.cs @@ -22,9 +22,20 @@ using System; namespace Yavsc.Model.RolesAndMembers { + /// + /// Lost password model. + /// public class LostPasswordModel { + /// + /// Gets or sets the name of the user. + /// + /// The name of the user. public string UserName { get; set; } + /// + /// Gets or sets the email. + /// + /// The email. public string Email { get; set; } } } diff --git a/yavscModel/RolesAndMembers/Profile.cs b/yavscModel/RolesAndMembers/Profile.cs index 44fd02bd..1816cd82 100644 --- a/yavscModel/RolesAndMembers/Profile.cs +++ b/yavscModel/RolesAndMembers/Profile.cs @@ -36,6 +36,13 @@ namespace Yavsc.Model.RolesAndMembers [StringLength (2047)] public string Address { get; set; } + /// + /// Gets or sets the user interface theme. + /// + /// The user interface theme. + [DisplayName ("Thème")] + [StringLength (2047)] + public string UITheme { get; set; } /// /// Gets or sets the state of the city and. /// @@ -241,64 +248,27 @@ namespace Yavsc.Model.RolesAndMembers /// Profile. public Profile (ProfileBase profile) { - if (profile == null) - throw new Exception ("No profile"); - - object b = profile.GetPropertyValue ("BlogVisible"); - BlogVisible = (b == null) ? true : (b is DBNull)? true : (bool)b; - - object s = profile.GetPropertyValue ("BlogTitle"); - BlogTitle = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("Avatar"); - avatar = (s is DBNull) ? null : (string)s; - - - s = profile.GetPropertyValue ("Address"); - Address = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("CityAndState"); - CityAndState = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("Country"); - Country = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("ZipCode"); - ZipCode = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("WebSite"); - WebSite = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("Name"); - Name = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("Phone"); - Phone = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("Mobile"); - Mobile = (s is DBNull) ? null : (string)s; - + if (profile == null) throw new Exception ("No profile"); userName = profile.UserName; - - s = profile.GetPropertyValue ("BankCode"); - BankCode = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("IBAN"); - IBAN = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("BIC"); - BIC = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("WicketCode"); - WicketCode = (s is DBNull) ? null : (string)s; - - s = profile.GetPropertyValue ("AccountNumber"); - AccountNumber = (s is DBNull) ? null : (string)s; - - object o = profile.GetPropertyValue ("BankedKey"); - BankedKey = (int)0; - s = profile.GetPropertyValue ("gcalid"); - GoogleCalendar = (s is DBNull)? null : (string) s; + UITheme = (string) profile.GetPropertyValue ("UITheme"); + BlogVisible = (bool) profile.GetPropertyValue ("BlogVisible"); + BlogTitle = (string) profile.GetPropertyValue ("BlogTitle"); + avatar = (string) profile.GetPropertyValue ("Avatar"); + Address = (string) profile.GetPropertyValue ("Address"); + CityAndState = (string) profile.GetPropertyValue ("CityAndState"); + Country = (string) profile.GetPropertyValue ("Country"); + ZipCode = (string) profile.GetPropertyValue ("ZipCode"); + WebSite = (string) profile.GetPropertyValue ("WebSite"); + Name = (string) profile.GetPropertyValue ("Name"); + Phone = (string) profile.GetPropertyValue ("Phone"); + Mobile = (string) profile.GetPropertyValue ("Mobile"); + BankCode = (string)profile.GetPropertyValue ("BankCode"); + IBAN = (string)profile.GetPropertyValue ("IBAN"); + BIC = (string)profile.GetPropertyValue ("BIC"); + WicketCode = (string) profile.GetPropertyValue ("WicketCode"); + AccountNumber = (string) profile.GetPropertyValue ("AccountNumber"); + BankedKey = (int) profile.GetPropertyValue ("BankedKey");; + GoogleCalendar = (string) profile.GetPropertyValue ("gcalid"); } } } diff --git a/yavscModel/RolesAndMembers/ProfileEdition.cs b/yavscModel/RolesAndMembers/ProfileEdition.cs index 3f45bb1b..d21ec033 100644 --- a/yavscModel/RolesAndMembers/ProfileEdition.cs +++ b/yavscModel/RolesAndMembers/ProfileEdition.cs @@ -25,8 +25,14 @@ using System.Web.Profile; namespace Yavsc.Model.RolesAndMembers { + /// + /// Profile edition. + /// public class ProfileEdition : Profile { + /// + /// Initializes a new instance of the class. + /// public ProfileEdition() {} @@ -36,6 +42,10 @@ namespace Yavsc.Model.RolesAndMembers NewUserName = UserName; } + /// + /// Gets or sets the new name of the user. + /// + /// The new name of the user. [Localizable(true), Required(ErrorMessage = "S'il vous plait, entrez un nom d'utilisateur valide") ,Display(ResourceType=typeof(LocalizedText),Name="User_name"), RegularExpression("([a-z]|[A-Z]|[\\s-_.~]|[0-9])+") diff --git a/yavscModel/RolesAndMembers/RegisterClientModel.cs b/yavscModel/RolesAndMembers/RegisterClientModel.cs index b36ea829..50b2b437 100644 --- a/yavscModel/RolesAndMembers/RegisterClientModel.cs +++ b/yavscModel/RolesAndMembers/RegisterClientModel.cs @@ -69,8 +69,16 @@ namespace Yavsc.Model.RolesAndMembers [DisplayName("Téléphone mobile")] public string Mobile { get; set; } + /// + /// Gets or sets the question. + /// + /// The question. public string Question { get; set; } + /// + /// Gets or sets the answer. + /// + /// The answer. public string Answer { get; set; } } } diff --git a/yavscModel/RolesAndMembers/RegisterModel.cs b/yavscModel/RolesAndMembers/RegisterModel.cs index de2bbd77..b8feb6c8 100644 --- a/yavscModel/RolesAndMembers/RegisterModel.cs +++ b/yavscModel/RolesAndMembers/RegisterModel.cs @@ -25,19 +25,11 @@ using System.ComponentModel.DataAnnotations; namespace Yavsc.Model.RolesAndMembers { - /// /// Register view model. /// - public class RegisterModel + public class RegisterModel: UserNameBase { - /// - /// Gets or sets the name of the user. - /// - /// The name of the user. - [Localizable(true), Required(ErrorMessage = "S'il vous plait, entrez un nom d'utilisateur") - ,Display(ResourceType=typeof(LocalizedText),Name="User_name")] - public string UserName { get; set; } /// /// Gets or sets the password. diff --git a/yavscModel/WorkFlow/IDataProvider.cs b/yavscModel/WorkFlow/IDataProvider.cs index d86e8361..84dc0bbd 100644 --- a/yavscModel/WorkFlow/IDataProvider.cs +++ b/yavscModel/WorkFlow/IDataProvider.cs @@ -4,9 +4,20 @@ using Yavsc.Model.FrontOffice; namespace Yavsc.Model.WorkFlow { + /// + /// I data provider. + /// public interface IDataProvider { + /// + /// Get the specified id. + /// + /// Identifier. T Get (long id); + /// + /// Update the specified data. + /// + /// Data. void Update (T data); } diff --git a/yavscModel/YavscModel.csproj b/yavscModel/YavscModel.csproj index 5780f8f9..04986a3e 100644 --- a/yavscModel/YavscModel.csproj +++ b/yavscModel/YavscModel.csproj @@ -62,7 +62,6 @@ - LocalizedText.fr.resx @@ -172,12 +171,29 @@ - + + + + + + + + + + + + + + + + + + @@ -193,6 +209,7 @@ + diff --git a/yavscclient/ChangeLog b/yavscclient/ChangeLog index 9090fcd6..f9032943 100644 --- a/yavscclient/ChangeLog +++ b/yavscclient/ChangeLog @@ -1,14 +1,4 @@ -2015-10-30 Paul Schneider +2015-11-06 Paul Schneider - * MyClass.cs: refactoring: a dedicated name space for the - catalog - -2015-07-15 Paul Schneider - - * YavscClient.csproj: Moves to Mono framework - -2015-06-09 Paul Schneider - - * YavscClient.csproj: Helps to fix packaging, and cleans - dependencies + * ChangeLog: