コード例 #1
0
        /// <summary>
        /// Get distinct, no-null list of deduc CacheIndexEntry for a given set of place ids
        /// </summary>
        /// <param name="placeIDs"></param>
        /// <returns></returns>
        private List <CfCacheIndexEntry> GetGeoDeducPlaces(IEnumerable <Guid> placeIDs)
        {
            var geoDeduciblePlaces = new List <CfCacheIndexEntry>();

            foreach (var id in placeIDs)
            {
                var place = CfCacheIndex.Get(id);
                if (place != null)
                {
                    //-- Have to add if check for opinions of provinces bug
                    if (place.Type == CfType.Province)
                    {
                        geoDeduciblePlaces.Add(place);
                    }
                    else
                    {
                        geoDeduciblePlaces.AddRange(CfPerfCache.GetGeoDeduciblePlaces(place));
                    }
                }
            }

            var feedPlaceIDs = geoDeduciblePlaces.Select(p => p.ID).Distinct().ToList();

            return((from c in feedPlaceIDs
                    where AppLookups.GetCacheIndexEntry(c) != null
                    select AppLookups.GetCacheIndexEntry(c)).ToList());
        }
コード例 #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public LocationOutdoor CreateLocationOutdoor(LocationOutdoor obj)
        {
            var user = CfPerfCache.GetClimber(CfIdentity.UserID);

            CreateLocationOutdoorAuthorization(obj);

            obj.NameUrlPart = obj.Name.ToUrlFriendlyString();

            locOutdoorRepo.Create(obj);
            var modPlaceDetail = objModMetaRepo.Create(new ObjectModMeta(obj, currentUser.UserID));

            //-- Update the cache
            AppLookups.AddIndexEntryToCache(obj.ToCacheIndexEntry());

            ClaimObject(obj);

            //-- Save post for the feed
            //PostService.CreateLocationCreatedPost(obj);

            var action = SaveModActionAndUpdateModProfile(ModActionType.LocationOutdoorAdd, null, obj, modPlaceDetail,
                                                          (m, actionID) => m.SetCreated(actionID), mp => mp.PlacesAdded++, "added {0} {1}", obj.Type, obj.Name);

            postSvc.CreateContentAddPost(action, obj.ID, user.PrivacyPostsDefaultIsPublic);

            //-- Shoot off a notification to moderators

            return(obj);
        }
コード例 #3
0
        public ActionResult Index()
        {
            var user = usrSvc.GetProfileByID(CfIdentity.UserID);

            ViewBag.User = user;

            CfCacheIndexEntry place = null;

            if (user.PlaceFavorite5.HasValue)
            {
                place = CfCacheIndex.Get(user.PlaceFavorite5.Value);
            }
            ViewBag.Place = place;

            var deducPlaces = new List <CfCacheIndexEntry>();

            if (place != null)
            {
                ViewBag.PartnerCalls = pcSvc.GetPlacesGeoDeducLatestPartnerCalls(place.ID, 25).ToList();

                if (place.Type.ToPlaceCateogry() != PlaceCategory.Area)
                {
                    deducPlaces = CfPerfCache.GetGeoDeduciblePlaces(place);
                }
            }
            ViewBag.DeducPlaces = deducPlaces;


            return(View());
        }
コード例 #4
0
        public ActionResult AddLocationPhotoMedia(Guid id, string mediaName)
        {
            if (!IsAuthenticated(ControllerContext.HttpContext))
            {
                return(new HttpStatusCodeResult(401, "Operation requires authenticated access"));
            }

            var l = new GeoService().GetLocationByID(id);

            if (l == default(Location))
            {
                return(new HttpStatusCodeResult(400, "Cannot add location media: Unrecognized location."));
            }

            var media = new Media();

            //-- If it already just
            if (l.HasAvatar)
            {
                media = SaveMediaFromStream(true, mediaName, l.ID);
            }
            else
            {
                media = new GeoService().SaveLocationAvatar(l, CurrentInputStream, null);
            }

            var by = CfPerfCache.GetClimber(CfIdentity.UserID);

            return(Json(new cf.Dtos.Mobile.V1.MediaDto(media, by)));
        }
コード例 #5
0
        private void InitializeEnvironmentCacheAndSearchProviders()
        {
            CfTrace.Current.Information(TraceCode.AppBuildSearchIndex, "Started {0:MMM dd HH.mm.ss}", DateTime.Now);

            if (!RoleEnvironment.IsAvailable)
            {
                CfCacheIndex.Initialize();
                CfPerfCache.Initialize();

                new SearchManager().BuildIndex(null);
                var directoryPath = AppDomain.CurrentDomain.BaseDirectory + @"App_Data\";
                var dir           = Lucene.Net.Store.FSDirectory.Open(new System.IO.DirectoryInfo(directoryPath));

                _siteSearchEngine = new CfLuceneIndexSearcher(dir);
            }
            else
            {
                CfCacheIndex.Initialize(new Level2MemcachedCacheIndex());
                CfPerfCache.Initialize(new Level2MemcachedPerfCache());


                var connectionString = ConfigurationManager.ConnectionStrings["CfCloudStorage"].ConnectionString;
                var cloudStorage     = CloudStorageAccount.Parse(connectionString);
                var dir = new Lucene.Net.Store.Azure.AzureDirectory(cloudStorage, "SearchCatalog", new RAMDirectory());
                _siteSearchEngine = new CfLuceneIndexSearcher(dir);

                lastIndexRefresh = DateTime.UtcNow;
            }

            CfTrace.Current.Information(TraceCode.AppBuildSearchIndex, "Ended {0:MMM dd HH.mm.ss}", DateTime.Now);
        }
コード例 #6
0
        public Message CheckOut(string id)
        {
            SvcContext ctx = InflateContext(); if (ctx.Invalid)

            {
                return(ctx.ContextMessage);
            }

            try
            {
                Guid ciID = Guid.ParseExact(id, "N");
                var  ci   = visitsSvc.GetCheckInById(ciID);

                //-- in case the visit has been deleted and we're trying to check out via the app.
                if (ci == null)
                {
                    return(ReturnAsJson(new VisitDto()));
                }
                else
                {
                    var co       = visitsSvc.CheckOut(ciID);
                    var location = CfPerfCache.GetLocation(co.LocationID);
                    return(ReturnAsJson(new VisitDto(co, location, CfIdentity.DisplayName, "")));
                }
            }
            catch (Exception ex)
            {
                return(Failed("Check out failed : " + ex.Message));
            }
        }
コード例 #7
0
        public Message GetMyPartnerCalls()
        {
            SvcContext ctx = InflateContext(); if (ctx.Invalid)
            {
                return(ctx.ContextMessage);
            }

            var calls = pcSvc.GetUsersLatestPartnerCalls(CfIdentity.UserID, 10);
            var dto   = new List <PartnerCallDto>();

            var user = CfPerfCache.GetClimber(CfIdentity.UserID);

            foreach (var c in calls)
            {
                //-- Here we use perf cache instead of Include()/join because it's likely the user will have revisited the same place
                var place = CfCacheIndex.Get(c.PlaceID);
                if (place != null)
                {
                    var callDto = new PartnerCallDto(place, c, user);
                    dto.Add(callDto);
                }
            }

            return(ReturnAsJson(dto));
        }
コード例 #8
0
        public Message GetTodayPartnerCallsForPlace(string id)
        {
            try
            {
                SvcContext ctx = InflateContext(); if (ctx.Invalid)
                {
                    return(ctx.ContextMessage);
                }

                Guid pID   = Guid.ParseExact(id, "N");
                var  calls = pcSvc.GetPlacesGeoDeducTodayPartnerCalls(pID);

                var dto = new List <PartnerCallDto>();

                foreach (var c in calls)
                {
                    //-- Here we use perf cache instead of Include()/join because it's likely the user will have revisited the same place
                    var place = CfCacheIndex.Get(c.PlaceID);
                    if (place != null)
                    {
                        var user    = CfPerfCache.GetClimber(c.UserID);
                        var callDto = new PartnerCallDto(place, c, user);
                        dto.Add(callDto);
                    }
                }

                return(ReturnAsJson(dto));
            }
            catch (Exception ex)
            {
                CfTrace.Error(ex);
                return(Failed("Partner search failed : " + ex.Message));
            }
        }
コード例 #9
0
        public Message GetUsersOpinions(string id)
        {
            SvcContext ctx = InflateContext(); if (ctx.Invalid)
            {
                return(ctx.ContextMessage);
            }
            Guid userID = Guid.ParseExact(id, "N");

            var dto = new List <OpinionDto>();

            var latestOpinions = new ContentService().GetUsersLatestOpinions(userID, 30);
            var profile        = CfPerfCache.GetClimber(userID);

            foreach (var m in latestOpinions)
            {
                var obj = AppLookups.GetCacheIndexEntry(m.ObjectID);
                if (obj != null)
                {
                    dto.Add(new OpinionDto(m.ID, m.Rating, m.Utc, obj.Name, m.UserID,
                                           profile.Avatar, m.Comment));
                }
            }

            return(ReturnAsJson(dto));
        }
コード例 #10
0
        private ClimbListDto GetClimbsOfLocation(CfCacheIndexEntry loc, DateTime dateTime)
        {
            if (loc == null)
            {
                throw new ArgumentNullException("GetClimbsOfLocation:loc");
            }

            //-- GetCurrentClimbsOfLocation is smart enough under the hood to distinguish for indoor & outdoor
            var placeCategory = loc.Type.ToPlaceCateogry();

            if (placeCategory == PlaceCategory.IndoorClimbing ||
                placeCategory == PlaceCategory.OutdoorClimbing)
            {
                if (dateTime > DateTime.UtcNow.AddHours(-24))
                {
                    return(CfPerfCache.TryGetFromCache <ClimbListDto>("mobindoorclimblist-" + loc.ID.ToString("N"),
                                                                      () => GetClimbListDto(loc, placeCategory, dateTime), CfPerfCache.SixtyMinCacheItemPolicy));
                }
                else
                {
                    return(GetClimbListDto(loc, placeCategory, dateTime));
                }
            }
            else
            {
                throw new ArgumentNullException("Service not invoked correctly - loc not valid place for operation");
            }
        }
コード例 #11
0
        public Message GetConversation(string id)
        {
            SvcContext ctx = InflateContext(); if (ctx.Invalid)

            {
                return(ctx.ContextMessage);
            }

            try
            {
                var userID       = Guid.ParseExact(id, "N");
                var conversation = new ConversationService().GetConversationByPartyIDs(userID, CfIdentity.UserID);;

                var me              = CfPerfCache.GetClimber(CfIdentity.UserID);
                var with            = CfPerfCache.GetClimber(userID);
                var conversationDto = new ConversationDetailDto(conversation, with, me);
                foreach (var m in conversation.Messages)
                {
                    conversationDto.Messages.Add(new ConversationMessageDto(m));
                }

                return(ReturnAsJson(conversationDto));
            }
            catch (Exception ex)
            {
                return(Failed("Message failed : " + ex.Message));
            }
        }
コード例 #12
0
        public Message LeaveOpinion(string id, string rating, string comment)
        {
            SvcContext ctx = InflateContext(); if (ctx.Invalid)

            {
                return(ctx.ContextMessage);
            }

            try
            {
                var objID = Guid.ParseExact(id, "N");
                var obj   = AppLookups.GetCacheIndexEntry(objID);

                var opinion = new ContentService().CreateOpinion(new Opinion()
                {
                    Comment  = comment,
                    ObjectID = obj.ID, Rating = byte.Parse(rating)
                }, obj.ID);

                var by  = CfPerfCache.GetClimber(CfIdentity.UserID);
                var dto = new OpinionDto(opinion, by);

                return(ReturnAsJson(dto));
            }
            catch (Exception ex)
            {
                return(Failed("Opinion failed : " + ex.Message));
            }
        }
コード例 #13
0
        public Message CommentOnPost(string id, string comment)
        {
            SvcContext ctx = InflateContext(); if (ctx.Invalid)

            {
                return(ctx.ContextMessage);
            }

            try
            {
                var postID = Guid.ParseExact(id, "N");
                var post   = postSvc.GetPostByID(postID);
                var com    = postSvc.CreateComment(post, comment);

                var profile = CfPerfCache.GetClimber(CfIdentity.UserID);

                var dto = new PostCommentDto(com, profile);

                return(ReturnAsJson(dto));
            }
            catch (Exception ex)
            {
                return(Failed("Comment failed : " + ex.Message));
            }
        }
コード例 #14
0
        public ActionResult AddClimbPhotoMedia(Guid id, string mediaName)
        {
            if (!IsAuthenticated(ControllerContext.HttpContext))
            {
                return(new HttpStatusCodeResult(401, "Operation requires authenticated access"));
            }

            var c = new GeoService().GetClimbByID(id);

            if (c == default(Climb))
            {
                return(new HttpStatusCodeResult(400, "Cannot add climb media: Unrecognized climb."));
            }

            var contentType = ControllerContext.HttpContext.Request.ContentType;

            var media = new Media();

            if (c.HasAvatar)
            {
                media = SaveMediaFromStream(c.Type != CfType.ClimbIndoor, mediaName, c.ID);
            }
            else
            {
                media = new GeoService().SaveClimbAvatar(c, CurrentInputStream, null);
            }

            var by = CfPerfCache.GetClimber(CfIdentity.UserID);

            return(Json(new cf.Dtos.Mobile.V1.MediaDto(media, by)));
        }
コード例 #15
0
        /// <summary>
        /// Create a new conversation when it is the first message
        /// </summary>
        /// <param name="toID"></param>
        /// <param name="excerpt"></param>
        /// <returns></returns>
        private Conversation CreateConversation(Guid toID, string excerpt)
        {
            var user = CfPerfCache.GetClimber(toID);

            if (user == null)
            {
                throw new ArgumentNullException("Cannot create conversation as no user exists for " + toID);
            }
            if (toID == CfIdentity.UserID)
            {
                throw new ArgumentNullException("Cannot start a conversation with yourself!");
            }


            var convo = new Conversation()
            {
                ID         = Guid.NewGuid(), PartyAID = CfIdentity.UserID, PartyBID = toID,
                StartedUtc = DateTime.UtcNow, LastActivityUtc = DateTime.UtcNow, LastSenderID = CfIdentity.UserID, LastExcerpt = excerpt
            };

            //-- conversation view allows us to track the "view" from a specific users perspective and (eventually) enforce if one user wishes to block another
            convo.ConversationViews.Add(new ConversationView( )
            {
                ID = Guid.NewGuid(), ConversationID = convo.ID, PartyID = toID, Status = (byte)ConversationStatus.Unread
            });
            convo.ConversationViews.Add(new ConversationView( )
            {
                ID = Guid.NewGuid(), ConversationID = convo.ID, PartyID = CfIdentity.UserID, Status = (byte)ConversationStatus.Read
            });

            return(convoRepo.Create(convo));
        }
コード例 #16
0
        public ActionResult UsersLoggedClimbs(Guid id)
        {
            var user = CfPerfCache.GetClimber(id);

            ViewBag.User         = user;
            ViewBag.LoggedClimbs = visitsSvc.GetUsersLoggedClimbs(user.ID).ToList();

            return(View());
        }
コード例 #17
0
        public IQueryable <PartnerCall> GetPlacesGeoDeducPartnerCalls(Guid placeID)
        {
            var geoDeduciblePlaces = new List <CfCacheIndexEntry>();

            geoDeduciblePlaces.AddRange(CfPerfCache.GetGeoDeduciblePlaces(CfCacheIndex.Get(placeID)));
            var placeIDs = geoDeduciblePlaces.Select(p => p.ID).Distinct().ToList();

            return(pcRepo.GetAll().Where(c => placeIDs.Contains(c.PlaceID) && !c.UserDeleted));
        }
コード例 #18
0
        public ActionResult Detail(Guid id)
        {
            var opinion = ctnSvc.GetOpinionByID(id);

            ViewBag.Opinion = opinion;
            ViewBag.Post    = new PostService().GetPostByID(opinion.ID);
            ViewBag.User    = CfPerfCache.GetClimber(opinion.UserID);

            return(View());
        }
コード例 #19
0
        private PostDto GetMobilePost(PostRendered p)
        {
            var post = new PostDto(p, CfCacheIndex.Get(p.PlaceID), p.UserDisplayName, p.UserAvatar);

            foreach (var c in p.PostComments)
            {
                var profile = CfPerfCache.GetClimber(c.UserID);
                post.Comments.Add(new PostCommentDto(c, profile));
            }
            return(post);
        }
コード例 #20
0
        public static MvcHtmlString ModProfileLink(this HtmlHelper helper, Guid userID)
        {
            if (userID == Stgs.SystemID)
            {
                return(new MvcHtmlString("System"));
            }

            var userProfile = CfPerfCache.GetClimber(userID);

            return(new MvcHtmlString(string.Format(@"<a href=""/Moderate/ActionUserList/{0}"">{1}</a>",
                                                   userID, userProfile.DisplayName)));
        }
コード例 #21
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="imgStream"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public List <string> GetHomePageProfileThumbs()
        {
            var profileThumbs = CfPerfCache.SessionGetFromCache <List <string> >("home-profiles-thumbs");

            if (profileThumbs == null)
            {
                profileThumbs = profileRepo.GetAll().Where(p => !string.IsNullOrEmpty(p.Avatar)).Select(p => p.Avatar).ToList();
                CfPerfCache.SessionAddToCache("home-profiles-thumbs", profileThumbs, CfPerfCache.SixtyMinTimeSpan);
            }

            return(profileThumbs);
        }
コード例 #22
0
        public Message GetLatestSends(string id)
        {
            Guid climbID = Guid.ParseExact(id, "N");
            var  logs    = visitsSvc.GetClimbsRecentSuccessfulLoggedClimbs(climbID, 25).ToList();
            var  dto     = new List <ClimbDetailSentClimbDto>();

            foreach (var l in logs.OrderByDescending(l => l.Utc))
            {
                var by = CfPerfCache.GetClimber(l.UserID);
                dto.Add(new ClimbDetailSentClimbDto(l, by));
            }
            return(ReturnAsJson(dto));
        }
コード例 #23
0
        public Message NewPartnerCall(string loc, string start, string end, string preferredLevel, string comment)
        {
            SvcContext ctx = InflateContext(); if (ctx.Invalid)

            {
                return(ctx.ContextMessage);
            }

            try
            {
                Guid locID         = Guid.ParseExact(loc, "N");
                var  place         = CfCacheIndex.Get(locID);
                var  startDateTime = DateTime.ParseExact(start, "dddMMMddyyyyhhmmtt", CultureInfo.InvariantCulture);
                var  endDateTime   = default(DateTime);
                if (end != "default")
                {
                    endDateTime = DateTime.ParseExact(end, "dddMMMddyyyyhhmmtt", CultureInfo.InvariantCulture);
                }

                var pc = pcSvc.CreatePartnerCall(new PartnerCall()
                {
                    Comment        = comment, StartDateTime = startDateTime,
                    EndDateTime    = endDateTime, ForIndoor = true, ForOutdoor = true, PlaceID = place.ID,
                    PreferredLevel = byte.Parse(preferredLevel)
                }
                                                 );

                var user = CfPerfCache.GetClimber(CfIdentity.UserID);

                var dto = new PartnerCallDto(place, pc, user);

                if (!pcSvc.UserHasSubscriptionForRelatedGeo(place.ID))
                {
                    pcSvc.CreatePartnerCallSubscription(new PartnerCallSubscription()
                    {
                        ForIndoor      = pc.ForIndoor,
                        ForOutdoor     = pc.ForOutdoor,
                        PlaceID        = pc.PlaceID,
                        EmailRealTime  = true,
                        MobileRealTime = false,
                        ExactMatchOnly = false
                    });
                }

                return(ReturnAsJson(dto));
            }
            catch (Exception ex)
            {
                return(Failed("PartnerCall failed : " + ex.Message));
            }
        }
コード例 #24
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="message"></param>
        public void ProcessCommentWorkItem(CommentAlertWorkItem comment)
        {
            var post            = new PostRepository().GetByID(comment.PostID);
            var userIDsOnThread = post.PostComments.Where(pc => pc.UserID != comment.ByID).Select(pc => pc.UserID).Distinct().ToList();
            var by = CfPerfCache.GetClimber(comment.ByID);

            var excerpt = comment.Content;

            if (excerpt.Length > 30)
            {
                excerpt = excerpt.Substring(0, 30);
            }
            var msg = string.Format("<a href='/climber/{0}'>{1}</a> made a <a href='/post/{2}'>comment</a><i> \"{3}...\"</i></a>",
                                    by.ID, by.DisplayName, comment.PostID, excerpt);

            //-- Also need to let the person who posted know incase they haven't commented on their own post or already part of the commentors
            if (by.ID != post.UserID && !userIDsOnThread.Contains(post.UserID))
            {
                userIDsOnThread.Add(post.UserID);
            }

            foreach (var uID in userIDsOnThread)
            {
                var settings = GetUserSiteSettings(uID);
                if (settings.CommentsOnPostsAlertFeed || settings.CommentsOnPostsEmailRealTime || settings.CommentsOnPostsMobileRealTime)
                {
                    var toUser = CfPerfCache.GetClimber(uID);

                    var a = new Alert()
                    {
                        ID = Guid.NewGuid(), Utc = DateTime.UtcNow, TypeID = (byte)AlertType.CommentOnPost, UserID = toUser.ID, Message = msg
                    };
                    if (settings.CommentsOnPostsAlertFeed)
                    {
                        a.ByFeed = true;
                    }
                    if (settings.CommentsOnPostsEmailRealTime)
                    {
                        a.ByEmail = true; MailMan.SendCommentEmail(toUser, by, comment.PostID, comment.Content);
                    }
                    if (settings.CommentsOnPostsMobileRealTime && !string.IsNullOrEmpty(settings.DeviceTypeRegistered))
                    {
                        a.ByMobile = true;
                        MobilePush_CommentAlert(comment.PostID, by.DisplayName, toUser.Email, settings.DeviceTypeRegistered);
                    }
                    new AlertRepository().Create(a);
                }
            }
        }
コード例 #25
0
        public static MvcHtmlString UserPicThumb(this HtmlHelper helper, Guid userID)
        {
            if (userID == Stgs.SystemID)
            {
                return(new MvcHtmlString("System"));
            }
            var userProfile = CfPerfCache.GetClimber(userID);

            if (userProfile == null)
            {
                return(new MvcHtmlString("User deleted"));
            }

            return(UserPicThumb(helper, userProfile.Avatar, userProfile.DisplayName));
        }
コード例 #26
0
        public static string UserDisplayName(this HtmlHelper helper, Guid userID)
        {
            if (userID == Stgs.SystemID)
            {
                return("System");
            }
            var userProfile = CfPerfCache.GetClimber(userID);

            if (userProfile == null)
            {
                return("User deleted");
            }

            return(userProfile.DisplayName);
        }
コード例 #27
0
        public Message GetLatestSendsByUser(string id)
        {
            Guid userID = Guid.ParseExact(id, "N");
            var  logs   = visitsSvc.GetUsersMostRecentLoggedClimbs(userID, 50).ToList();
            var  dto    = new List <ClimbDetailSentClimbDto>();
            var  by     = CfPerfCache.GetClimber(userID);

            foreach (var l in logs.OrderByDescending(l => l.Utc))
            {
                var send = new ClimbDetailSentClimbDto(l, by);
                send.By = l.ClimbName;
                dto.Add(send);
            }
            return(ReturnAsJson(dto));
        }
コード例 #28
0
        public void Initiazlize()
        {
            CfCacheIndex.Initialize();
            CfPerfCache.Initialize();

            var cIdentity = new ClaimsIdentity(new List <Claim>()
            {
                new Claim("http://climbfind.com/claims/userid", jsk.ToString())
            });
            var ids = new ClaimsIdentityCollection(new List <ClaimsIdentity>()
            {
                cIdentity
            });

            CfIdentity.Inject(new ClaimsPrincipal(ids));
        }
コード例 #29
0
        public static MvcHtmlString UserProfileLink(this HtmlHelper helper, Guid userID)
        {
            if (userID == Stgs.SystemID)
            {
                return(new MvcHtmlString("System"));
            }

            var userProfile = CfPerfCache.GetClimber(userID);

            if (userProfile == null)
            {
                return(new MvcHtmlString("User deleted"));
            }

            return(new MvcHtmlString(string.Format(@"<a href=""{0}"">{1}</a>", userProfile.SlugUrl, userProfile.DisplayName)));
        }
コード例 #30
0
        public ClimbOutdoor CreateClimbOutdoor(ClimbOutdoor obj, List <int> categories)
        {
            var user = CfPerfCache.GetClimber(CfIdentity.UserID);

            //-- The tricky part about this, it that using the Action/Lambda we call create with the ClimbOutdoor type argument
            var meta = CreateClimb(obj, categories, CfType.ClimbOutdoor, () => climbRepo.Create(obj));

            ClaimObject(obj); // note we only claim outdoor climbs, not indoor....

            var action = SaveModActionAndUpdateModProfile(ModActionType.ClimbAdd, null, (Climb)obj, meta,
                                                          (m, actionID) => m.SetCreated(actionID), mp => mp.ClimbsAdded++, "added climb {0}", obj.Name);

            postSvc.CreateContentAddPost(action, obj.LocationID, user.PrivacyPostsDefaultIsPublic);

            return(obj);
        }