October 2009 - Posts

Tänkte dela med mig en kod bit som ersätter en sträng till vänlig url sträng, ex: Om vårat arbete => om-varat-arbete

        public static string ConvertStringToSlug(string title)
        {
            if (String.IsNullOrEmpty(title)) return "";
            title = title.ToUpperInvariant();
            title = title.Replace("À", "a")
                 .Replace("Á", "a")
                 .Replace("Ä", "a")
                 .Replace("Å", "a")
                 .Replace("Â", "a")
                 .Replace("È", "e")
                 .Replace("É", "e")
                 .Replace("Ë", "e")
                 .Replace("Ê", "e")
                 .Replace("Ì", "i")
                 .Replace("Í", "i")
                 .Replace("Ï", "i")
                 .Replace("Î", "i")
                 .Replace("Ò", "o")
                 .Replace("Ò", "o")
                 .Replace("Ö", "o")
                 .Replace("Ô", "o")
                 .Replace("Ù", "u")
                 .Replace("Ú", "u")
                 .Replace("Ü", "u")
                 .Replace("Û", "u")
                 .Replace("Ñ", "n")
                 .Replace("Ç", "c")
                 .Replace("·", "-")
                 .Replace("/", "-")
                 .Replace("_", "-")
                 .Replace(",", "-")
                 .Replace(":", "-")
                 .Replace(";", "-")
                 .Trim();
 
            title = title.Replace("/[^a-zA-Z0-9 -]/g", "").Replace(" ", "-").ToLower();
            return title;
        }

 Nu så finns både Visual Studio 2010 Beta 2 och .Net 4.0 Beta 2 att ladda ner publikt.

http://www.microsoft.com/visualstudio/sv-se/try/default.mspx

 

I asp.net mvc är det lätt att skapa ett simpelt cms liknade system. Det första du ska göra är att skapa ett nytt asp.net mvc projekt. Du behöver inte inkludera något test projekt om du inte vill, för det kommer vi inte använda.

Det första vi gör när vi skapat ett nytt projekt är att lägga till en ny controller (i inlägget kallar vi denna: AdminController.cs)

När vi skapar en controller blir vi tillfrågade: “Add action methos for Create, Update, and Details scenarios”. Vi väljer att ta med detta, så vi klickar i kryssrutan.

 

1

Nu så behöver vi skapa en databas ,det gör vi genom att högerklicka på App_Data mappen i ditt webb projekt och välj Add –> New item, Sql Server database. Som du döper till något vettigt. (döpte min till MvcCms.mdf)

I databasen behöver vi lägga till en tabell med namnet: Page och som innehåller: Id(Int, räknare, nyckel), Title(Nvarchar(100)), Content(Nvarchar(max)).

I våran Models mapp i så skapar vi en ny ADO.NET Entity Data Model vid namn Page. (I codebehind sen blir det Pages, då den lägger på ett S)

I models mappen har jag även lagt till en klass kallad Page

    public class Page
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}

Nu ska vi göra en lista av alla sidor som finns i våran databas när man går till /Admin. Då ser det ut såhär i våran AdminController:

using MvcCms.Models;
        public ActionResult Index()
{
Entities ent = new Entities();
IEnumerable<Page> pages = (from f in ent.Pages
orderby f.Id descending
select new Page
{
Id = f.Id,
Title = f.Title,
Content = f.Content,
TitleUrl = f.TitleUrl,
});

return View(pages);

}

 

(Entities är namnet på min ado.net data modell). När vi gjort detta högerklickar vi på namnet Index() och väljer “Add View”.

2

View name är Index precis som i våran AdminController, Vi skapar också en “Create a strongly-typed view” med våran Page klass som list, Sen är det dina inställningar till masterpage och vilket ContentPlaceHolder ID.

Våran html upplägg ser nu ut såhär, jag gjorde några ändringar:

    <h2>
Index</h2>
<table>
<tr>
<th>
</th>
<th>
Id
</th>
<th>
Title
</th>
<th>
Content
</th>
<th>
TitleUrl
</th>
</tr>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%= Html.ActionLink("Edit", "Edit", new { id = item.Id }) %> |
<%--<%= Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ })%>--%>
</td>
<td>
<%= Html.Encode(item.Id) %>
</td>
<td>
<%= Html.ActionLink(item.Title, item.TitleUrl, "Pages") %>
</td>
<td>
<%= Html.Encode(item.Content) %>
</td>
<td>
<%= Html.Encode(item.TitleUrl) %>
</td>
</tr>
<% } %>
</table>
<p>
<%= Html.ActionLink("Create New", "Create") %>
</p>

Alla item i våran lista skrivs ut med Html.Encode() men vi ändrar den till en Html.ActionLink för våran title så det blir en klickbar länk genom att skriva

<%= Html.ActionLink(item.Title, item.TitleUrl, "Pages") %> 

Det mesta säger sig själv i våran actionlink. “Pages” är våran router som vi kommer skriva till i global.asax

I global.asax filen finns det redan ett upplägg för vad som ska vara första sidan.

Vi skapar en ny MapRoute innan “Default” MapRoute som ser ut så här:

            routes.MapRoute("Pages", "page/{TitleUrl}",
new { controller = "Home", action = "Page" },
new { TitleUrl = @"(.*)+" }
);

Vi skapar två stycken objekt, den första där vi bestämmer vilken kontroll och view vi ska använda. Sen skapar vi ett till objekt som vi bara skriver in TitleUrl i. “(.*)+” är som en valing string för regex som kan innehålla allt.

Så våran routing blir “page/{TitleUrl}” ex: localhost/page/om-oss. Det behöver inte vara “page” det kan stå “sidor” också. Men det är bra att inte använda åäö.

Vi kan nu återvända till AdminController där vi skapar en Create view från koden. Notera att det finns två stycken ActionResult Create, där vi ska använda den som inte har [AcceptVerbs(HttpVerbs.Post)]. Den ska vi använda senare.

3

Nu får vi massa html i våran Create.aspx men vi gör några ändringar så det ser ut såhär:

    <h2>Create</h2>

<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

<% using (Html.BeginForm()) {%>

<fieldset>
<legend>Fields</legend>
<p>
<label for="Title">Title:</label>
<%= Html.TextBox("Title") %>
<%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="Content">Content:</label>
<%= Html.TextArea("Content") %>
<%= Html.ValidationMessage("Content", "*") %>
</p>
<p>
<label for="TitleUrl">TitleUrl:</label>
<%= Html.TextBox("TitleUrl") %> <a href="javascript:void(0)" onclick="string_to_slug()">Gör vänlig url</a>
<%= Html.ValidationMessage("TitleUrl", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>

<% } %>

<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>

Vi skapar en vanlig a href för att anropa vårat javascript för att konvertera titeln till en vänlig url.

 

<a href="javascript:void(0)" onclick="string_to_slug()">Gör vänlig url</a>

För att skapa string_to_slug() så använder vi denna javascript kod: http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/

Som jag har skrivit om lite, så all javascript ser ut såhär på Create.apsx sidan:

    function string_to_slug() {
var str = $('#Title').val();
str = str.replace(/^\s+|\s+$/g, ''); // trim

// remove accents, swap ñ for n, etc
var from = "ÀÁÄÂÈÉËÊÌÍÏÎÒÓÖÔÙÚÜÛàáäâèéëêìíïîòóöôùúüûÑñÇç·/_,:;";
var to = "aaaaeeeeiiiioooouuuuaaaaeeeeiiiioooouuuunncc------";
for (var i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from[i], "g"), to[i]);
}

str = str.replace(/[^a-zA-Z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.toLowerCase();
insert_slug(str);
}

function insert_slug(str) {
$('#TitleUrl').val(str);
}
Notera att vi måste inkludera jquery på någon sida före denna kod, så jag la en länk på Site.master.
Nu kan vi börja skapa våran funktion som ska lägga till datan i databasen. Det är nu vi ska använda den andra ActionResult Create
        [AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string Title, string Content, string TitleUrl)
{
try
{
Entities ent = new Entities();

Pages page = new Pages();
page.Content = Content;
page.Title = Title;
page.TitleUrl = TitleUrl;

ent.AddToPages(page);
ent.SaveChanges();

return RedirectToAction("Index");
}
catch
{
return View();
}
}
public ActionResult Create(string Title, string Content, string TitleUrl) 
Var title, content, titleurl har precis samma namn som i våran Create.aspx. Entites är våran data modell och Pages är våran Page tabell. 
Sen när vi har sparat vår data så återvänder vi till admin index så ska vi kunna se våran nya sida högst upp.
4 

Nu så ska det se ut något såhär. Men vad vore ett cms utan ett sätt att kunna redigera sin sida? Så vi skapar en ny view för Edit precis som förut med Create.

I våran Edit.aspx ser det ut såhär:

    <h2>Edit</h2>

<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>

<% using (Html.BeginForm()) {%>

<fieldset>
<legend>Fields</legend>

<p>
<label for="Title">Title:</label>
<%= Html.TextBox("Title", Model.Title) %>
<%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="Content">Content:</label>
<%= Html.TextArea("Content", Model.Content) %>
<%= Html.ValidationMessage("Content", "*") %>
</p>
<p>
<label for="TitleUrl">TitleUrl:</label>
<%= Html.TextBox("TitleUrl", Model.TitleUrl) %> <a href="javascript:void(0)" onclick="string_to_slug()">Gör vänlig url</a>
<%= Html.ValidationMessage("TitleUrl", "*") %>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>

<% } %>

<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>

 

Jag inkluderar samma javascript som jag har Create.aspx också för man kan vilja ändra på sin url också.

Vi behöver hämta ut våra tre fält som vi vill kunna ändra. Title, Content och TitleUrl. Såhär ser koden ut för det:

        public ActionResult Edit(int id)
{
Entities ent = new Entities();
Page page = new Page();

page.Title = ent.Pages.Where(f => f.Id == id).Select(f => f.Title).First();
page.Content = ent.Pages.Where(f => f.Id == id).Select(f => f.Content).First();
page.TitleUrl = ent.Pages.Where(f => f.Id == id).Select(f => f.TitleUrl).First();

return View(page);
}
Precis som vid Create post action så har vi en Edit post action.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, string Title, string Content, string TitleUrl)
Men nu så har vi också Id med i våran post. Notera att koden här nedan ser lite annorlunda ut än den för Create.
Entities ent = new Entities();

Pages page = (from f in ent.Pages
where f.Id == id
select f).First();

page.Content = Content;
page.Title = Title;
page.TitleUrl = TitleUrl;

ent.SaveChanges();

return RedirectToAction("Index");
Där våran Pages modell plockar ut allt som har det id vi håller på att uppdatera och vi har ingen AddToPages(page) nu eftersom vi inte ska lägga till en ny sida utan uppdatera den.
Men för att kunna visa sidorna med våran välida urler ex: /page/min-sida så ska vi skapa en ny view i våran HomeController som heter Page.
Såhär ser ActionResult Page koden ut:
        public ActionResult Page(string TitleUrl)
{
if (!String.IsNullOrEmpty(TitleUrl))
{
return View();
}
else
{
return RedirectToAction("Index");
}
}

Där vi har ett id för att kunna visa något på sidan, om vi inte har det så skickar vi vidare förefrågan till startisdan.

5

Nu så ska “View content” vara Details för vi vill bara skriva ut datan från databasen. Om du ändrar namnet Page till något annat få du ändra Page i routing(global.asax filen) också.

I våran MainContent så behöver det bara se ut såhär:

    <h2>
<%= Html.Encode(Model.Title) %>
</h2>
<p>
        <%= Html.Encode(Model.Content) %>
</p>

Eftersom vi inte vill visa Id eller TitleUrl och vi vill att Titeln ska vara större än Content.

För att plocka ut Titeln och Content så lägger vi till denna kod innan för if(id != 0)

    Entities ent = new Entities();
Page page = new Page();

int id = Models.Page.GetIdByTitleUrl(TitleUrl);

page.Title = ent.Pages.Where(f => f.Id == id).Select(f => f.Title).First();
page.Content = ent.Pages.Where(f => f.Id == id).Select(f => f.Content).First();

return View(page);
Models.Page.GetIdByTitleUrl(string) är en funktion för att hämta ut id för den titelurl.
        public static int GetIdByTitleUrl(string TitleUrl)
{
Entities ent = new Entities();
return ent.Pages.Where(f => f.TitleUrl == TitleUrl).Select(f => f.Id).First();
}
På det här sättet kan du bygga ett simpelt cms. I detta inlägg har jag skrivit om hur du skapar nya sidor.
Hoppas ni gillar inlägget, det kanske kommer en del två på detta inlägg där jag kommer skriva om hur man väljer vilka sidor som ska vara med i menyn och hur man ändrar startsidan till sin egen sidan man just skapat.
Exempel på detta.
 

Nu finns det ett gäng förkorta lång adress till kort adress, t.ex. bit.ly. Jag byggde ihop en egen förkortning tjänst i asp.net mvc 1. Som jag nu har publicerat på CodePlex under Ms-Pl.

Jag har använt en sql express databas för att lätt spara adresser i. Sedan har jag använde WebService och jQuery för att skapa och komma åt den korta adressen.

Shorten

Dagens stora community sidor har oftas en funktion när man registrerar sig som kollar ifall det användarnamnet man vill använda är upptaget eller inte. T.ex. Twitter har en sådan funktion. Att göra en egen sådan funktion till sin webbapplikation är inte så svårt.

Det första vi gör är att skapa en WebService, som vi döper till MyService.asmx. När man skapar filen så är System.Web.Service.ScriptService bortkommeterat så det inte används, vi tar bort // framför så att vi kan använda den.

Sedan så kan vi skapa en public string som ser ut något sånt här. Tänk på att ni ska anropa era funktion som hämtar ut användarnamnet om det finns eller inte. Jag har gjort en förenklad version direkt i MyWebservice

    [WebMethod]
public string CheckUserName(string UserName) {
if (UserName == "Fredrik" || UserName == "Mikael")
return "Användarnamnet finns redan! Välj ett annat.";
else
return "Användarnamnet finns inte!";
}

 Där så ser vi ifall någon försöker att registrera en användare med namnet Fredrik eller Mikael så får dom ett meddelande tillbaks om att användarnamnet redan är upptaget. Men om det inte finns, så behöver man inte få ett meddelande tillbaks, men det är lättare för användaren att veta då.

Nu så ska vi försöka att anropa denna funktion för att ta reda på ifall användarnamnet är upptaget eller inte. Vi skapar helt enkelt en jQuery ajax anrop som ser ut såhär:

    function ShowAvailability() {
      $.ajax({
          type: "POST",
          url: "MyService.asmx/CheckUserName",
          data: '{UserName: "' + $(".username")[0].value + '" }',
          contentType: "application/json; charset=utf-8",
          dataType: "json",
          success: OnSuccess,
          failure: OnFailure,
        
     });

 Där har vi en url, där vi pekar till vart våran WebService fil ligger och vilken webservice vi ska anropa. På data så hämtar vi ut värdet som skriv i textboxen med css klassen .username. Sen har vi json datatypen och contenttypen. Success och Failure har jag skapat två likadana funktioner för som ser ut såhär:

function OnSuccess(response) {
       var mesg = $(".status")[0];
       mesg.innerHTML = response["d"];
        
      }
                    
function OnFailure(response) {
      var mesg = $(".status")[0];
      mesg.innerHTML = response["d"];
     }

 Där vi hämtar ut det vi får som json svar. Som ser ut såhär:

{"d":"Användarnamnet finns redan! Välj ett annat."}

Varför bokstaven d vet jag inte, men det ska ju vara något? Jag har sedan lagt till en span tag med css klassen .status för att visa det vi får som svar på OnSuccess eller OnFailure.

För att anropa ajax så skapar vi en a tag som ser ut såhär:

<a href="javascript:void(0)" onclick="ShowAvailability()">Kolla ifall användarnamnet är upptaget</a>

 Du kan ladda ner mitt exempel här

More Posts