├── src ├── TechStacks │ ├── TechStacks │ │ ├── partials │ │ │ ├── partial1.html │ │ │ ├── partial2.html │ │ │ ├── stacks │ │ │ │ ├── popular.html │ │ │ │ ├── latest.html │ │ │ │ ├── stack.html │ │ │ │ ├── create.html │ │ │ │ └── edit.html │ │ │ ├── tech │ │ │ │ ├── latest.html │ │ │ │ ├── create.html │ │ │ │ ├── tech.html │ │ │ │ └── edit.html │ │ │ ├── user │ │ │ │ └── feed.html │ │ │ ├── favorites │ │ │ │ └── favorites.html │ │ │ └── home.html │ │ ├── Global.asax │ │ ├── img │ │ │ ├── app │ │ │ │ ├── bg.png │ │ │ │ ├── brand.png │ │ │ │ ├── logo-76.png │ │ │ │ ├── tech-75.png │ │ │ │ ├── heart-75.png │ │ │ │ ├── stacks-75.png │ │ │ │ ├── tech-white-75.png │ │ │ │ ├── logo-220-inverted.png │ │ │ │ └── stacks-white-75.png │ │ │ ├── favicon.png │ │ │ ├── logo-280.png │ │ │ ├── java-logo.png │ │ │ ├── kotlin-logo.png │ │ │ ├── share │ │ │ │ ├── Email.png │ │ │ │ ├── Google.png │ │ │ │ ├── Pocket.png │ │ │ │ ├── Reddit.png │ │ │ │ ├── Tumblr.png │ │ │ │ ├── Twitter.png │ │ │ │ ├── Facebook.png │ │ │ │ ├── LinkedIn.png │ │ │ │ ├── Pinboard.png │ │ │ │ ├── Pinterest.png │ │ │ │ └── Wordpress.png │ │ │ ├── swift-logo.png │ │ │ ├── github_normal.png │ │ │ ├── no-profile64.png │ │ │ ├── appstore-badge.png │ │ │ ├── en_app_rgb_wo_60.png │ │ │ ├── facebook_normal.png │ │ │ ├── twitter_normal.png │ │ │ └── sign-in-with-twitter-gray.png │ │ ├── SQLite.Interop.dll │ │ ├── tests │ │ │ ├── unit │ │ │ │ ├── testModules.js │ │ │ │ └── techControllerSpec.js │ │ │ └── karma.conf.js │ │ ├── js │ │ │ ├── stacks │ │ │ │ ├── filters.js │ │ │ │ └── services.js │ │ │ ├── navigation │ │ │ │ └── controllers.js │ │ │ ├── home │ │ │ │ └── controllers.js │ │ │ ├── user │ │ │ │ └── controllers.js │ │ │ ├── components │ │ │ │ └── chosen │ │ │ │ │ └── directives.js │ │ │ ├── favorites │ │ │ │ └── controllers.js │ │ │ └── tech │ │ │ │ └── services.js │ │ ├── Scripts │ │ │ └── _references.js │ │ ├── bower.json │ │ ├── Global.asax.cs │ │ ├── Views │ │ │ ├── Tech │ │ │ │ ├── AllTech.cshtml │ │ │ │ └── Tech.cshtml │ │ │ ├── Stacks │ │ │ │ ├── AllStacks.cshtml │ │ │ │ └── Stack.cshtml │ │ │ ├── User │ │ │ │ └── User.cshtml │ │ │ ├── Shared │ │ │ │ └── ServerHtml.cshtml │ │ │ └── Home.cshtml │ │ ├── package.json │ │ ├── Properties │ │ │ ├── PublishProfiles │ │ │ │ └── CustomProfile.pubxml │ │ │ └── AssemblyInfo.cs │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ ├── Web.config │ │ └── default.html │ ├── package-lock.json │ ├── TechStacks.ServiceModel │ │ ├── SessionInfo.cs │ │ ├── LogoUrlApproval.cs │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── Types │ │ │ ├── PageStats.cs │ │ │ ├── UserFavorites.cs │ │ │ └── TechnologyStack.cs │ │ ├── Lock.cs │ │ ├── AccountTechStacks.cs │ │ ├── TechStacks.ServiceModel.csproj │ │ ├── UserFavorites.cs │ │ ├── Technologies.cs.orig │ │ └── Technologies.cs │ ├── TechStacks.ServiceInterface │ │ ├── AppFeatureFlags.cs │ │ ├── app.config │ │ ├── SessionInfoServices.cs │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── Validations │ │ │ ├── TechStackValidator.cs │ │ │ └── TechValidators.cs │ │ ├── TechStackQueries.cs │ │ ├── Filters │ │ │ ├── TechStackFilters.cs │ │ │ └── TechFilters.cs │ │ ├── Admin │ │ │ └── AdminServices.cs │ │ ├── TechStacks.ServiceInterface.csproj │ │ ├── CustomAuthUserSession.cs │ │ ├── UserStackServices.cs │ │ ├── TechExtensions.cs │ │ ├── TechnologyServices.cs │ │ ├── ClientRoutesService.cs │ │ ├── TwitterUpdates.cs │ │ ├── UserFavoriteServices.cs │ │ └── TechnologyServicesAdmin.cs │ └── TechStacks.Tests │ │ ├── Properties │ │ └── AssemblyInfo.cs │ │ ├── app.config │ │ ├── UnitTests.cs │ │ ├── TechStacks.Tests.csproj │ │ ├── TestHosts.cs │ │ ├── Seeds.cs │ │ └── AdminTasks.cs ├── NuGet.Config └── TechStacks.sln ├── license.txt └── .gitignore /src/TechStacks/TechStacks/partials/partial1.html: -------------------------------------------------------------------------------- 1 |

This is the partial for view 1.

-------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/partial2.html: -------------------------------------------------------------------------------- 1 |

This is the partial for view 2.

-------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="TechStacks.Global" Language="C#" %> 2 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/bg.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/favicon.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/logo-280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/logo-280.png -------------------------------------------------------------------------------- /src/TechStacks/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TechStacks", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/SQLite.Interop.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/SQLite.Interop.dll -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/brand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/brand.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/logo-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/logo-76.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/tech-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/tech-75.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/java-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/java-logo.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/kotlin-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/kotlin-logo.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Email.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/swift-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/swift-logo.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/heart-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/heart-75.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/stacks-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/stacks-75.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/github_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/github_normal.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/no-profile64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/no-profile64.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Google.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Pocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Pocket.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Reddit.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Tumblr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Tumblr.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Twitter.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/appstore-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/appstore-badge.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/en_app_rgb_wo_60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/en_app_rgb_wo_60.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/facebook_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/facebook_normal.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Facebook.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/LinkedIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/LinkedIn.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Pinboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Pinboard.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Pinterest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Pinterest.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/share/Wordpress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/share/Wordpress.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/twitter_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/twitter_normal.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/tech-white-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/tech-white-75.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/logo-220-inverted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/logo-220-inverted.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/app/stacks-white-75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/app/stacks-white-75.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/stacks/popular.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
-------------------------------------------------------------------------------- /src/TechStacks/TechStacks/img/sign-in-with-twitter-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ServiceStackApps/TechStacks/HEAD/src/TechStacks/TechStacks/img/sign-in-with-twitter-gray.png -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/SessionInfo.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | 3 | namespace TechStacks.ServiceModel 4 | { 5 | [Route("/my-session")] 6 | public class SessionInfo {} 7 | } 8 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/tests/unit/testModules.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | /*global angular*/ 4 | var testMod = angular.module('testMod', ['techs.controllers', 'tech.services']); 5 | testMod.controller('testConfigController', ['$scope', function ($scope) { 6 | 7 | }]); 8 | })(); -------------------------------------------------------------------------------- /src/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/AppFeatureFlags.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack.Configuration; 2 | 3 | namespace TechStacks.ServiceInterface 4 | { 5 | public static class AppFeatureFlags 6 | { 7 | public static bool EnableTwitterUpdates(this IAppSettings appSettings) 8 | { 9 | return appSettings.Get("EnableTwitterUpdates", true); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/stacks/filters.js: -------------------------------------------------------------------------------- 1 | /* global angular */ 2 | (function () { 3 | "use strict"; 4 | var app = angular.module('stacks.filters', []); 5 | 6 | app.filter('stack', function() { 7 | return function (stack, tier) { 8 | if (stack) { 9 | var tiers = stack.Tiers; 10 | return tiers.indexOf(tier) !== -1; 11 | } 12 | return false; 13 | }; 14 | }); 15 | })(); 16 | 17 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/SessionInfoServices.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using TechStacks.ServiceModel; 3 | 4 | namespace TechStacks.ServiceInterface 5 | { 6 | [Authenticate] 7 | public class SessionInfoServices : Service 8 | { 9 | public object Any(SessionInfo request) 10 | { 11 | var sessionClone = SessionAs().CreateCopy(); 12 | sessionClone.ProviderOAuthAccess = null; 13 | return sessionClone; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/LogoUrlApproval.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using TechStacks.ServiceModel.Types; 3 | 4 | namespace TechStacks.ServiceModel 5 | { 6 | [Route("/admin/technology/{TechnologyId}/logo")] 7 | public class LogoUrlApproval : IReturn 8 | { 9 | public long TechnologyId { get; set; } 10 | public bool Approved { get; set; } 11 | } 12 | 13 | public class LogoUrlApprovalResponse 14 | { 15 | public Technology Result { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Setting ComVisible to false makes the types in this assembly not visible 6 | // to COM components. If you need to access a type in this assembly from 7 | // COM, set the ComVisible attribute to true on that type. 8 | [assembly: ComVisible(false)] 9 | 10 | // The following GUID is for the ID of the typelib if this project is exposed to COM 11 | [assembly: Guid("84ccc30e-d756-4716-a37e-f0a311af1e43")] 12 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Setting ComVisible to false makes the types in this assembly not visible 6 | // to COM components. If you need to access a type in this assembly from 7 | // COM, set the ComVisible attribute to true on that type. 8 | [assembly: ComVisible(false)] 9 | 10 | // The following GUID is for the ID of the typelib if this project is exposed to COM 11 | [assembly: Guid("eafa19af-a515-4a4c-a1ab-5d25e5e9de63")] 12 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Setting ComVisible to false makes the types in this assembly not visible 6 | // to COM components. If you need to access a type in this assembly from 7 | // COM, set the ComVisible attribute to true on that type. 8 | [assembly: ComVisible(false)] 9 | 10 | // The following GUID is for the ID of the typelib if this project is exposed to COM 11 | [assembly: Guid("e648c4b4-ca56-4e49-925d-24f962cfd4d2")] 12 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/Types/PageStats.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TechStacks.ServiceModel.Types 4 | { 5 | public class PageStats 6 | { 7 | public string Id { get; set; } 8 | 9 | public long ViewCount { get; set; } 10 | 11 | public long FavCount { get; set; } 12 | 13 | public long RefId { get; set; } 14 | 15 | public string RefType { get; set; } 16 | 17 | public string RefSlug { get; set; } 18 | 19 | public DateTime LastModified { get; set; } 20 | } 21 | 22 | public interface IRegisterStats 23 | { 24 | string GetStatsId(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Scripts/_references.js: -------------------------------------------------------------------------------- 1 | //Add JS/CSS references here to enable intellisense from external packages. 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/Validations/TechStackValidator.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.FluentValidation; 3 | using TechStacks.ServiceModel; 4 | 5 | namespace TechStacks.ServiceInterface.Validations 6 | { 7 | public class TechStackValidator : AbstractValidator 8 | { 9 | public TechStackValidator() 10 | { 11 | RuleSet(ApplyTo.Post, () => 12 | { 13 | RuleFor(x => x.Name).NotEmpty(); 14 | //http://stackoverflow.com/a/3831442/670151 15 | RuleFor(x => x.Name).Matches("(?!^\\d+$)^.+$"); 16 | }); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/Lock.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | 3 | namespace TechStacks.ServiceModel 4 | { 5 | [Route("/admin/techstacks/{TechnologyStackId}/lock")] 6 | public class LockTechStack : IReturn 7 | { 8 | public long TechnologyStackId { get; set; } 9 | public bool IsLocked { get; set; } 10 | } 11 | 12 | public class LockStackResponse {} 13 | 14 | [Route("/admin/technology/{TechnologyId}/lock")] 15 | public class LockTech : IReturn 16 | { 17 | public long TechnologyId { get; set; } 18 | public bool IsLocked { get; set; } 19 | } 20 | 21 | public class LockTechResponse {} 22 | } 23 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TechStack", 3 | "version": "0.0.1", 4 | "authors": [ 5 | "test" 6 | ], 7 | "license": "MIT", 8 | "private": true, 9 | "dependencies": { 10 | "angular": "~1.2.23", 11 | "angular-route": "1.2.x", 12 | "angular-loader": "1.2.x", 13 | "angular-mocks": "~1.2.x", 14 | "bootstrap": "~3.2.0", 15 | "modernizr": "~2.8.3", 16 | "jquery": "~1.11.1", 17 | "angular-bootstrap": "~0.11.2", 18 | "jquery-ui-bootstrap": "~0.2.5", 19 | "chosen": "https://github.com/harvesthq/chosen/releases/download/v1.1.0/chosen_v1.1.0.zip", 20 | "roboto-fontface": "~0.2.7" 21 | }, 22 | "ignore": [ 23 | "**/.*", 24 | "node_modules", 25 | "bower_components", 26 | "test", 27 | "tests" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Web; 6 | using System.Web.Security; 7 | using System.Web.SessionState; 8 | using ServiceStack; 9 | using ServiceStack.Configuration; 10 | using TechStacks; 11 | 12 | namespace TechStacks 13 | { 14 | public class Global : System.Web.HttpApplication 15 | { 16 | protected void Application_Start(object sender, EventArgs e) 17 | { 18 | var customSettings = new FileInfo(@"~/appsettings.license.txt".MapHostAbsolutePath()); 19 | var debugSettings = new FileInfo(@"~/../wwwroot_build/deploy/appsettings.license.txt".MapAbsolutePath()); 20 | AppHost.Load(); 21 | new AppHost().Init(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/Types/UserFavorites.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ServiceStack.DataAnnotations; 7 | 8 | namespace TechStacks.ServiceModel.Types 9 | { 10 | public class UserFavoriteTechnologyStack 11 | { 12 | [AutoIncrement] 13 | public int Id { get; set; } 14 | 15 | public string UserId { get; set; } 16 | 17 | [References(typeof(TechnologyStack))] 18 | public int TechnologyStackId { get; set; } 19 | } 20 | 21 | public class UserFavoriteTechnology 22 | { 23 | [AutoIncrement] 24 | public int Id { get; set; } 25 | 26 | public string UserId { get; set; } 27 | 28 | [References(typeof(Technology))] 29 | public int TechnologyId { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Views/Tech/AllTech.cshtml: -------------------------------------------------------------------------------- 1 | @inherits ViewPage 2 | @{ 3 | Layout = "ServerHtml"; 4 | ViewBag.Tab = "tech"; 5 | } 6 | 7 |
8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | @foreach (var tech in Model.Results) 21 | { 22 | 23 | 24 | 27 | 28 | 29 | } 30 |
11 | Technologies 12 |
NameDescriptionVendor
@tech.Name 25 |
@tech.Description
26 |
@tech.VendorName
31 | 32 |
33 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/AccountTechStacks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ServiceStack; 4 | using TechStacks.ServiceModel.Types; 5 | 6 | namespace TechStacks.ServiceModel 7 | { 8 | [Route("/my-feed")] 9 | public class GetUserFeed {} 10 | 11 | public class GetUserFeedResponse 12 | { 13 | public List Results { get; set; } 14 | } 15 | 16 | [Route("/userinfo/{UserName}")] 17 | public class GetUserInfo : IReturn 18 | { 19 | public string UserName { get; set; } 20 | } 21 | 22 | public class GetUserInfoResponse 23 | { 24 | public string UserName { get; set; } 25 | public DateTime Created { get; set; } 26 | public string AvatarUrl { get; set; } 27 | public List TechStacks { get; set; } 28 | public List FavoriteTechStacks { get; set; } 29 | public List FavoriteTechnologies { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Views/Stacks/AllStacks.cshtml: -------------------------------------------------------------------------------- 1 | @inherits ViewPage 2 | @{ 3 | Layout = "ServerHtml"; 4 | ViewBag.Tab = "stacks"; 5 | } 6 | 7 |
8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | @foreach (var techStack in Model.Results) 20 | { 21 | 22 | 23 | 24 | 25 | 26 | } 27 |
10 | TechStacks 11 |
NameDescriptionCreated By
@techStack.Name
@techStack.Description
28 |
-------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/TechStacks.ServiceModel.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472 4 | ..\..\ 5 | default 6 | TechStacks.ServiceModel 7 | TechStacks.ServiceModel 8 | Copyright © 2014 9 | bin\$(Configuration)\ 10 | 11 | 12 | full 13 | 14 | 15 | pdbonly 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TechStack", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "tests/karma.conf.js", 6 | "author": "", 7 | "license": "", 8 | "scripts": { 9 | "postinstall": "bower install" 10 | }, 11 | "devDependencies": { 12 | "bower": "~1.8.14", 13 | "grunt": "^0.4.5", 14 | "grunt-gulp": "~0.1.0", 15 | "grunt-karma": "~0.8.3", 16 | "grunt-msbuild": "^1.1.1", 17 | "grunt-nuget": "~0.1.3", 18 | "gulp": "~3.8.7", 19 | "gulp-grunt": "~0.5.2", 20 | "gulp-if": "~1.2.4", 21 | "gulp-minify-css": "~0.3.8", 22 | "gulp-newer": "~0.3.0", 23 | "gulp-replace": "~0.4.0", 24 | "gulp-rimraf": "~0.1.0", 25 | "gulp-uglify": "~1.0.1", 26 | "gulp-useref": "~1.0.1", 27 | "karma": "~0.12", 28 | "karma-chrome-launcher": "~0.1.4", 29 | "karma-jasmine": "~0.2.0", 30 | "karma-junit-reporter": "^0.2.2", 31 | "karma-phantomjs-launcher": "~0.1.4", 32 | "requirejs": "~2.1.15", 33 | "ssvs-utils": "~0.1.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.Tests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/navigation/controllers.js: -------------------------------------------------------------------------------- 1 | /* global angular */ 2 | (function () { 3 | "use strict"; 4 | var app = angular.module('navigation.controllers', []); 5 | 6 | app.controller('navigationCtrl', [ 7 | '$scope', '$location', '$http', 'userService', 8 | function ($scope, $location, $http, userService) { 9 | /** 10 | * @return {boolean} 11 | */ 12 | $scope.IsRouteActive = function(routePath) { 13 | return routePath === $location.path(); 14 | }; 15 | 16 | $scope.isFavoriteTechStack = function(techStack) { 17 | var isFav = false; 18 | for (var i = 0; i < $scope.favoriteTechStacks.length; i++) { 19 | if ($scope.favoriteTechStacks[i].Id == techStack.Id) { 20 | isFav = true; 21 | break; 22 | } 23 | } 24 | return isFav; 25 | }; 26 | 27 | userService.isAuthenticated().then(function (session) { 28 | userService.getFavoriteTechStacks(); 29 | userService.getFavoriteTechs(); 30 | }, function(error) { 31 | 32 | }); 33 | } 34 | ]); 35 | })(); 36 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/Validations/TechValidators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ServiceStack; 7 | using ServiceStack.FluentValidation; 8 | using TechStacks.ServiceModel; 9 | 10 | namespace TechStacks.ServiceInterface.Validations 11 | { 12 | public class CreateTechValidator : AbstractValidator 13 | { 14 | public CreateTechValidator() 15 | { 16 | RuleSet(ApplyTo.Post, () => 17 | { 18 | RuleFor(x => x.Name).NotEmpty(); 19 | //http://stackoverflow.com/a/3831442/670151 20 | RuleFor(x => x.Name).Matches("(?!^\\d+$)^.+$"); 21 | }); 22 | } 23 | } 24 | 25 | public class UpdateTechValidator : AbstractValidator 26 | { 27 | public UpdateTechValidator() 28 | { 29 | RuleSet(ApplyTo.Put, () => 30 | { 31 | RuleFor(x => x.Id).GreaterThan(0); 32 | RuleFor(x => x.Name).NotEmpty(); 33 | //http://stackoverflow.com/a/3831442/670151 34 | RuleFor(x => x.Name).Matches("(?!^\\d+$)^.+$"); 35 | }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Properties/PublishProfiles/CustomProfile.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | MSDeploy 9 | Release 10 | Any CPU 11 | http://angular.techstacks.io 12 | True 13 | False 14 | awstest.servicestack.net 15 | TechStacks 16 | 17 | True 18 | WMSVC 19 | True 20 | deploy 21 | <_SavePWD>True 22 | 23 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TechStacks")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TechStacks")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b63da288-def6-4753-bbaa-3bc12a8bde55")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/TechStackQueries.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using System.Linq; 4 | using ServiceStack; 5 | using ServiceStack.OrmLite; 6 | using TechStacks.ServiceModel; 7 | using TechStacks.ServiceModel.Types; 8 | 9 | namespace TechStacks.ServiceInterface 10 | { 11 | public static class TechStackQueries 12 | { 13 | public static List GetTechstackDetails(this IDbConnection db, SqlExpression stackQuery) 14 | { 15 | //distinct 16 | var latestStacks = db.Select(stackQuery) 17 | .GroupBy(x => x.Id) 18 | .Select(x => x.First()) 19 | .ToList(); 20 | 21 | if (latestStacks.Count == 0) 22 | return new List(); 23 | 24 | var technologyChoices = 25 | db.LoadSelect(db.From() 26 | .Join() 27 | .Join() 28 | .Where(techChoice => 29 | Sql.In(techChoice.TechnologyStackId, latestStacks.Select(x => x.Id)) 30 | )); 31 | 32 | var stackDetails = latestStacks.Map(x => x.ConvertTo()); 33 | stackDetails.ForEach(stack => stack.TechnologyChoices = technologyChoices 34 | .Map(x => x.ToTechnologyInStack()) 35 | .Where(x => stack.Id == x.TechnologyStackId) 36 | .ToList()); 37 | 38 | return stackDetails; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2013, Demis Bellot, ServiceStack. 2 | https://servicestack.net 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the ServiceStack nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/stacks/latest.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 9 | 10 |
11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
14 |
15 | + 16 | + 17 |
18 | 19 | 20 | TechStacks 21 |
NameDescriptionCreated By
{{techStack.Name}}
{{techStack.Description}}
35 | 36 | Add TechStack 37 |
-------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Views/Tech/Tech.cshtml: -------------------------------------------------------------------------------- 1 | @inherits ViewPage 2 | @{ 3 | Layout = "ServerHtml"; 4 | ViewBag.Tab = "tech"; 5 | var tech = Model.Technology; 6 | var relatedStacks = Model.TechnologyStacks; 7 | } 8 | 9 |
10 | 11 |
12 | 13 | 14 |

15 | @tech.Name 16 |

17 | 18 |

19 | @tech.ProductUrl 20 | 21 | by 22 | @tech.VendorName 23 | 24 |

25 | 26 |

@tech.Description

27 |
28 |
29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | @foreach (var techStack in relatedStacks) 40 | { 41 | 42 | 47 | 50 | 51 | } 52 |
TechStacks using @tech.Name
NameDescription
43 | 44 | @techStack.Name 45 | 46 | 48 |
@techStack.Description
49 |
53 |
54 |
55 | 56 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 36 | 37 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/Filters/TechStackFilters.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.Data; 3 | using ServiceStack.OrmLite; 4 | using ServiceStack.Web; 5 | using TechStacks.ServiceModel; 6 | using TechStacks.ServiceModel.Types; 7 | 8 | namespace TechStacks.ServiceInterface.Filters 9 | { 10 | public class TechStackFilters 11 | { 12 | public static void FilterCreateTechStackRequest(IRequest req, IResponse res, CreateTechnologyStack dto) 13 | { 14 | var dbFactory = req.TryResolve(); 15 | 16 | if (req.Verb == "POST") 17 | { 18 | using (var db = dbFactory.OpenDbConnection()) 19 | { 20 | //Check unqiue name 21 | var exists = db.Single(x => x.Name.ToLower() == dto.Name.ToLower()); 22 | 23 | if (exists != null) 24 | { 25 | throw HttpError.Conflict("A TechnologyStack with that name already exists"); 26 | } 27 | } 28 | } 29 | } 30 | 31 | public static void FilterUpdateTechStackRequest(IRequest req, IResponse res, UpdateTechnologyStack dto) 32 | { 33 | var dbFactory = req.TryResolve(); 34 | if (req.Verb == "PUT") 35 | { 36 | using (var db = dbFactory.OpenDbConnection()) 37 | { 38 | //Check unqiue name 39 | var exists = db.Single(x => x.Name.ToLower() == dto.Name.ToLower()); 40 | if (exists != null && exists.Id != dto.Id) 41 | { 42 | throw HttpError.Conflict("A TechnologyStack with that name already exists"); 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/Admin/AdminServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using ServiceStack; 8 | using ServiceStack.Configuration; 9 | using ServiceStack.OrmLite; 10 | using TechStacks.ServiceModel; 11 | using TechStacks.ServiceModel.Types; 12 | 13 | namespace TechStacks.ServiceInterface.Admin 14 | { 15 | [Authenticate] 16 | [RequiredRole("Admin")] 17 | public class AdminServices : Service 18 | { 19 | public object Put(LogoUrlApproval request) 20 | { 21 | var tech = Db.SingleById(request.TechnologyId); 22 | if (tech == null) 23 | { 24 | throw HttpError.NotFound("Technology not found"); 25 | } 26 | tech.LogoApproved = request.Approved; 27 | Db.Save(tech); 28 | return new LogoUrlApprovalResponse 29 | { 30 | Result = tech 31 | }; 32 | } 33 | 34 | public object Put(LockTechStack request) 35 | { 36 | var techStack = Db.SingleById(request.TechnologyStackId); 37 | if (techStack == null) 38 | { 39 | throw HttpError.NotFound("TechnologyStack not found"); 40 | } 41 | 42 | techStack.IsLocked = request.IsLocked; 43 | Db.Save(techStack); 44 | return new LockStackResponse(); 45 | } 46 | 47 | public object Put(LockTech request) 48 | { 49 | var tech = Db.SingleById(request.TechnologyId); 50 | if (tech == null) 51 | { 52 | throw HttpError.NotFound("Technology not found"); 53 | } 54 | 55 | tech.IsLocked = request.IsLocked; 56 | Db.Save(tech); 57 | return new LockTechResponse(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/Filters/TechFilters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using ServiceStack; 7 | using ServiceStack.Data; 8 | using ServiceStack.OrmLite; 9 | using ServiceStack.Web; 10 | using TechStacks.ServiceModel; 11 | using TechStacks.ServiceModel.Types; 12 | 13 | namespace TechStacks.ServiceInterface.Filters 14 | { 15 | public class TechFilters 16 | { 17 | public static void FilterCreateTechRequest(IRequest req, IResponse res, CreateTechnology dto) 18 | { 19 | var dbFactory = req.TryResolve(); 20 | 21 | if (req.Verb == "POST") 22 | { 23 | using (var db = dbFactory.OpenDbConnection()) 24 | { 25 | //Check unqiue name 26 | var exists = db.Single(x => x.Name.ToLower() == dto.Name.ToLower()); 27 | if (exists != null) 28 | { 29 | throw HttpError.Conflict("A Technology with that name already exists"); 30 | } 31 | } 32 | } 33 | } 34 | 35 | public static void FilterUpdateTechRequest(IRequest req, IResponse res, UpdateTechnology dto) 36 | { 37 | var dbFactory = req.TryResolve(); 38 | 39 | if (req.Verb == "PUT") 40 | { 41 | using (var db = dbFactory.OpenDbConnection()) 42 | { 43 | //Check unqiue name 44 | var exists = db.Single(x => x.Name.ToLower() == dto.Name.ToLower()); 45 | if (exists != null && exists.Id != dto.Id) 46 | { 47 | throw HttpError.Conflict("A Technology with that name already exists"); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/tests/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | 4 | // base path that will be used to resolve all patterns (eg. files, exclude) 5 | basePath: '', 6 | 7 | 8 | // frameworks to use 9 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 10 | frameworks: ['jasmine'], 11 | 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | '../bower_components/angular/angular.js', 16 | '../bower_components/angular-route/angular-route.js', 17 | '../bower_components/angular-mocks/angular-mocks.js', 18 | '../js/**/*.js', 19 | 'unit/**/*.js' 20 | ], 21 | 22 | 23 | // list of files to exclude 24 | exclude: [ 25 | 'karma.conf.js' 26 | ], 27 | 28 | 29 | // preprocess matching files before serving them to the browser 30 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 31 | preprocessors: { 32 | }, 33 | 34 | 35 | // test results reporter to use 36 | // possible values: 'dots', 'progress' 37 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 38 | reporters: ['progress'], 39 | 40 | 41 | // web server port 42 | port: 9876, 43 | 44 | 45 | // enable / disable colors in the output (reporters and logs) 46 | colors: true, 47 | 48 | 49 | // level of logging 50 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 51 | logLevel: config.LOG_INFO, 52 | 53 | 54 | // enable / disable watching file and executing tests whenever any file changes 55 | autoWatch: true, 56 | 57 | 58 | // start these browsers 59 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 60 | browsers: ['PhantomJS'], 61 | 62 | 63 | // Continuous Integration mode 64 | // if true, Karma captures browsers, runs the tests and exits 65 | singleRun: false 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/tech/latest.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 38 | 44 | 45 |
5 |
6 | + 7 | + 8 |
9 | 10 | 11 |
12 | 18 |
19 | 20 | Technologies 21 |
NameDescriptionVendor
31 | 32 |
{{tech.Name}}
33 |
34 |
36 |
{{tech.Description}}
37 |
39 | 42 |
{{tech.VendorName}}
43 |
46 | Add technology 47 |
48 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/TechStacks.ServiceInterface.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472 4 | default 5 | TechStacks.ServiceInterface 6 | TechStacks.ServiceInterface 7 | Copyright © 2014 8 | bin\$(Configuration)\ 9 | 10 | 11 | full 12 | 13 | 14 | pdbonly 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/stacks/services.js: -------------------------------------------------------------------------------- 1 | /* global angular */ 2 | (function () { 3 | "use strict"; 4 | var app = angular.module('stacks.services', ['tech.services']); 5 | 6 | app.service('techStackServices', ['$http', '$q', 'techServices', 7 | function ($http, $q, techServices) { 8 | 9 | var getResults = techServices.getResults; 10 | 11 | return { 12 | createStack: function (newStack) { 13 | return getResults($http.post('/techstacks', newStack)); 14 | }, 15 | getStack: function (id) { 16 | return getResults($http.get('/techstacks/' + id)); 17 | }, 18 | getStackPreviousVersions: function (id) { 19 | return getResults($http.get('/techstacks/' + id + '/previous-versions')); 20 | }, 21 | getTechStackFavorites: function (id) { 22 | return getResults($http.get('/techstacks/' + id + '/favorites')); 23 | }, 24 | allTechs: function () { 25 | return getResults($http.get('/technology/search')); 26 | }, 27 | updateStack: function (techStack) { 28 | return getResults($http.put('/techstacks/' + techStack.Id, techStack)); 29 | }, 30 | deleteTechStack: function (techStack) { 31 | return getResults($http.delete('/techstacks/' + techStack.Id)); 32 | }, 33 | updateLockStatus: function(techStackId, isLocked) { 34 | return getResults($http.put('/admin/techstacks/' + techStackId + '/lock', { IsLocked: isLocked })); 35 | }, 36 | searchStacks: function (searchQuery) { 37 | return getResults($http.get('/techstacks/search?orderBy=-LastModified&NameContains=' + searchQuery + "&DescriptionContains=" + searchQuery)); 38 | }, 39 | getPageStats: function (id) { 40 | return getResults($http.get('/pagestats/stack/' + id)); 41 | }, 42 | popularTechStacks: techServices.popularTechStacks, 43 | overview: techServices.overview, 44 | searchTech: techServices.searchTech, 45 | allTiers: techServices.allTiers 46 | }; 47 | }]); 48 | })(); 49 | 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | .idea/ 5 | .vs/ 6 | node_modules/ 7 | bower_components/ 8 | 9 | # mstest test results 10 | TestResults 11 | 12 | ## Ignore Visual Studio temporary files, build results, and 13 | ## files generated by popular Visual Studio add-ons. 14 | 15 | # User-specific files 16 | *.suo 17 | *.user 18 | *.sln.docstates 19 | *.secrets.cs 20 | 21 | # Build results 22 | [Dd]ebug/ 23 | [Rr]elease/ 24 | deploy/ 25 | x64/ 26 | *_i.c 27 | *_p.c 28 | *.ilk 29 | *.meta 30 | *.obj 31 | *.pch 32 | *.pdb 33 | *.pgc 34 | *.pgd 35 | *.rsp 36 | *.sbr 37 | *.tlb 38 | *.tli 39 | *.tlh 40 | *.tmp 41 | *.log 42 | *.txt 43 | *.vspscc 44 | *.vssscc 45 | .builds 46 | 47 | # Visual C++ cache files 48 | ipch/ 49 | *.aps 50 | *.ncb 51 | *.opensdf 52 | *.sdf 53 | 54 | # Visual Studio profiler 55 | *.psess 56 | *.vsp 57 | *.vspx 58 | 59 | # Guidance Automation Toolkit 60 | *.gpState 61 | 62 | # ReSharper is a .NET coding add-in 63 | _ReSharper* 64 | 65 | # NCrunch 66 | *.ncrunch* 67 | .*crunch*.local.xml 68 | 69 | # Installshield output folder 70 | [Ee]xpress 71 | 72 | # DocProject is a documentation generator add-in 73 | DocProject/buildhelp/ 74 | DocProject/Help/*.HxT 75 | DocProject/Help/*.HxC 76 | DocProject/Help/*.hhc 77 | DocProject/Help/*.hhk 78 | DocProject/Help/*.hhp 79 | DocProject/Help/Html2 80 | DocProject/Help/html 81 | 82 | # Click-Once directory 83 | publish 84 | 85 | # Publish Web Output 86 | *.Publish.xml 87 | 88 | # NuGet Packages Directory 89 | packages 90 | 91 | # Windows Azure Build Output 92 | csx 93 | *.build.csdef 94 | 95 | # Windows Store app package directory 96 | AppPackages/ 97 | 98 | # Others 99 | [Bb]in 100 | [Oo]bj 101 | sql 102 | *.Cache 103 | ClientBin 104 | [Ss]tyle[Cc]op.* 105 | ~$* 106 | *.dbmdl 107 | 108 | Generated_Code #added for RIA/Silverlight projects 109 | 110 | # Backup & report files from converting an old project file to a newer 111 | # Visual Studio version. Backup files are not needed, because we have git ;-) 112 | _UpgradeReport_Files/ 113 | Backup*/ 114 | UpgradeLog*.XML 115 | 116 | ssl/ 117 | *.crt 118 | *.ssl 119 | *.pem 120 | results/ 121 | teststub.* 122 | *.sqlite 123 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/home/controllers.js: -------------------------------------------------------------------------------- 1 | /* global angular */ 2 | (function () { 3 | "use strict"; 4 | var app = angular.module('home.controllers', ['stacks.services']); 5 | 6 | app.controller('homeCtrl', [ 7 | '$rootScope', '$scope', '$http', 'techStackServices', 8 | function ($rootScope, $scope, $http, techStackServices) { 9 | 10 | if ($rootScope.cachedTechStacks) { 11 | $scope.techStacks = $rootScope.cachedTechStacks; 12 | $scope.topTechnologies = $rootScope.cachedTopTechnologies; 13 | $scope.topUsers = $rootScope.cachedTopUsers; 14 | $scope.topTechCategories = $rootScope.cachedTopTechCategories; 15 | $scope.popularTechStacks = $rootScope.cachedPopularTechStacks; 16 | } 17 | 18 | function refresh() { 19 | techStackServices.overview().then(function (overview) { 20 | $scope.techStacks = overview.LatestTechStacks; 21 | $scope.topTechnologies = overview.TopTechnologies; 22 | $scope.topUsers = overview.TopUsers; 23 | $scope.topTechCategories = []; 24 | $scope.popularTechStacks = overview.PopularTechStacks; 25 | 26 | techStackServices.allTiers().then(function(allTiers) { 27 | $.map(allTiers, function (tier) { 28 | $scope.topTechCategories.push({ 29 | name: tier.name, 30 | title: tier.title, 31 | techs: overview.TopTechnologiesByTier[tier.name] 32 | }); 33 | }); 34 | }); 35 | 36 | $rootScope.cachedTechStacks = $scope.techStacks; 37 | $rootScope.cachedTopTechnologies = $scope.topTechnologies; 38 | $rootScope.cachedTopUsers = $scope.topUsers; 39 | $rootScope.cachedTopTechCategories = $scope.topTechCategories; 40 | $rootScope.cachedPopularTechStacks = $scope.popularTechStacks; 41 | }); 42 | } 43 | 44 | refresh(); 45 | } 46 | ]); 47 | })(); 48 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Views/Stacks/Stack.cshtml: -------------------------------------------------------------------------------- 1 | @inherits ViewPage 2 | @{ 3 | Layout = "ServerHtml"; 4 | ViewBag.Tab = "stacks"; 5 | var currentStack = Model.Result; 6 | var allTiers = CachedTechnologyStackServices.GetAllTiers(); 7 | } 8 | 9 |
10 |
11 | 12 | 13 | 14 |
@currentStack.AppUrl
15 |
16 | 17 |

18 | @currentStack.Name 19 |

20 | 21 |

22 | @currentStack.VendorName 23 |

24 | 25 |

@currentStack.Description

26 | 27 | @foreach (var tier in allTiers) 28 | { 29 | var techs = currentStack.TechnologyChoices.Where(x => x.Tier == tier.Value).ToList(); 30 | if (techs.Count > 0) 31 | { 32 |
33 |
34 |

@tier.Title

35 | @foreach (var tech in techs) 36 | { 37 |
38 | 39 | 40 | 41 |
42 | } 43 |
44 |
45 | } 46 | } 47 | 48 |
49 | 50 |
51 |

52 | last updated: @currentStack.LastModified.ToString("yyyy/MM/dd") 53 | by @currentStack.LastModifiedBy 54 |

55 |
56 |
57 | 58 | @if (currentStack.DetailsHtml != null) 59 | { 60 |
61 |
62 |
63 |
@currentStack.DetailsHtml.AsRaw()
64 |
65 |
66 |
67 | } 68 | 69 |
70 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/user/controllers.js: -------------------------------------------------------------------------------- 1 | /* global angular */ 2 | (function () { 3 | "use strict"; 4 | var app = angular.module('user.controllers', []); 5 | 6 | app.controller('userFeedCtrl', [ 7 | '$rootScope', '$scope', '$routeParams', '$location', 'userService', 'techStackServices', 8 | function ($rootScope, $scope, $routeParams, $location, userService, techStackServices) { 9 | $scope.currentUserName = $routeParams.userName; 10 | 11 | //load last page with opacity to increase perceived perf 12 | if ($rootScope.cachedAvatarUrl) { 13 | $scope.loading = true; 14 | $scope.avatarUrl = $rootScope.cachedAvatarUrl; 15 | $scope.techStacks = $rootScope.cachedTechStacks; 16 | $scope.favoriteTechStacks = $rootScope.cachedFavoriteTechStacks; 17 | $scope.favoriteTechnologies = $rootScope.cachedFavoriteTechnologies; 18 | } 19 | 20 | userService.getUserInfo($routeParams.userName).then(function (response) { 21 | var r = response.data; 22 | $scope.avatarUrl = r.AvatarUrl; 23 | $scope.techStacks = r.TechStacks; 24 | $scope.favoriteTechStacks = r.FavoriteTechStacks; 25 | $scope.favoriteTechnologies = r.FavoriteTechnologies; 26 | 27 | //cache last data 28 | $rootScope.cachedAvatarUrl = $scope.avatarUrl; 29 | $rootScope.cachedTechStacks = $scope.techStacks; 30 | $rootScope.cachedFavoriteTechStacks = $scope.favoriteTechStacks; 31 | $rootScope.cachedFavoriteTechnologies = $scope.favoriteTechnologies; 32 | $scope.loading = false; 33 | }); 34 | 35 | $scope.deleteStack = function(selectedStack) { 36 | techStackServices.deleteTechStack(selectedStack).success(function () { 37 | for (var i = 0; i < $scope.techStacks.length; i++) { 38 | var techStack = $scope.techStacks[i]; 39 | if (techStack.Id === selectedStack.Id) { 40 | $scope.techStacks.splice(i, 1); 41 | break; 42 | } 43 | } 44 | }); 45 | }; 46 | } 47 | ]); 48 | })(); 49 | 50 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/CustomAuthUserSession.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ServiceStack; 3 | using ServiceStack.Auth; 4 | using ServiceStack.Configuration; 5 | using ServiceStack.Data; 6 | using ServiceStack.OrmLite; 7 | 8 | namespace TechStacks.ServiceInterface 9 | { 10 | public class CustomUserSession : AuthUserSession 11 | { 12 | public string GithubProfileUrl { get; set; } 13 | public string TwitterProfileUrl { get; set; } 14 | 15 | public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary authInfo) 16 | { 17 | base.OnAuthenticated(authService, session, tokens, authInfo); 18 | var appSettings = authService.TryResolve(); 19 | var userAuthRepo = authService.TryResolve(); 20 | var userAuth = userAuthRepo.GetUserAuth(session, tokens); 21 | var dbConnectionFactory = authService.TryResolve(); 22 | foreach (var authTokens in session.ProviderOAuthAccess) 23 | { 24 | if (authTokens.Provider.ToLower() == "github") 25 | { 26 | GithubProfileUrl = session.GetProfileUrl(); 27 | } 28 | if (authTokens.Provider.ToLower() == "twitter") 29 | { 30 | TwitterProfileUrl = session.GetProfileUrl(); 31 | if (appSettings.GetList("TwitterAdmins").Contains(session.UserName) && !session.HasRole(RoleNames.Admin, userAuthRepo)) 32 | { 33 | userAuthRepo.AssignRoles(userAuth, roles: new[] { RoleNames.Admin }); 34 | } 35 | } 36 | 37 | ProfileUrl = GithubProfileUrl ?? TwitterProfileUrl; 38 | using (var db = dbConnectionFactory.OpenDbConnection()) 39 | { 40 | var userAuthInstance = db.Single(x => x.Id == this.UserAuthId.ToInt()); 41 | if (userAuthInstance != null) 42 | { 43 | userAuthInstance.DefaultProfileUrl = this.ProfileUrl; 44 | db.Save(userAuthInstance); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | public class CustomUserAuth : UserAuth 52 | { 53 | public string DefaultProfileUrl { get; set; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.Tests/UnitTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | using ServiceStack; 6 | using ServiceStack.Data; 7 | using ServiceStack.OrmLite; 8 | using ServiceStack.Testing; 9 | using ServiceStack.Text; 10 | using TechStacks.ServiceInterface; 11 | using TechStacks.ServiceModel; 12 | using TechStacks.ServiceModel.Types; 13 | 14 | namespace TechStacks.Tests 15 | { 16 | [TestFixture] 17 | public class UnitTests 18 | { 19 | private ServiceStackHost appHost; 20 | 21 | [OneTimeSetUp] 22 | public void Init() 23 | { 24 | appHost = new UnitTestHost(); 25 | var debugSettings = new FileInfo(@"~/../../../TechStacks/wwwroot_build/deploy/appsettings.license.txt".MapAbsolutePath()); 26 | Licensing.RegisterLicenseFromFileIfExists(debugSettings.FullName); 27 | appHost.Init(); 28 | } 29 | 30 | [OneTimeTearDown] 31 | public void TestFixtureTearDown() 32 | { 33 | appHost.Dispose(); 34 | } 35 | 36 | [SetUp] 37 | public void Setup() 38 | { 39 | var dbFactory = appHost.Resolve(); 40 | using (var db = dbFactory.OpenDbConnection()) 41 | { 42 | db.DropAndCreateTable(); 43 | db.DropAndCreateTable(); 44 | db.DropAndCreateTable(); 45 | db.DropAndCreateTable(); 46 | db.DropAndCreateTable(); 47 | } 48 | 49 | SeedTestHost(); 50 | } 51 | 52 | [Test] 53 | public void Can_Get_Stacks() 54 | { 55 | var service = appHost.Resolve(); 56 | var response = (GetAllTechnologyStacksResponse)service.Get(new GetAllTechnologyStacks()); 57 | var dbFactory = appHost.Resolve(); 58 | using (var db = dbFactory.OpenDbConnection()) 59 | { 60 | var allStacks = db.Select().ToList(); 61 | Assert.That(allStacks.Count, Is.EqualTo(response.Results.Count)); 62 | } 63 | } 64 | 65 | private void SeedTestHost() 66 | { 67 | Seeds.SeedApp(appHost.Resolve()); 68 | } 69 | 70 | [Test] 71 | public void Generate_AuthKey() 72 | { 73 | Convert.ToBase64String(AesUtils.CreateKey()).Print(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/TechStacks.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechStacks", "TechStacks\TechStacks\TechStacks.csproj", "{6F86FC59-9DE7-4BF5-8D11-CB7E99DA29FC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechStacks.ServiceModel", "TechStacks\TechStacks.ServiceModel\TechStacks.ServiceModel.csproj", "{35D650BD-8598-4ABB-806E-79D6E57D228B}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechStacks.ServiceInterface", "TechStacks\TechStacks.ServiceInterface\TechStacks.ServiceInterface.csproj", "{8321EC19-4800-4E5F-8E56-A11D531022A8}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechStacks.Tests", "TechStacks\TechStacks.Tests\TechStacks.Tests.csproj", "{90B6A6F3-49B8-4804-B27B-BCA97B7990CA}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {6F86FC59-9DE7-4BF5-8D11-CB7E99DA29FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {6F86FC59-9DE7-4BF5-8D11-CB7E99DA29FC}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {6F86FC59-9DE7-4BF5-8D11-CB7E99DA29FC}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {6F86FC59-9DE7-4BF5-8D11-CB7E99DA29FC}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {35D650BD-8598-4ABB-806E-79D6E57D228B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {35D650BD-8598-4ABB-806E-79D6E57D228B}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {35D650BD-8598-4ABB-806E-79D6E57D228B}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {35D650BD-8598-4ABB-806E-79D6E57D228B}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {8321EC19-4800-4E5F-8E56-A11D531022A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {8321EC19-4800-4E5F-8E56-A11D531022A8}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {8321EC19-4800-4E5F-8E56-A11D531022A8}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {8321EC19-4800-4E5F-8E56-A11D531022A8}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {90B6A6F3-49B8-4804-B27B-BCA97B7990CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {90B6A6F3-49B8-4804-B27B-BCA97B7990CA}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {90B6A6F3-49B8-4804-B27B-BCA97B7990CA}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {90B6A6F3-49B8-4804-B27B-BCA97B7990CA}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/tech/create.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | Add a new Technology 6 |
7 |
8 | 9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 | 41 |
42 | 45 |
46 |
47 |
48 | 49 |
50 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/UserFavorites.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ServiceStack; 3 | using TechStacks.ServiceModel.Types; 4 | 5 | namespace TechStacks.ServiceModel 6 | { 7 | [Route("/favorites/techtacks", Verbs = "GET")] 8 | public class GetFavoriteTechStack : IReturn 9 | { 10 | public int TechnologyStackId { get; set; } 11 | } 12 | public class GetFavoriteTechStackResponse 13 | { 14 | public List Results { get; set; } 15 | } 16 | 17 | [Route("/favorites/techtacks/{TechnologyStackId}", Verbs = "PUT")] 18 | public class AddFavoriteTechStack : IReturn 19 | { 20 | public int TechnologyStackId { get; set; } 21 | } 22 | 23 | [Route("/favorites/techtacks/{TechnologyStackId}", Verbs = "DELETE")] 24 | public class RemoveFavoriteTechStack : IReturn 25 | { 26 | public int TechnologyStackId { get; set; } 27 | } 28 | 29 | public class FavoriteTechStackResponse 30 | { 31 | public TechnologyStack Result { get; set; } 32 | } 33 | 34 | 35 | [Route("/favorites/technology", Verbs = "GET")] 36 | public class GetFavoriteTechnologies : IReturn 37 | { 38 | public int TechnologyId { get; set; } 39 | } 40 | public class GetFavoriteTechnologiesResponse 41 | { 42 | public List Results { get; set; } 43 | } 44 | 45 | [Route("/favorites/technology/{TechnologyId}", Verbs = "PUT")] 46 | public class AddFavoriteTechnology : IReturn 47 | { 48 | public int TechnologyId { get; set; } 49 | } 50 | [Route("/favorites/technology/{TechnologyId}", Verbs = "DELETE")] 51 | public class RemoveFavoriteTechnology : IReturn 52 | { 53 | public int TechnologyId { get; set; } 54 | } 55 | 56 | public class FavoriteTechnologyResponse 57 | { 58 | public Technology Result { get; set; } 59 | } 60 | 61 | [Route("/pagestats/{Type}/{Slug}")] 62 | public class GetPageStats : IReturn 63 | { 64 | public string Type { get; set; } 65 | public string Slug { get; set; } 66 | } 67 | 68 | public class GetPageStatsResponse 69 | { 70 | public string Type { get; set; } 71 | public string Slug { get; set; } 72 | public long ViewCount { get; set; } 73 | public long FavCount { get; set; } 74 | } 75 | 76 | [Restrict(VisibleInternalOnly = true)] 77 | [Route("/tasks/hourly")] 78 | public class HourlyTask 79 | { 80 | public bool Force { get; set; } 81 | } 82 | 83 | public class HourlyTaskResponse : IMeta 84 | { 85 | public Dictionary Meta { get; set; } 86 | 87 | public ResponseStatus ResponseStatus { get; set; } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/stacks/stack.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
{{currentStack.AppUrl}}
7 |
8 | 9 | 11 | Edit 12 | 13 | 14 |

15 | {{stats.ViewCount}} views, {{stats.FavCount}} faves 16 |

17 | 18 |

19 | {{currentStack.Name}} 20 | 22 | 24 |

25 | 26 |

27 | {{currentStack.VendorName}} 28 |

29 | 30 |

31 | 32 |
33 |
34 |

{{tier.title}}

35 |
36 | 37 | 38 | 39 |
40 |
41 |
42 | 43 |
44 | 45 |
46 |

47 | last updated at {{currentStack.LastModified | date: "yyyy/MM/dd"}} 48 | by {{currentStack.LastModifiedBy}} 49 | created by {{currentStack.CreatedBy}} 50 |

51 |
52 |
53 | 54 |
55 | 56 |
57 |
58 |
59 |
60 |
61 | 62 |
63 | 64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/tech/tech.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 |

7 | {{stats.ViewCount}} views, {{stats.FavCount}} faves 8 |

9 |
10 | 11 | 13 | Edit 14 | 15 | 16 |

{{tech.Name}} 17 | 19 | 21 |

22 | 23 |

{{tech.Name}} 24 | 26 | 28 |

29 | 30 |

31 | {{tech.ProductUrl}} 32 | 33 | by 34 | {{tech.VendorName}} 35 | 36 |

37 | 38 |

{{tech.Description}}

39 |
40 |
41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 61 |
TechStacks using {{tech.Name}}
NameDescription
53 | 54 | {{techStack.Name}} 55 | 56 | 58 |
{{techStack.Description}}
59 |
62 |
63 |
64 | 65 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/UserStackServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using ServiceStack; 5 | using ServiceStack.OrmLite; 6 | using TechStacks.ServiceModel; 7 | using TechStacks.ServiceModel.Types; 8 | 9 | namespace TechStacks.ServiceInterface 10 | { 11 | public class UserStackServices : Service 12 | { 13 | [Authenticate] 14 | public object Any(GetUserFeed request) 15 | { 16 | var session = SessionAs(); 17 | 18 | var favTechs = Db.Select(x => x.UserId == session.UserAuthId); 19 | 20 | var userFeed = favTechs.Count == 0 21 | ? GetDefaultFeed() 22 | : GetDefaultFeed(favTechs.Select(x => x.TechnologyId).ToList()); 23 | 24 | return new GetUserFeedResponse 25 | { 26 | Results = userFeed 27 | }; 28 | } 29 | 30 | private List GetDefaultFeed(List favTechIds = null) 31 | { 32 | var q = Db.From().OrderByDescending(x => x.Id).Limit(20); 33 | 34 | if (favTechIds != null) 35 | { 36 | q.Join((ts, tsc) => 37 | ts.Id == tsc.TechnologyStackId && Sql.In(tsc.TechnologyId, favTechIds)); 38 | } 39 | 40 | return Db.GetTechstackDetails(q); 41 | } 42 | } 43 | 44 | [CacheResponse(Duration = 3600)] 45 | public class CachedUserStackServices : Service 46 | { 47 | public object Any(GetUserInfo request) 48 | { 49 | var user = Db.Single(x => x.UserName == request.UserName); 50 | if (user == null) 51 | throw HttpError.NotFound("User not found"); 52 | 53 | var techStacks = Db.Select(Db.From() 54 | .Where(x => x.CreatedBy == request.UserName) 55 | .OrderByDescending(x => x.Id)); 56 | 57 | var favStacks = Db.Select( 58 | Db.From() 59 | .Join() 60 | .Where(u => u.UserId == user.Id.ToString())); 61 | 62 | favStacks.Each(x => x.Details = null); //lighten payload 63 | 64 | var favTechs = Db.Select( 65 | Db.From() 66 | .Join() 67 | .Where(u => u.UserId == user.Id.ToString())); 68 | 69 | return new GetUserInfoResponse 70 | { 71 | Created = DateTime.UtcNow, 72 | UserName = user.UserName, 73 | AvatarUrl = user.DefaultProfileUrl ?? "/img/no-profile64.png", 74 | TechStacks = techStacks, 75 | FavoriteTechStacks = favStacks, 76 | FavoriteTechnologies = favTechs, 77 | }; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Views/User/User.cshtml: -------------------------------------------------------------------------------- 1 | @inherits ViewPage 2 | @{ 3 | Layout = "ServerHtml"; 4 | var avatarUrl = Model.AvatarUrl; 5 | var currentUserName = Model.UserName; 6 | } 7 | 8 |
9 |
10 |
11 | 12 |
13 |

@currentUserName

14 |
15 |
16 | @if (!Model.TechStacks.IsEmpty()) 17 | { 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @foreach (var techStack in Model.TechStacks) 29 | { 30 | 31 | 32 | 33 | 34 | } 35 |
20 | TechStacks 21 |
NameDescription
@techStack.Name@techStack.Description
36 | } 37 | 38 | @if (!Model.FavoriteTechStacks.IsEmpty()) 39 | { 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | @foreach (var techStack in Model.FavoriteTechStacks) 51 | { 52 | 53 | 54 | 55 | 56 | } 57 |
42 | Favorite TechStacks 43 |
NameDescription
@techStack.Name
@techStack.Description
58 | } 59 | 60 | @if (!Model.FavoriteTechnologies.IsEmpty()) 61 | { 62 | 63 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | @foreach (var tech in Model.FavoriteTechnologies) 73 | { 74 | 75 | 76 | 77 | 78 | } 79 |
64 | Favorite Technologies 65 |
NameDescription
@tech.Name
@tech.Description
80 | } 81 | 82 |
83 |
-------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/TechExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using System.Threading.Tasks; 4 | using ServiceStack; 5 | using ServiceStack.Data; 6 | using ServiceStack.OrmLite; 7 | using TechStacks.ServiceModel; 8 | using TechStacks.ServiceModel.Types; 9 | 10 | namespace TechStacks.ServiceInterface 11 | { 12 | public static class TechExtensions 13 | { 14 | public static TechnologyInStack ToTechnologyInStack(this TechnologyChoice technologyChoice) 15 | { 16 | var result = technologyChoice.ConvertTo(); 17 | result.PopulateWith(technologyChoice.Technology); 18 | result.Id = technologyChoice.Id; 19 | return result; 20 | } 21 | 22 | /// 23 | /// From http://stackoverflow.com/a/2921135/670151 24 | /// 25 | /// 26 | /// 27 | public static string GenerateSlug(this string phrase) 28 | { 29 | string str = phrase.RemoveAccent().ToLower() 30 | .Replace("#", "sharp") // c#, f# => csharp, fsharp 31 | .Replace("+", "p"); // c++ => cpp 32 | 33 | // invalid chars 34 | str = Regex.Replace(str, @"[^a-z0-9\s-]", "-"); 35 | // convert multiple spaces into one space 36 | str = Regex.Replace(str, @"\s+", " ").Trim(); 37 | // cut and trim 38 | str = str.Substring(0, str.Length <= 45 ? str.Length : 45).Trim(); 39 | str = Regex.Replace(str, @"\s", "-"); // hyphens 40 | return str; 41 | } 42 | 43 | public static string RemoveAccent(this string txt) 44 | { 45 | byte[] bytes = System.Text.Encoding.GetEncoding("Cyrillic").GetBytes(txt); 46 | return System.Text.Encoding.ASCII.GetString(bytes); 47 | } 48 | 49 | public static Task RegisterPageView(this IDbConnectionFactory dbFactory, string id) 50 | { 51 | var db = dbFactory.Open(); 52 | 53 | return db.ExecuteSqlAsync("UPDATE page_stats SET view_count = view_count + 1 WHERE id = @id", new { id }) 54 | .ContinueWith(t => 55 | { 56 | var parts = id.Substring(1).SplitOnFirst('/'); 57 | if (t.Result == 0 && parts.Length == 2) 58 | { 59 | var type = parts[0]; 60 | var slug = parts[1]; 61 | 62 | return db.InsertAsync(new PageStats 63 | { 64 | Id = id, 65 | RefType = type, 66 | RefSlug = slug, 67 | ViewCount = 1, 68 | LastModified = DateTime.UtcNow, 69 | }) 70 | .ContinueWith(t2 => (int)t2.Result); 71 | } 72 | 73 | return t; 74 | }).ContinueWith(_ => db.Dispose()); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/Technologies.cs.orig: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.Serialization; 3 | using ServiceStack; 4 | using TechStacks.ServiceModel.Types; 5 | 6 | namespace TechStacks.ServiceModel 7 | { 8 | <<<<<<< HEAD 9 | [Route("/technology/{Id}", Verbs = "GET")] 10 | ======= 11 | [Route("/technology/{Slug}")] 12 | >>>>>>> 33e62cf1daa54c76cb40cec44e22ea99660e8b7c 13 | public class Technologies : IReturn 14 | { 15 | public string Slug { get; set; } 16 | 17 | [IgnoreDataMember] 18 | public long Id 19 | { 20 | set { this.Slug = value.ToString(); } 21 | } 22 | } 23 | 24 | [Route("/technology", Verbs = "POST")] 25 | public class CreateTechnology : IReturn 26 | { 27 | public string Name { get; set; } 28 | public string VendorName { get; set; } 29 | public string VendorUrl { get; set; } 30 | public string ProductUrl { get; set; } 31 | public string LogoUrl { get; set; } 32 | public string Description { get; set; } 33 | 34 | public TechnologyTier Tier { get; set; } 35 | } 36 | 37 | public class CreateTechnologyResponse 38 | { 39 | public Technology Tech { get; set; } 40 | } 41 | 42 | [Route("/technology/{Id}", Verbs = "PUT")] 43 | public class UpdateTechnology : IReturn 44 | { 45 | public long Id { get; set; } 46 | 47 | public string Name { get; set; } 48 | public string VendorName { get; set; } 49 | public string VendorUrl { get; set; } 50 | public string ProductUrl { get; set; } 51 | public string LogoUrl { get; set; } 52 | public string Description { get; set; } 53 | 54 | public TechnologyTier Tier { get; set; } 55 | } 56 | 57 | public class UpdateTechnologyResponse 58 | { 59 | public Technology Tech { get; set; } 60 | } 61 | 62 | [Route("/technology/{Id}", Verbs = "DELETE")] 63 | public class DeleteTechnology : IReturn 64 | { 65 | public long Id { get; set; } 66 | } 67 | 68 | public class DeleteTechnologyResponse 69 | { 70 | public Technology Tech { get; set; } 71 | } 72 | 73 | [Query(QueryTerm.Or)] 74 | [Route("/technology/search")] 75 | public class FindTechnologies : QueryBase {} 76 | 77 | [Route("/technology/{Id}/techstacks")] 78 | public class GetStacksThatUseTech 79 | { 80 | public long Id { get; set; } 81 | } 82 | 83 | public class GetStacksThatUseTechResponse 84 | { 85 | public List TechStacks { get; set; } 86 | } 87 | 88 | [Route("/technology", Verbs = "GET")] 89 | public class AllTechnologies : IReturn 90 | { 91 | 92 | } 93 | 94 | public class AllTechnologiesResponse 95 | { 96 | public List Techs { get; set; } 97 | } 98 | 99 | public class TechnologiesResponse 100 | { 101 | public Technology Tech { get; set; } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/components/chosen/directives.js: -------------------------------------------------------------------------------- 1 | /* global angular,$ */ 2 | (function () { 3 | "use strict"; 4 | 5 | var app = angular.module('chosen', []); 6 | 7 | app.directive('chosenSelect', [ 8 | '$timeout', '$q', function($timeout, $q) { 9 | return { 10 | restrict: 'E', 11 | template: '', 12 | scope: { 13 | data: '=', 14 | itemValue: '@', 15 | itemName: '@', 16 | options: '=', 17 | selectedValues: '=', 18 | onSelection: '&' 19 | }, 20 | replace: true, 21 | link: function(scope, element) { 22 | var dataDeferred = $q.defer(); 23 | 24 | //One off bind 25 | var initWatch = scope.$watch('data', function() { 26 | if (scope.data != null) { 27 | $timeout(function() { 28 | 29 | $(element).chosen(scope.options); 30 | $(element).chosen().change(function(event, item) { 31 | if (!scope.controlReady) { 32 | return; 33 | } 34 | 35 | if (item.selected) { 36 | scope.selectedValues.push(parseInt(item.selected)); 37 | } 38 | if (item.deselected) { 39 | var existingPos = scope.selectedValues.indexOf(parseInt(item.deselected)); 40 | if (existingPos >= 0) { 41 | scope.selectedValues.splice(existingPos, 1); 42 | } 43 | } 44 | 45 | setValues(scope.selectedValues); 46 | }); 47 | initWatch(); 48 | dataDeferred.resolve(); 49 | }); 50 | } 51 | 52 | }); 53 | 54 | function setValues(selectedValues) { 55 | $(element).chosen().val(selectedValues); 56 | $(element).chosen().trigger("chosen:updated"); 57 | } 58 | 59 | dataDeferred.promise.then(function () { 60 | setValues(scope.selectedValues); 61 | scope.controlReady = true; 62 | }); 63 | 64 | scope.$watch('selectedValues', function() { 65 | $timeout(function () { 66 | if (scope.controlReady) { 67 | setValues(scope.selectedValues); 68 | } 69 | }); 70 | }); 71 | } 72 | }; 73 | } 74 | ]); 75 | 76 | })(); -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/TechnologyServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using ServiceStack; 4 | using ServiceStack.Data; 5 | using ServiceStack.OrmLite; 6 | using TechStacks.ServiceModel; 7 | using TechStacks.ServiceModel.Types; 8 | 9 | namespace TechStacks.ServiceInterface 10 | { 11 | public class TechnologyServices : Service 12 | { 13 | public object Get(GetTechnologyPreviousVersions request) 14 | { 15 | if (request.Slug == null) 16 | throw new ArgumentNullException("Slug"); 17 | 18 | long id; 19 | if (!long.TryParse(request.Slug, out id)) 20 | { 21 | var tech = Db.Single(x => x.Slug == request.Slug.ToLower()); 22 | id = tech.Id; 23 | } 24 | 25 | return new GetTechnologyPreviousVersionsResponse 26 | { 27 | Results = Db.Select(Db.From() 28 | .Where(x => x.TechnologyId == id) 29 | .OrderByDescending(x => x.LastModified)) 30 | }; 31 | } 32 | 33 | public object Get(GetAllTechnologies request) 34 | { 35 | return new GetAllTechnologiesResponse 36 | { 37 | Results = Db.Select(Db.From().Take(100)).ToList() 38 | }; 39 | } 40 | } 41 | 42 | [CacheResponse(Duration = 3600)] 43 | public class CachedTechnologyServices : Service 44 | { 45 | public IAutoQueryDb AutoQuery { get; set; } 46 | 47 | public object Any(FindTechnologies request) 48 | { 49 | var q = AutoQuery.CreateQuery(request, Request.GetRequestParams()); 50 | return AutoQuery.Execute(request, q); 51 | } 52 | 53 | public object Get(GetTechnology request) 54 | { 55 | int id; 56 | var tech = int.TryParse(request.Slug, out id) 57 | ? Db.SingleById(id) 58 | : Db.Single(x => x.Slug == request.Slug.ToLower()); 59 | 60 | if (tech == null) 61 | throw HttpError.NotFound("Tech stack not found"); 62 | 63 | var techStacks = Db.Select(Db.From() 64 | .Join() 65 | .Join() 66 | .Where(x => x.TechnologyId == tech.Id) 67 | .OrderByDescending(x => x.LastModified)); 68 | 69 | return new GetTechnologyResponse 70 | { 71 | Technology = tech, 72 | TechnologyStacks = techStacks 73 | }; 74 | } 75 | 76 | public object Get(GetTechnologyFavoriteDetails request) 77 | { 78 | int id; 79 | var tech = int.TryParse(request.Slug, out id) 80 | ? Db.SingleById(id) 81 | : Db.Single(x => x.Slug == request.Slug.ToLower()); 82 | 83 | var favoriteCount = 84 | Db.Count(x => x.TechnologyId == tech.Id); 85 | 86 | return new GetTechnologyFavoriteDetailsResponse 87 | { 88 | FavoriteCount = (int)favoriteCount 89 | }; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/stacks/create.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
7 |
8 |
9 |
10 | Add a new Technology Stack 11 |
12 |
13 | 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 | 22 |
23 |
24 | 27 | 28 |
29 |
30 | 33 | 34 |
35 |
36 | 37 | 43 |
44 |
45 | 46 | 47 |
48 |
49 | 50 | 51 |
52 |
53 | 54 | 55 |
56 | 59 |
60 |
61 |
62 | 63 |
64 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.Tests/TechStacks.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472 4 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 7 | False 8 | UnitTest 9 | default 10 | TechStacks.Tests 11 | TechStacks.Tests 12 | Copyright © 2014 13 | bin\$(Configuration)\ 14 | 15 | 16 | full 17 | 18 | 19 | pdbonly 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.Tests/TestHosts.cs: -------------------------------------------------------------------------------- 1 | using Funq; 2 | using ServiceStack; 3 | using ServiceStack.Auth; 4 | using ServiceStack.Caching; 5 | using ServiceStack.Data; 6 | using ServiceStack.OrmLite; 7 | using ServiceStack.Testing; 8 | using TechStacks.ServiceInterface; 9 | using TechStacks.ServiceInterface.Filters; 10 | using TechStacks.ServiceModel; 11 | using TechStacks.ServiceModel.Types; 12 | 13 | namespace TechStacks.Tests 14 | { 15 | public class UnitTestHost : BasicAppHost 16 | { 17 | public UnitTestHost() : base(typeof(TechnologyServices).Assembly) {} 18 | 19 | public override void Configure(Container container) 20 | { 21 | container.Register(new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider)); 22 | var dbFactory = container.Resolve(); 23 | this.Plugins.Add(new AuthFeature(() => new CustomUserSession(), 24 | new IAuthProvider[] 25 | { 26 | new TwitterAuthProvider(this.AppSettings), 27 | new GithubAuthProvider(this.AppSettings), 28 | new CredentialsAuthProvider(), 29 | })); 30 | 31 | var authRepo = new OrmLiteAuthRepository(dbFactory); 32 | container.Register(authRepo); 33 | authRepo.InitSchema(); 34 | 35 | container.RegisterAs(); 36 | container.Resolve().InitSchema(); 37 | 38 | using (var db = dbFactory.OpenDbConnection()) 39 | { 40 | db.CreateTableIfNotExists(); 41 | db.CreateTableIfNotExists(); 42 | db.CreateTableIfNotExists(); 43 | db.CreateTableIfNotExists(); 44 | db.CreateTableIfNotExists(); 45 | } 46 | 47 | this.Plugins.Add(new AutoQueryFeature { MaxLimit = 1000 }); 48 | } 49 | } 50 | 51 | public class IntegrationTestHost : AppSelfHostBase 52 | { 53 | public IntegrationTestHost() 54 | : base("IntegrationTestHost", typeof(TechnologyServices).Assembly) {} 55 | 56 | public override void Configure(Container container) 57 | { 58 | container.Register(new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider)); 59 | var dbFactory = container.Resolve(); 60 | this.Plugins.Add(new AuthFeature(() => new CustomUserSession(), new IAuthProvider[] 61 | { 62 | new TwitterAuthProvider(this.AppSettings), 63 | new GithubAuthProvider(this.AppSettings), 64 | new CredentialsAuthProvider(), 65 | })); 66 | 67 | var authRepo = new OrmLiteAuthRepository(dbFactory); 68 | container.Register(authRepo); 69 | authRepo.InitSchema(); 70 | 71 | container.RegisterAs(); 72 | container.Resolve().InitSchema(); 73 | 74 | using (var db = dbFactory.OpenDbConnection()) 75 | { 76 | db.CreateTableIfNotExists(); 77 | db.CreateTableIfNotExists(); 78 | db.CreateTableIfNotExists(); 79 | db.CreateTableIfNotExists(); 80 | db.CreateTableIfNotExists(); 81 | } 82 | 83 | this.Plugins.Add(new AutoQueryFeature { MaxLimit = 1000 }); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/user/feed.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
7 |
8 |
9 |
10 | 11 |
12 |

13 |
14 |
15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
17 | TechStacks 18 |
NameDescriptionconfirm
29 |

30 | You haven't 31 | {{currentUserName}} hasn't 32 | created any TechStacks yet. 33 |

34 | Create a stack 35 |
44 | 45 | 46 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
47 | Favorite TechStacks 48 |
NameDescription
{{techStack.Name}}
{{techStack.Description}}
60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
63 | Favorite Technologies 64 |
NameDescription
{{tech.Name}}
{{tech.Description}}
76 | 77 |
78 |
-------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/favorites/controllers.js: -------------------------------------------------------------------------------- 1 | /* global angular */ 2 | (function () { 3 | "use strict"; 4 | var app = angular.module('favorites.controllers', ['stacks.services']); 5 | 6 | app.controller('favoritesCtrl', [ 7 | '$rootScope', '$scope', '$http', 'techStackServices', 'userService', 8 | function ($rootScope, $scope, $http, techStackServices, userService) { 9 | 10 | if ($rootScope.cachedFeedStacks) { 11 | $scope.feedStacks = $rootScope.cachedFeedStacks; 12 | } 13 | 14 | if ($rootScope.cachedTopTechnologies) { 15 | $scope.topTechnologies = $rootScope.cachedTopTechnologies; 16 | $scope.topUsers = $rootScope.cachedTopUsers; 17 | } 18 | 19 | function refresh() { 20 | userService.isAuthenticated().then(function () { 21 | userService.getUserFeed().then(function (results) { 22 | $scope.feedStacks = results; 23 | $rootScope.cachedFeedStacks = $scope.feedStacks; 24 | }); 25 | }); 26 | 27 | techStackServices.overview().then(function (overview) { 28 | $scope.techStacks = overview.LatestTechStacks; 29 | $scope.topTechnologies = overview.TopTechnologies; 30 | $scope.topUsers = overview.TopUsers; 31 | $scope.topTechCategories = []; 32 | 33 | techStackServices.allTiers().then(function (allTiers) { 34 | $.map(allTiers, function (tier) { 35 | $scope.topTechCategories.push({ 36 | name: tier.name, 37 | title: tier.title, 38 | techs: overview.TopTechnologiesByTier[tier.name] 39 | }); 40 | }); 41 | }); 42 | 43 | $rootScope.cachedTechStacks = $scope.techStacks; 44 | $rootScope.cachedTopTechnologies = $scope.topTechnologies; 45 | $rootScope.cachedTopUsers = $scope.topUsers; 46 | $rootScope.cachedTopTechCategories = $scope.topTechCategories; 47 | }); 48 | } 49 | 50 | refresh(); 51 | 52 | $scope.isFavoriteTech = function (tech) { 53 | var isFav = false; 54 | for (var i = 0; i < $scope.favoriteTechs.length > 0; i++) { 55 | var favTech = $scope.favoriteTechs[i]; 56 | if (favTech.Id === tech.Id) { 57 | isFav = true; 58 | break; 59 | } 60 | } 61 | return isFav; 62 | }; 63 | 64 | $scope.isFavoriteTechStack = function (techStack) { 65 | var isFav = false; 66 | for (var i = 0; i < $scope.favoriteTechStacks.length; i++) { 67 | if ($scope.favoriteTechStacks[i].Id === techStack.Id) { 68 | isFav = true; 69 | break; 70 | } 71 | } 72 | return isFav; 73 | }; 74 | 75 | $scope.addFavoriteTechStack = function (techStack) { 76 | userService.addFavoriteTechStack(techStack); 77 | }; 78 | 79 | $scope.removeFavoriteTechStack = function (techStack) { 80 | userService.removeFavoriteTechStack(techStack); 81 | }; 82 | 83 | $scope.addFavoriteTech = function (tech) { 84 | userService.addFavoriteTech(tech); 85 | }; 86 | 87 | $scope.removeFavoriteTech = function (tech) { 88 | userService.removeFavoriteTech(tech) 89 | .then(refresh); 90 | }; 91 | } 92 | ]); 93 | })(); 94 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/ClientRoutesService.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using TechStacks.ServiceModel; 3 | 4 | namespace TechStacks.ServiceInterface 5 | { 6 | [FallbackRoute("/{PathInfo*}", Matches = "AcceptsHtml")] 7 | public class FallbackForClientRoutes 8 | { 9 | public string PathInfo { get; set; } 10 | } 11 | 12 | [Route("/ping")] 13 | public class Ping {} 14 | 15 | public class ClientRoutesService : Service 16 | { 17 | public object Any(Ping request) 18 | { 19 | return "OK"; 20 | } 21 | 22 | public bool ShowServerHtml() 23 | { 24 | if (Request.GetParam("html") == "client") 25 | { 26 | Response.DeleteCookie("html"); 27 | return false; 28 | } 29 | 30 | var serverHtml = (Request.UserAgent != null && Request.UserAgent.Contains("Googlebot")) 31 | || Request.GetParam("html") == "server"; 32 | 33 | if (serverHtml) 34 | { 35 | Response.SetPermanentCookie("html", "server"); 36 | } 37 | 38 | return serverHtml; 39 | } 40 | 41 | public object Any(FallbackForClientRoutes request) 42 | { 43 | var path = (request.PathInfo ?? "").Trim('/'); 44 | if (ShowServerHtml()) 45 | return path == "" 46 | ? new HttpResult(base.Gateway.Send(new Overview())) { 47 | View = "Home" 48 | } 49 | : new HttpResult(base.Gateway.Send(new GetTechnologyStack { Slug = request.PathInfo })) { 50 | View = "Stack" 51 | }; 52 | 53 | return AngularJsApp(); 54 | } 55 | 56 | public object AngularJsApp() 57 | { 58 | //Return default.cshtml for unmatched requests so routing is handled on the client 59 | return new HttpResult(VirtualFileSources.GetFile("/default.html")); 60 | } 61 | 62 | public object Any(ClientAllTechnologyStacks request) 63 | { 64 | return !ShowServerHtml() 65 | ? AngularJsApp() 66 | : new HttpResult(base.Gateway.Send(new GetAllTechnologyStacks())) { 67 | View = "AllStacks" 68 | }; 69 | } 70 | 71 | public object Any(ClientAllTechnologies request) 72 | { 73 | return !ShowServerHtml() 74 | ? AngularJsApp() 75 | : new HttpResult(base.Gateway.Send(new GetAllTechnologies())) { 76 | View = "AllTech" 77 | }; 78 | } 79 | 80 | public object Any(ClientTechnology request) 81 | { 82 | return !ShowServerHtml() 83 | ? AngularJsApp() 84 | : new HttpResult(base.Gateway.Send(new GetTechnology { Slug = request.Slug })) { 85 | View = "Tech" 86 | }; 87 | } 88 | 89 | public object Any(ClientUser request) 90 | { 91 | return !ShowServerHtml() 92 | ? AngularJsApp() 93 | : new HttpResult(base.Gateway.Send(new GetUserInfo { UserName = request.UserName })) { 94 | View = "User" 95 | }; 96 | } 97 | } 98 | 99 | //Client Routes to generate urls in sitemap.xml 100 | 101 | [Route("/tech")] 102 | public class ClientAllTechnologies {} 103 | 104 | [Route("/tech/{Slug}")] 105 | public class ClientTechnology 106 | { 107 | public string Slug { get; set; } 108 | } 109 | 110 | [Route("/stacks")] 111 | public class ClientAllTechnologyStacks { } 112 | 113 | [Route("/{Slug}")] 114 | public class ClientTechnologyStack 115 | { 116 | public string Slug { get; set; } 117 | } 118 | 119 | [Route("/users/{UserName}")] 120 | public class ClientUser 121 | { 122 | public string UserName { get; set; } 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/Types/TechnologyStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ServiceStack.DataAnnotations; 4 | 5 | namespace TechStacks.ServiceModel.Types 6 | { 7 | public abstract class TechnologyStackBase 8 | { 9 | [AutoIncrement] 10 | public long Id { get; set; } 11 | 12 | public string Name { get; set; } 13 | public string VendorName { get; set; } 14 | public string Description { get; set; } 15 | public string AppUrl { get; set; } 16 | public string ScreenshotUrl { get; set; } 17 | 18 | public DateTime Created { get; set; } 19 | public string CreatedBy { get; set; } 20 | public DateTime LastModified { get; set; } 21 | public string LastModifiedBy { get; set; } 22 | 23 | public bool IsLocked { get; set; } 24 | 25 | public string OwnerId { get; set; } 26 | 27 | [Index] 28 | public string Slug { get; set; } 29 | 30 | public string Details { get; set; } 31 | 32 | public DateTime? LastStatusUpdate { get; set; } 33 | } 34 | 35 | public class TechnologyStack : TechnologyStackBase {} 36 | 37 | public class TechnologyStackHistory : TechnologyStackBase 38 | { 39 | public long TechnologyStackId { get; set; } 40 | public string Operation { get; set; } 41 | public List TechnologyIds { get; set; } 42 | } 43 | 44 | public class TechnologyChoice 45 | { 46 | [AutoIncrement] 47 | public long Id { get; set; } 48 | 49 | [References(typeof(Technology))] 50 | public long TechnologyId { get; set; } 51 | 52 | [Reference] 53 | public Technology Technology { get; set; } 54 | 55 | [References(typeof(TechnologyStack))] 56 | public long TechnologyStackId { get; set; } 57 | 58 | [Reference] 59 | public TechnologyStack TechnologyStack { get; set; } 60 | 61 | public string CreatedBy { get; set; } 62 | public string LastModifiedBy { get; set; } 63 | public string OwnerId { get; set; } 64 | } 65 | 66 | public abstract class TechnologyBase 67 | { 68 | [AutoIncrement] 69 | public long Id { get; set; } 70 | 71 | public string Name { get; set; } 72 | public string VendorName { get; set; } 73 | public string VendorUrl { get; set; } 74 | public string ProductUrl { get; set; } 75 | public string LogoUrl { get; set; } 76 | public string Description { get; set; } 77 | 78 | public DateTime Created { get; set; } 79 | public string CreatedBy { get; set; } 80 | public DateTime LastModified { get; set; } 81 | public string LastModifiedBy { get; set; } 82 | public string OwnerId { get; set; } 83 | 84 | [Index] 85 | public string Slug { get; set; } 86 | 87 | public bool LogoApproved { get; set; } 88 | public bool IsLocked { get; set; } 89 | 90 | public TechnologyTier Tier { get; set; } 91 | 92 | public DateTime? LastStatusUpdate { get; set; } 93 | } 94 | 95 | public class Technology : TechnologyBase {} 96 | 97 | public class TechnologyHistory : TechnologyBase 98 | { 99 | public long TechnologyId { get; set; } 100 | public string Operation { get; set; } 101 | } 102 | 103 | public enum TechnologyTier 104 | { 105 | [Description("Programming Languages")] 106 | ProgrammingLanguage, 107 | 108 | [Description("Client Libraries")] 109 | Client, 110 | 111 | [Description("HTTP Server Technologies")] 112 | Http, 113 | 114 | [Description("Server Libraries")] 115 | Server, 116 | 117 | [Description("Databases and NoSQL Datastores")] 118 | Data, 119 | 120 | [Description("Server Software")] 121 | SoftwareInfrastructure, 122 | 123 | [Description("Operating Systems")] 124 | OperatingSystem, 125 | 126 | [Description("Cloud/Hardware Infastructure")] 127 | HardwareInfrastructure, 128 | 129 | [Description("3rd Party APIs/Services")] 130 | ThirdPartyServices, 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/js/tech/services.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var app = angular.module('tech.services', []); 4 | 5 | app.service('techServices', ['$rootScope', '$http', '$q', function ($rootScope, $http, $q) { 6 | 7 | var errorStatusMessages = { 8 | 401: 'Not Authenticated', 9 | 500: 'Internal Server Error' 10 | }; 11 | 12 | var getResults = function (promise) { 13 | var deferred = $q.defer(); 14 | $rootScope.errorMessage = null; 15 | promise 16 | .success(function (response) { 17 | deferred.resolve(response.Result || response.Results || response); 18 | }) 19 | .error(function (e, status) { 20 | $rootScope.errorMessage = 21 | (e && e.ResponseStatus && e.ResponseStatus.Message) //DTO Error 22 | || e //Raw Error 23 | || errorStatusMessages[status]; //HTTP Status Error 24 | 25 | deferred.reject($rootScope.errorMessage); 26 | }); 27 | return deferred.promise; 28 | }; 29 | 30 | return { 31 | getResults: getResults, 32 | getTech: function(id) { 33 | return getResults($http.get('/technology/' + id)); 34 | }, 35 | getTechPreviousVersions: function (id) { 36 | return getResults($http.get('/technology/' + id + '/previous-versions')); 37 | }, 38 | getTechFavorites: function (id) { 39 | return getResults($http.get('/technology/' + id + '/favorites')); 40 | }, 41 | searchTech: function (searchQuery) { 42 | return getResults($http.get('/technology/search?orderBy=Name&NameContains=' + searchQuery + "&DescriptionContains=" + searchQuery)); 43 | }, 44 | getAllTechs: function () { 45 | return getResults($http.get('/technology')); 46 | }, 47 | createTech: function (newTech) { 48 | return getResults($http.post('/technology', newTech)); 49 | }, 50 | updateTech: function (tech) { 51 | return getResults($http.put('/technology/' + tech.Id, tech)); 52 | }, 53 | deleteTech: function (tech) { 54 | return getResults($http.delete('/technology/' + tech.Id)); 55 | }, 56 | updateLockStatus: function (techId, isLocked) { 57 | return getResults($http.put('/admin/technology/' + techId + '/lock', { IsLocked: isLocked })); 58 | }, 59 | makeFavorite: function (tech) { 60 | return getResults($http.put('/favorites/technology', { TechnologyId: tech.Id })); 61 | }, 62 | approveLogo: function(tech,status) { 63 | return getResults($http.put('/admin/technology/' + tech.Id + '/logo', { Approved: status })); 64 | }, 65 | getPageStats: function(id) { 66 | return getResults($http.get('/pagestats/tech/' + id)); 67 | }, 68 | overview: function () { 69 | return getResults($http.get('/overview')); 70 | }, 71 | config: function () { 72 | return getResults($http.get('/config')); 73 | }, 74 | popularTechStacks: function () { 75 | var deferred = $q.defer(); 76 | if ($rootScope.cachedPopularTechStacks) { 77 | deferred.resolve($rootScope.cachedPopularTechStacks); 78 | } else { 79 | this.overview().then(function (r) { 80 | deferred.resolve($rootScope.cachedPopularTechStacks = r.PopularTechStacks); 81 | }); 82 | } 83 | return deferred.promise; 84 | }, 85 | allTiers: function() { 86 | var deferred = $q.defer(); 87 | if ($rootScope.allTiers && $rootScope.allTiers.length > 0) { 88 | deferred.resolve($rootScope.allTiers); 89 | } 90 | getResults($http.get('/config')).then(function(r) { 91 | deferred.resolve(r.AllTiers); 92 | }); 93 | return deferred.promise; 94 | } 95 | }; 96 | }]); 97 | })(); 98 | 99 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.Tests/Seeds.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Funq; 8 | using ServiceStack; 9 | using ServiceStack.Auth; 10 | using ServiceStack.Configuration; 11 | using ServiceStack.Data; 12 | using ServiceStack.OrmLite; 13 | using TechStacks.ServiceInterface.Filters; 14 | using TechStacks.ServiceModel; 15 | using TechStacks.ServiceModel.Types; 16 | using TechStacks.ServiceInterface; 17 | 18 | namespace TechStacks.Tests 19 | { 20 | public static class Seeds 21 | { 22 | public static void SeedApp(IDbConnectionFactory dbFactory) 23 | { 24 | using (var db = dbFactory.OpenDbConnection()) 25 | { 26 | var ssTech = new Technology 27 | { 28 | Name = "ServiceStack", 29 | Slug = "ServiceStack".GenerateSlug(), 30 | Tier = TechnologyTier.Server, 31 | Description = 32 | "Obscenely fast! Built with only fast, clean, code-first and light-weight parts. Start using .NET's fastest serializers, ORMs, redis and caching libraries!", 33 | VendorName = "ServiceStack", 34 | LogoUrl = "https://github.com/ServiceStack/Assets/raw/master/img/artwork/fulllogo-280.png" 35 | }; 36 | 37 | var iisTech = new Technology 38 | { 39 | Name = "IIS", 40 | Slug = "IIS".GenerateSlug(), 41 | Tier = TechnologyTier.Http, 42 | Description = "Microsoft's web host", 43 | VendorName = "Microsoft", 44 | LogoUrl = 45 | "http://www.microsoft.com/web/media/gallery/apps-screenshots/Microsoft-App-Request-Routing.png" 46 | }; 47 | 48 | var ravenDbTech = new Technology 49 | { 50 | Name = "RavenDB", 51 | Slug = "RavenDB".GenerateSlug(), 52 | Tier = TechnologyTier.Data, 53 | Description = "Open source 2nd generation document DB", 54 | VendorName = "RavenDB", 55 | LogoUrl = "http://static.ravendb.net/logo-for-nuget.png" 56 | }; 57 | 58 | var postgresTech = new Technology 59 | { 60 | Name = "PostgreSQL", 61 | Slug = "PostgreSQL".GenerateSlug(), 62 | Tier = TechnologyTier.Data, 63 | Description = "The world's most advanced open source database.", 64 | 65 | VendorName = "PostgreSQL", 66 | LogoUrl = "http://www.myintervals.com/blog/wp-content/uploads/2011/12/postgresql-logo1.png" 67 | }; 68 | 69 | db.Insert(ssTech); 70 | db.Insert(iisTech); 71 | db.Insert(ravenDbTech); 72 | db.Insert(postgresTech); 73 | 74 | var initialStack = new TechnologyStack 75 | { 76 | Name = "Initial Stack", 77 | Slug = "Initial Stack".GenerateSlug(), 78 | Description = "Example stack" 79 | }; 80 | 81 | db.Insert(initialStack); 82 | 83 | var initialStackId = db.LastInsertId(); 84 | db.Insert(new TechnologyChoice 85 | { 86 | TechnologyId = ssTech.Id, 87 | TechnologyStackId = initialStackId 88 | }); 89 | 90 | db.Insert(new TechnologyChoice 91 | { 92 | TechnologyId = postgresTech.Id, 93 | TechnologyStackId = initialStackId 94 | }); 95 | 96 | db.Insert(new TechnologyChoice 97 | { 98 | TechnologyId = ravenDbTech.Id, 99 | TechnologyStackId = initialStackId 100 | }); 101 | 102 | db.Insert(new TechnologyChoice 103 | { 104 | TechnologyId = iisTech.Id, 105 | TechnologyStackId = initialStackId 106 | }); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Views/Shared/ServerHtml.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 38 | 39 | 40 | 65 | 66 |
67 | @RenderBody() 68 |
69 | 70 | 77 | 78 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/TwitterUpdates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | using ServiceStack; 8 | using ServiceStack.Auth; 9 | using ServiceStack.Configuration; 10 | 11 | namespace TechStacks.ServiceInterface 12 | { 13 | public class TwitterUpdates 14 | { 15 | private TwitterGateway gateway; 16 | private readonly string accessToken; 17 | private readonly string accessTokenSecret; 18 | 19 | public TwitterUpdates( 20 | string consumerKey, string consumerSecret, 21 | string accessToken, string accessTokenSecret) 22 | { 23 | this.accessToken = accessToken; 24 | this.accessTokenSecret = accessTokenSecret; 25 | this.gateway = new TwitterGateway 26 | { 27 | TwitterAuthProvider = new TwitterAuthProvider(new DictionarySettings( 28 | new Dictionary { 29 | {"oauth.twitter.ConsumerKey", consumerKey}, 30 | {"oauth.twitter.ConsumerSecret", consumerSecret}, 31 | })) 32 | }; 33 | } 34 | 35 | public string Tweet(string status) 36 | { 37 | var response = gateway.Send(new PostStatusTwitter 38 | { 39 | AccessToken = accessToken, 40 | AccessTokenSecret = accessTokenSecret, 41 | Status = status 42 | }) 43 | .FirstOrDefault(); 44 | 45 | return response.StartsWithIgnoreCase("ERROR: ") ? response : null; 46 | } 47 | } 48 | 49 | public class PostStatusTwitter 50 | { 51 | public string AccessToken { get; set; } 52 | public string AccessTokenSecret { get; set; } 53 | public string Status { get; set; } 54 | } 55 | 56 | public class TwitterGateway 57 | { 58 | public TwitterAuthProvider TwitterAuthProvider { get; set; } 59 | 60 | public List Send(params PostStatusTwitter[] messages) 61 | { 62 | var results = new List(); 63 | foreach (var message in messages) 64 | { 65 | try 66 | { 67 | var response = PostToUrl(TwitterAuthProvider, 68 | "https://api.twitter.com/1.1/statuses/update.json", 69 | message.AccessToken, message.AccessTokenSecret, 70 | new Dictionary { { "status", message.Status } }); 71 | 72 | results.Add(response); 73 | } 74 | catch (Exception ex) 75 | { 76 | results.Add("ERROR: " + ex); 77 | } 78 | } 79 | return results; 80 | } 81 | 82 | public static string PostToUrl(TwitterAuthProvider oAuthProvider, string url, string accessToken, string accessTokenSecret, Dictionary args, string acceptType = MimeTypes.Json) 83 | { 84 | var uri = new Uri(url); 85 | var webReq = (HttpWebRequest)WebRequest.Create(uri); 86 | webReq.Accept = acceptType; 87 | webReq.Method = HttpMethods.Post; 88 | 89 | string data = null; 90 | if (args != null) 91 | { 92 | var sb = new StringBuilder(); 93 | foreach (var arg in args) 94 | { 95 | if (sb.Length > 0) 96 | sb.Append("&"); 97 | sb.Append($"{arg.Key}={OAuthUtils.PercentEncode(arg.Value)}"); 98 | } 99 | data = sb.ToString(); 100 | } 101 | 102 | webReq.Headers[HttpRequestHeader.Authorization] = OAuthAuthorizer.AuthorizeRequest( 103 | oAuthProvider, accessToken, accessTokenSecret, "POST", uri, data); 104 | 105 | if (data != null) 106 | { 107 | webReq.ContentType = MimeTypes.FormUrlEncoded; 108 | using (var writer = new StreamWriter(webReq.GetRequestStream())) 109 | writer.Write(data); 110 | } 111 | 112 | using (var webRes = webReq.GetResponse()) 113 | { 114 | return webRes.ReadToEnd(); 115 | } 116 | } 117 | 118 | } 119 | } -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceModel/Technologies.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using ServiceStack; 5 | using TechStacks.ServiceModel.Types; 6 | 7 | namespace TechStacks.ServiceModel 8 | { 9 | [QueryDb(QueryTerm.Or)] 10 | [Route("/technology/search")] 11 | [AutoQueryViewer( 12 | Title = "Find Technologies", Description = "Explore different Technologies", 13 | IconUrl = "octicon:database", 14 | DefaultSearchField = "Tier", DefaultSearchType = "=", DefaultSearchText = "Data")] 15 | public class FindTechnologies : QueryDb 16 | { 17 | public string Name { get; set; } 18 | public string NameContains { get; set; } 19 | } 20 | 21 | [QueryDb(QueryTerm.And)] 22 | [Route("/admin/technology/search")] 23 | [AutoQueryViewer( 24 | Title = "Find Technologies Admin", Description = "Explore different Technologies", 25 | IconUrl = "octicon:database", 26 | DefaultSearchField = "Tier", DefaultSearchType = "=", DefaultSearchText = "Data")] 27 | public class FindTechnologiesAdmin : QueryDb 28 | { 29 | public string Name { get; set; } 30 | } 31 | 32 | [Route("/technology/{Slug}")] 33 | public class GetTechnology : IReturn, IRegisterStats 34 | { 35 | public string Slug { get; set; } 36 | 37 | public long Id 38 | { 39 | set { this.Slug = value.ToString(); } 40 | } 41 | 42 | public string GetStatsId() 43 | { 44 | return "/tech/" + Slug; 45 | } 46 | } 47 | 48 | public class GetTechnologyResponse 49 | { 50 | public DateTime Created { get; set; } 51 | 52 | public Technology Technology { get; set; } 53 | 54 | public List TechnologyStacks { get; set; } 55 | 56 | public ResponseStatus ResponseStatus { get; set; } 57 | } 58 | 59 | [Route("/technology/{Slug}/previous-versions", Verbs = "GET")] 60 | public class GetTechnologyPreviousVersions : IReturn 61 | { 62 | public string Slug { get; set; } 63 | 64 | [IgnoreDataMember] 65 | public long Id 66 | { 67 | set { this.Slug = value.ToString(); } 68 | } 69 | } 70 | 71 | public class GetTechnologyPreviousVersionsResponse 72 | { 73 | public List Results { get; set; } 74 | } 75 | 76 | [Route("/technology", Verbs = "POST")] 77 | public class CreateTechnology : IReturn 78 | { 79 | [ValidateNotEmpty] 80 | public string Name { get; set; } 81 | [ValidateNotEmpty] 82 | public string VendorName { get; set; } 83 | [ValidateNotEmpty] 84 | public string VendorUrl { get; set; } 85 | [ValidateNotEmpty] 86 | public string ProductUrl { get; set; } 87 | [ValidateNotEmpty] 88 | public string LogoUrl { get; set; } 89 | [ValidateNotEmpty] 90 | public string Description { get; set; } 91 | public bool IsLocked { get; set; } 92 | 93 | public TechnologyTier Tier { get; set; } 94 | } 95 | 96 | public class CreateTechnologyResponse 97 | { 98 | public Technology Result { get; set; } 99 | 100 | public ResponseStatus ResponseStatus { get; set; } 101 | } 102 | 103 | [Route("/technology/{Id}", Verbs = "PUT")] 104 | public class UpdateTechnology : IReturn 105 | { 106 | public long Id { get; set; } 107 | 108 | public string Name { get; set; } 109 | public string VendorName { get; set; } 110 | public string VendorUrl { get; set; } 111 | public string ProductUrl { get; set; } 112 | public string LogoUrl { get; set; } 113 | public string Description { get; set; } 114 | public bool IsLocked { get; set; } 115 | 116 | public TechnologyTier Tier { get; set; } 117 | } 118 | 119 | public class UpdateTechnologyResponse 120 | { 121 | public Technology Result { get; set; } 122 | 123 | public ResponseStatus ResponseStatus { get; set; } 124 | } 125 | 126 | [Route("/technology/{Id}", Verbs = "DELETE")] 127 | public class DeleteTechnology : IReturn 128 | { 129 | public long Id { get; set; } 130 | } 131 | 132 | public class DeleteTechnologyResponse 133 | { 134 | public Technology Result { get; set; } 135 | 136 | public ResponseStatus ResponseStatus { get; set; } 137 | } 138 | 139 | [Route("/technology", Verbs = "GET")] 140 | public class GetAllTechnologies : IReturn {} 141 | 142 | public class GetAllTechnologiesResponse 143 | { 144 | public List Results { get; set; } 145 | } 146 | 147 | [Route("/technology/{Slug}/favorites")] 148 | public class GetTechnologyFavoriteDetails : IReturn 149 | { 150 | public string Slug { get; set; } 151 | } 152 | 153 | public class GetTechnologyFavoriteDetailsResponse 154 | { 155 | public List Users { get; set; } 156 | public int FavoriteCount { get; set; } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.Tests/AdminTasks.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using System.IO; 4 | using NUnit.Framework; 5 | using ServiceStack; 6 | using ServiceStack.Configuration; 7 | using ServiceStack.Data; 8 | using ServiceStack.DataAnnotations; 9 | using ServiceStack.OrmLite; 10 | using ServiceStack.Text; 11 | using TechStacks.ServiceInterface; 12 | using TechStacks.ServiceModel.Types; 13 | using System; 14 | 15 | namespace TechStacks.Tests 16 | { 17 | [NUnit.Framework.Ignore("One-off Admin Tasks")] 18 | public class AdminTasks 19 | { 20 | private IAppSettings config; 21 | IAppSettings Config 22 | { 23 | get { return config ?? (config = new TextFileSettings("~/appsettings.txt".MapProjectPath())); } 24 | } 25 | 26 | private IDbConnectionFactory factory; 27 | private IDbConnectionFactory Factory 28 | 29 | { 30 | get 31 | { 32 | return factory ?? (factory = new OrmLiteConnectionFactory( 33 | Config.GetString("OrmLite.ConnectionString"), PostgreSqlDialect.Provider)); 34 | } 35 | } 36 | 37 | public IDbConnection OpenDbConnection() 38 | { 39 | return Factory.OpenDbConnection(); 40 | } 41 | 42 | [Test] 43 | public void Reset_History_Table() 44 | { 45 | using (var db = OpenDbConnection()) 46 | { 47 | db.DropAndCreateTable(); 48 | db.DropAndCreateTable(); 49 | 50 | var allTechs = db.Select(); 51 | foreach (var tech in allTechs) 52 | { 53 | var history = tech.ConvertTo(); 54 | history.Operation = "INSERT"; 55 | history.TechnologyId = tech.Id; 56 | db.Insert(history); 57 | } 58 | 59 | var allStacks = db.Select(); 60 | foreach (var stack in allStacks) 61 | { 62 | var history = stack.ConvertTo(); 63 | history.Operation = "INSERT"; 64 | history.TechnologyStackId = stack.Id; 65 | history.TechnologyIds = db.Column(db.From() 66 | .Where(x => x.TechnologyStackId == stack.Id) 67 | .Select(x => x.TechnologyId)); 68 | db.Insert(history); 69 | } 70 | } 71 | } 72 | 73 | [Test] 74 | public void UpdateSlugTitles() 75 | { 76 | using (var db = OpenDbConnection()) 77 | { 78 | var allTechs = db.Select(); 79 | allTechs.ForEach(x => x.Slug = x.Name.GenerateSlug()); 80 | db.UpdateAll(allTechs); 81 | var allStacks = db.Select(); 82 | allStacks.ForEach(x => x.Slug = x.Name.GenerateSlug()); 83 | db.UpdateAll(allStacks); 84 | } 85 | } 86 | 87 | [Test] 88 | public void Can_Tweet_update() 89 | { 90 | var twitter = new TwitterUpdates( 91 | Config.GetString("WebStacks.ConsumerKey"), 92 | Config.GetString("WebStacks.ConsumerSecret"), 93 | Config.GetString("WebStacks.AccessToken"), 94 | Config.GetString("WebStacks.AccessSecret")); 95 | 96 | twitter.Tweet("Test for http:techstacks.io"); 97 | } 98 | 99 | [Test] 100 | public void Import_PageViews() 101 | { 102 | var pageViews = "~/../../page-views.txt".MapAbsolutePath().ReadAllText(); 103 | var map = pageViews.ParseKeyValueText(); 104 | "{0} page view entries".Print(map.Count); 105 | 106 | CheckUniqueStats(pageViews); 107 | 108 | using (var db = Factory.OpenDbConnection()) 109 | { 110 | db.DropAndCreateTable(); 111 | var now = DateTime.UtcNow; 112 | foreach (var entry in map) 113 | { 114 | var parts = entry.Key.Substring(1).SplitOnFirst('/'); 115 | if (parts.Length != 2) continue; 116 | var type = parts[0]; 117 | var slug = parts[1]; 118 | 119 | var pageStats = new PageStats 120 | { 121 | Id = entry.Key, 122 | RefType = type, 123 | RefSlug = slug, 124 | RefId = 0, 125 | ViewCount = long.Parse(entry.Value), 126 | LastModified = now, 127 | }; 128 | 129 | db.Insert(pageStats); 130 | } 131 | } 132 | } 133 | 134 | private static void CheckUniqueStats(string pageViews) 135 | { 136 | var uniquePaths = new HashSet(); 137 | foreach (var line in pageViews.ReadLines()) 138 | { 139 | var parts = line.SplitOnFirst(' '); 140 | var key = parts[0]; 141 | if (uniquePaths.Contains(key)) 142 | "Duplicated: {0}:{1}".Print(key, parts[1]); 143 | 144 | uniquePaths.Add(key); 145 | } 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/favorites/favorites.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

TechStacks with my favorite technologies...

6 |
7 | 8 |
9 |
10 | 20 |
{{feedStack.Description | limitTo:140}} ... Read More
21 | 29 |
30 |
31 |
32 | 33 |
34 |
35 | Favorite Stacks 36 |
37 |
38 | 48 | 49 | Add TechStack 50 | 51 |
52 |
53 | 54 |
55 |
56 | Favorite Technologies 57 |
58 |
59 | 69 |
Can't find your favorite tech?
70 | Add technology 71 |
72 |
73 | 74 |
75 |
76 | Browse by Technology 77 |
78 |
79 | 85 |
86 |
87 | 88 |
89 |
90 | Browse by Users 91 |
92 |
93 | 102 |
103 |
104 |
105 |
106 |
-------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/tech/edit.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
7 |
8 |
9 |
10 | Update 11 | {{tech.Name}} 12 |
13 |
14 | 15 |
16 | 31 |
32 | 33 | 34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 | 45 |
46 |
47 | 48 | 49 |
50 |
51 | 56 | 57 |
58 |
59 | 60 | 61 |
62 |
63 | 64 | 65 |
66 |
67 | 68 | 69 |
70 | 73 |
74 | 78 | 79 |
80 |
81 |
82 | Loading... 83 |
84 |
85 |
86 |
87 |
88 | 89 |
90 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/stacks/edit.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
7 |
8 |
9 |
10 | Update 11 | {{currentStack.Name}} 12 | TechStack 13 |
14 |
15 |
16 | 31 |
32 | 33 | 34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 | 47 |
48 |
49 | 52 | 53 |
54 |
55 | 58 | 59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |
69 | 70 | 71 |
72 | 75 |
76 | 80 | 81 |
82 |
83 |
84 | Loading... 85 |
86 |
87 |
88 |
89 |
90 | 91 |
92 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/UserFavoriteServices.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ServiceStack; 4 | using ServiceStack.OrmLite; 5 | using TechStacks.ServiceModel; 6 | using TechStacks.ServiceModel.Types; 7 | 8 | namespace TechStacks.ServiceInterface 9 | { 10 | [Authenticate] 11 | public class UserFavoriteServices : Service 12 | { 13 | public object Get(GetFavoriteTechStack request) 14 | { 15 | var session = SessionAs(); 16 | var favorites = Db.Select(x => x.UserId == session.UserAuthId); 17 | var results = favorites.Count == 0 18 | ? new List() 19 | : Db.Select(Db.From() 20 | .Where(x => Sql.In(x.Id, favorites.Select(y => y.TechnologyStackId)))); 21 | 22 | return new GetFavoriteTechStackResponse 23 | { 24 | Results = results 25 | }; 26 | } 27 | 28 | public object Put(AddFavoriteTechStack request) 29 | { 30 | var session = SessionAs(); 31 | var techStack = Db.SingleById(request.TechnologyStackId); 32 | if (techStack == null) 33 | throw HttpError.NotFound("TechnologyStack not found"); 34 | 35 | var existingFavorite = Db.Single( 36 | x => x.TechnologyStackId == request.TechnologyStackId && x.UserId == session.UserAuthId); 37 | 38 | if (existingFavorite == null) 39 | { 40 | Db.Insert(new UserFavoriteTechnologyStack 41 | { 42 | TechnologyStackId = request.TechnologyStackId, 43 | UserId = session.UserAuthId 44 | }); 45 | 46 | Cache.FlushAll(); 47 | } 48 | 49 | return new FavoriteTechStackResponse 50 | { 51 | Result = techStack 52 | }; 53 | } 54 | 55 | public object Delete(RemoveFavoriteTechStack request) 56 | { 57 | var session = SessionAs(); 58 | var techStack = Db.SingleById(request.TechnologyStackId); 59 | if (techStack == null) 60 | throw HttpError.NotFound("TechnologyStack not found"); 61 | 62 | var existingFavorite = 63 | Db.Single( 64 | x => x.TechnologyStackId == request.TechnologyStackId && x.UserId == session.UserAuthId); 65 | 66 | if (existingFavorite == null) 67 | throw HttpError.NotFound("Favorite not found"); 68 | 69 | Db.DeleteById(existingFavorite.Id); 70 | 71 | Cache.FlushAll(); 72 | 73 | return new FavoriteTechStackResponse 74 | { 75 | Result = techStack, 76 | }; 77 | } 78 | 79 | public object Get(GetFavoriteTechnologies request) 80 | { 81 | var session = SessionAs(); 82 | var favorites = Db.Select(x => x.UserId == session.UserAuthId).ToList(); 83 | var results = favorites.Count == 0 84 | ? new List() 85 | : Db.Select(Db.From() 86 | .Where(x => Sql.In(x.Id, favorites.Select(y => y.TechnologyId)))); 87 | 88 | return new GetFavoriteTechnologiesResponse 89 | { 90 | Results = results 91 | }; 92 | } 93 | 94 | public object Put(AddFavoriteTechnology request) 95 | { 96 | var session = SessionAs(); 97 | var technology = Db.SingleById(request.TechnologyId); 98 | if (technology == null) 99 | throw HttpError.NotFound("Technology not found"); 100 | 101 | var existingFavorite = 102 | Db.Single( 103 | x => x.TechnologyId == request.TechnologyId && x.UserId == session.UserAuthId); 104 | 105 | if (existingFavorite == null) 106 | { 107 | Db.Insert(new UserFavoriteTechnology 108 | { 109 | TechnologyId = request.TechnologyId, 110 | UserId = session.UserAuthId 111 | }); 112 | 113 | Cache.FlushAll(); 114 | } 115 | 116 | return new FavoriteTechnologyResponse 117 | { 118 | Result = technology 119 | }; 120 | } 121 | 122 | public object Delete(RemoveFavoriteTechnology request) 123 | { 124 | var session = SessionAs(); 125 | var technology = Db.SingleById(request.TechnologyId); 126 | if (technology == null) 127 | throw HttpError.NotFound("Technology not found"); 128 | 129 | var existingFavorite = 130 | Db.Single( 131 | x => x.TechnologyId == request.TechnologyId && x.UserId == session.UserAuthId); 132 | 133 | if (existingFavorite == null) 134 | throw HttpError.NotFound("Favorite not found"); 135 | 136 | Db.DeleteById(existingFavorite.Id); 137 | 138 | Cache.FlushAll(); 139 | 140 | return new FavoriteTechnologyResponse 141 | { 142 | Result = technology 143 | }; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 11 |
14 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/partials/home.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | Welcome to TechStacks! 7 |
8 |
9 |

10 | Discover what technologies were used to create popular Websites and Apps, for example here's what TechStacks was created with :) 11 |

12 |

13 | Missing your favorite Tech or TechStack? Sign-in and add it now! 14 | You'll also be able to customize this page and see who else uses your favorite tech! 15 |

16 | 36 |
37 |
38 |
39 | 40 |
41 |
42 | 49 |
50 | Most Popular: 51 |
52 | 60 |
61 |
62 |
63 |
64 | 71 |
72 | Most Popular: 73 |
74 | 82 |
83 |
84 | 85 |
86 | 87 |
88 |
89 | Browse by Technology 90 |
91 |
92 | 98 |
99 |
100 | 101 |
102 |
103 | Browse by Users 104 |
105 |
106 | 115 |
116 |
117 | 118 |
119 |
120 |
-------------------------------------------------------------------------------- /src/TechStacks/TechStacks/Views/Home.cshtml: -------------------------------------------------------------------------------- 1 | @inherits ViewPage 2 | @{ 3 | Layout = "ServerHtml"; 4 | } 5 |
6 |
7 |
8 |
9 | Welcome to TechStacks! 10 |
11 |
12 |

13 | Discover what technologies were used to create popular Websites and Apps, for example here's what TechStacks was created with :) 14 |

15 |

16 | Missing your favorite Tech or TechStack? Sign-in and add it now! 17 | You'll also be able to customize this page and see who else uses your favorite tech! 18 |

19 | 39 |
40 |
41 |
42 | 43 |
44 | 45 | @foreach (var techStack in Model.LatestTechStacks) 46 | { 47 |
48 | 58 |
59 | @techStack.Description.SafeSubstring(0, 140) 60 | ... Read More 61 |
62 | 74 |
75 | } 76 |
77 | 78 |
79 | 80 |
81 |
82 | Browse by Technology 83 |
84 |
85 |
    86 | @foreach (var tech in Model.TopTechnologies) 87 | { 88 |
  • 89 | @tech.Name 90 | (@tech.StacksCount) 91 |
  • 92 | } 93 |
94 |
95 |
96 | 97 |
98 |
99 | Browse by Users 100 |
101 |
102 | 114 |
115 |
116 | 117 |
118 |
119 | Sitemap 120 |
121 |
122 | 136 |
137 |
138 | 139 |
140 |
141 | -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/tests/unit/techControllerSpec.js: -------------------------------------------------------------------------------- 1 | var allTechsResponse = { "Techs": [{ "Id": 1, "Name": "ServiceStack", "VendorName": "ServiceStack", "Description": "Obscenely fast! Built with only fast, clean, code-first and light-weight parts. Start using .NET's fastest serializers, ORMs, redis and caching libraries!", "Tiers": ["App", "Web", "Client"] }, { "Id": 2, "Name": "IIS", "VendorName": "Microsoft", "Description": "Microsoft's web host", "Tiers": ["Web"] }, { "Id": 3, "Name": "RavenDB", "VendorName": "RavenDB", "Description": "Open source 2nd generation document DB", "Tiers": ["Data"] }, { "Id": 4, "Name": "PostgreSQL", "VendorName": "PostgreSQL", "Description": "The world's most advanced open source database.", "Tiers": ["Data"] }] }; 2 | var specificTechResponse = { "Tech": { "Id": 1, "Name": "ServiceStack", "VendorName": "ServiceStack", "Description": "Obscenely fast! Built with only fast, clean, code-first and light-weight parts. Start using .NET's fastest serializers, ORMs, redis and caching libraries!", "Tiers": ["App", "Web", "Client"] } }; 3 | var latestTechStacks = { "TechStacks": [{ "TechnologyChoices": [{ "TechnologyId": 1, "TechnologyStackId": 1, "Tier": "Web", "Id": 1, "Name": "ServiceStack", "VendorName": "ServiceStack", "LogoUrl": "https://github.com/ServiceStack/Assets/raw/master/img/artwork/fulllogo-280.png", "Description": "Obscenely fast! Built with only fast, clean, code-first and light-weight parts. Start using .NET's fastest serializers, ORMs, redis and caching libraries!", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "layoric", "OwnerId": "1", "Tiers": ["App", "Web", "Client"] }, { "TechnologyId": 4, "TechnologyStackId": 1, "Tier": "Data", "Id": 2, "Name": "PostgreSQL", "VendorName": "PostgreSQL", "LogoUrl": "http://www.myintervals.com/blog/wp-content/uploads/2011/12/postgresql-logo1.png", "Description": "The world's most advanced open source database.", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "ExampleUser", "OwnerId": "1", "Tiers": ["Data"] }, { "TechnologyId": 3, "TechnologyStackId": 1, "Tier": "Data", "Id": 3, "Name": "RavenDB", "VendorName": "RavenDB", "LogoUrl": "http://static.ravendb.net/logo-for-nuget.png", "Description": "Open source 2nd generation document DB", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "ExampleUser", "OwnerId": "1", "Tiers": ["Data"] }, { "TechnologyId": 2, "TechnologyStackId": 1, "Tier": "Web", "Id": 4, "Name": "IIS", "VendorName": "Microsoft", "LogoUrl": "http://www.microsoft.com/web/media/gallery/apps-screenshots/Microsoft-App-Request-Routing.png", "Description": "Microsoft's web host", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "ExampleUser", "OwnerId": "1", "Tiers": ["Web"] }], "Id": 1, "Name": "Initial Stack", "Description": "Example stack11", "CreatedBy": "ExampleUser", "LastModifiedBy": "layoric", "LastModified": "2014-12-17T16:42:03.9192566", "Created": "0001-01-01T00:00:00.0000000", "OwnerId": "1", "Details": "asdasd" }] } 4 | var searchTechs = { "Offset": 0, "Total": 4, "Results": [{ "Id": 1, "Name": "ServiceStack", "VendorName": "ServiceStack", "LogoUrl": "https://github.com/ServiceStack/Assets/raw/master/img/artwork/fulllogo-280.png", "Description": "Obscenely fast! Built with only fast, clean, code-first and light-weight parts. Start using .NET's fastest serializers, ORMs, redis and caching libraries!", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "layoric", "OwnerId": "1", "Tiers": ["App", "Web", "Client"] }, { "Id": 2, "Name": "IIS", "VendorName": "Microsoft", "LogoUrl": "http://www.microsoft.com/web/media/gallery/apps-screenshots/Microsoft-App-Request-Routing.png", "Description": "Microsoft's web host", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "ExampleUser", "OwnerId": "1", "Tiers": ["Web"] }, { "Id": 3, "Name": "RavenDB", "VendorName": "RavenDB", "LogoUrl": "http://static.ravendb.net/logo-for-nuget.png", "Description": "Open source 2nd generation document DB", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "ExampleUser", "OwnerId": "1", "Tiers": ["Data"] }, { "Id": 4, "Name": "PostgreSQL", "VendorName": "PostgreSQL", "LogoUrl": "http://www.myintervals.com/blog/wp-content/uploads/2011/12/postgresql-logo1.png", "Description": "The world's most advanced open source database.", "LogoApproved": false, "CreatedBy": "ExampleUser", "LastModifiedBy": "ExampleUser", "OwnerId": "1", "Tiers": ["Data"] }] }; 5 | 6 | describe('tech controllers unit tests', function () { 7 | "use strict"; 8 | var $scope, 9 | $httpBackend, 10 | $timeout, 11 | allTechsRequestHandler, 12 | specificTechRequestHandler, 13 | createLatestTechCtrl, 14 | latestTechStacksRequestHandler, 15 | searchTechsRequestHandler, 16 | createTechCtrl; 17 | var techStacksRequestHandler; 18 | 19 | beforeEach(function(done) { 20 | module('testMod'); 21 | inject(function($rootScope, $injector) { 22 | var $controller = $injector.get('$controller'); 23 | $timeout = $injector.get('$timeout'); 24 | $scope = $rootScope.$new(); 25 | $httpBackend = $injector.get('$httpBackend'); 26 | allTechsRequestHandler = $httpBackend.when('GET', '/technology') 27 | .respond(allTechsResponse); 28 | specificTechRequestHandler = $httpBackend.when('GET', '/technology/1') 29 | .respond(specificTechResponse); 30 | latestTechStacksRequestHandler = $httpBackend.when('GET', '/techstacks/latest').respond(latestTechStacks); 31 | searchTechsRequestHandler = $httpBackend.when('GET', '/technology/search?NameContains=&DescriptionContains=') 32 | .respond(searchTechs); 33 | techStacksRequestHandler = $httpBackend.when('GET', '/technology/1/techstacks').respond(latestTechStacks); 34 | createLatestTechCtrl = function() { 35 | return $controller('latestTechsCtrl', { $scope: $scope }); 36 | }; 37 | createTechCtrl = function () { 38 | 39 | return $controller('techCtrl', { $scope: $scope, $routeParams: { techId: 1 } }); 40 | }; 41 | done(); 42 | }); 43 | }); 44 | 45 | afterEach(function() { 46 | $httpBackend.verifyNoOutstandingExpectation(); 47 | $httpBackend.verifyNoOutstandingRequest(); 48 | }); 49 | 50 | it('should have 4 techs on the scope at "techs"', function() { 51 | var myCtrl1 = createLatestTechCtrl(); 52 | $timeout.flush(); 53 | $httpBackend.flush(); 54 | expect(myCtrl1).toBeDefined(); 55 | expect($scope.techs).toBeDefined(); 56 | expect($scope.techs.length).toBe(4); 57 | }); 58 | 59 | it('should have specific tech on scope with Id of 1', function () { 60 | var myCtrl1 = createTechCtrl(); 61 | $httpBackend.flush(); 62 | expect(myCtrl1).toBeDefined(); 63 | expect($scope.tech).toBeDefined(); 64 | expect($scope.tech.Id).toBe(1); 65 | }); 66 | }); -------------------------------------------------------------------------------- /src/TechStacks/TechStacks.ServiceInterface/TechnologyServicesAdmin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ServiceStack; 6 | using ServiceStack.Configuration; 7 | using ServiceStack.OrmLite; 8 | using TechStacks.ServiceModel; 9 | using TechStacks.ServiceModel.Types; 10 | 11 | namespace TechStacks.ServiceInterface 12 | { 13 | [Authenticate] 14 | public class TechnologyServicesAdmin : Service 15 | { 16 | public IAppSettings AppSettings { get; set; } 17 | 18 | public TwitterUpdates TwitterUpdates { get; set; } 19 | 20 | private const int TweetUrlLength = 22; 21 | 22 | private string PostTwitterUpdate(string msgPrefix, List techIds, int maxLength) 23 | { 24 | var stackNames = Db.Column(Db.From() 25 | .Where(x => techIds.Contains(x.Id)) 26 | .Select(x => x.Name)); 27 | 28 | var sb = new StringBuilder(msgPrefix); 29 | for (int i = 0; i < stackNames.Count; i++) 30 | { 31 | var name = stackNames[i]; 32 | if (sb.Length + name.Length + 3 > maxLength) 33 | break; 34 | 35 | if (i > 0) 36 | sb.Append(","); 37 | 38 | sb.Append(" " + name); 39 | } 40 | 41 | return TwitterUpdates.Tweet(sb.ToString()); 42 | } 43 | 44 | public object Post(CreateTechnology request) 45 | { 46 | var slug = request.Name.GenerateSlug(); 47 | var existingTech = Db.Single(q => q.Name == request.Name || q.Slug == slug); 48 | if (existingTech != null) 49 | throw new ArgumentException("'{0}' already exists".Fmt(slug)); 50 | 51 | var tech = request.ConvertTo(); 52 | var session = SessionAs(); 53 | tech.CreatedBy = session.UserName; 54 | tech.Created = DateTime.UtcNow; 55 | tech.LastModifiedBy = session.UserName; 56 | tech.LastModified = DateTime.UtcNow; 57 | tech.OwnerId = session.UserAuthId; 58 | tech.LogoApproved = true; 59 | tech.Slug = slug; 60 | 61 | var id = Db.Insert(tech, selectIdentity: true); 62 | var createdTechStack = Db.SingleById(id); 63 | 64 | var history = createdTechStack.ConvertTo(); 65 | history.TechnologyId = id; 66 | history.Operation = "INSERT"; 67 | Db.Insert(history); 68 | 69 | Cache.FlushAll(); 70 | 71 | var postUpdate = AppSettings.EnableTwitterUpdates(); 72 | if (postUpdate) 73 | { 74 | var url = new ClientTechnology { Slug = tech.Slug }.ToAbsoluteUri(); 75 | PostTwitterUpdate( 76 | "Who's using #{0}? {1}".Fmt(tech.Slug.Replace("-", ""), url), 77 | Db.ColumnDistinct(Db.From() 78 | .Where(x => x.TechnologyId == tech.Id) 79 | .Select(x => x.TechnologyStackId)).ToList(), 80 | maxLength: 140 - (TweetUrlLength - url.Length)); 81 | } 82 | 83 | return new CreateTechnologyResponse 84 | { 85 | Result = createdTechStack, 86 | }; 87 | } 88 | 89 | public object Put(UpdateTechnology request) 90 | { 91 | var tech = Db.SingleById(request.Id); 92 | if (tech == null) 93 | throw HttpError.NotFound("Tech not found"); 94 | 95 | var session = SessionAs(); 96 | var authRepo = HostContext.AppHost.GetAuthRepository(Request); 97 | using (authRepo as IDisposable) 98 | { 99 | if (tech.IsLocked && !(tech.OwnerId == session.UserAuthId || session.HasRole(RoleNames.Admin, authRepo))) 100 | throw HttpError.Unauthorized( 101 | "This Technology is locked and can only be modified by its Owner or Admins."); 102 | } 103 | 104 | //Only Post an Update if there was no other update today 105 | var postUpdate = AppSettings.EnableTwitterUpdates() 106 | && tech.LastStatusUpdate.GetValueOrDefault(DateTime.MinValue) < DateTime.UtcNow.Date; 107 | 108 | tech.PopulateWith(request); 109 | tech.LastModifiedBy = session.UserName; 110 | tech.LastModified = DateTime.UtcNow; 111 | 112 | if (postUpdate) 113 | tech.LastStatusUpdate = tech.LastModified; 114 | 115 | Db.Save(tech); 116 | 117 | var history = tech.ConvertTo(); 118 | history.TechnologyId = tech.Id; 119 | history.Operation = "UPDATE"; 120 | Db.Insert(history); 121 | 122 | Cache.FlushAll(); 123 | 124 | var response = new UpdateTechnologyResponse 125 | { 126 | Result = tech 127 | }; 128 | 129 | if (postUpdate) 130 | { 131 | var url = new ClientTechnology { Slug = tech.Slug }.ToAbsoluteUri(); 132 | response.ResponseStatus = new ResponseStatus 133 | { 134 | Message = PostTwitterUpdate( 135 | "Who's using #{0}? {1}".Fmt(tech.Slug.Replace("-", ""), url), 136 | Db.ColumnDistinct(Db.From() 137 | .Where(x => x.TechnologyId == tech.Id) 138 | .Select(x => x.TechnologyStackId)).ToList(), 139 | maxLength: 140 - (TweetUrlLength - url.Length)) 140 | }; 141 | } 142 | 143 | return response; 144 | } 145 | 146 | public object Delete(DeleteTechnology request) 147 | { 148 | var existingTech = Db.SingleById(request.Id); 149 | if (existingTech == null) 150 | throw HttpError.NotFound("Tech not found"); 151 | 152 | var session = SessionAs(); 153 | var authRepo = HostContext.AppHost.GetAuthRepository(Request); 154 | using (authRepo as IDisposable) 155 | { 156 | if (existingTech.OwnerId != session.UserAuthId && !session.HasRole(RoleNames.Admin, authRepo)) 157 | throw HttpError.Unauthorized("Only the Owner or Admins can delete this Technology"); 158 | } 159 | 160 | Db.DeleteById(request.Id); 161 | 162 | var history = existingTech.ConvertTo(); 163 | history.TechnologyId = existingTech.Id; 164 | history.LastModified = DateTime.UtcNow; 165 | history.LastModifiedBy = session.UserName; 166 | history.Operation = "DELETE"; 167 | Db.Insert(history); 168 | 169 | Cache.FlushAll(); 170 | 171 | return new DeleteTechnologyResponse 172 | { 173 | Result = new Technology { Id = (long)request.Id } 174 | }; 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/TechStacks/TechStacks/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | TechStacks 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 81 | 82 |
83 | 84 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 124 | 125 | 126 | --------------------------------------------------------------------------------