Arquivo da categoria: Tecnologia da Informação

Adicionando Atributos em Propriedades (C#)

Adicionando Atributos em Propriedades (C#)

Nem sempre é possível utilizar objetos complexos (classes) para incluir informações em propriedades de classes. Essa situação pode acontecer quando se está dando manutenção em um código que está amarrado a outro comportamento que não pode ser alterado.

No caso que deu origem à esse código, a classe possui uma dezena de propriedades públicas string que já estavam atreladas a outras classes e comportamento em outras partes do código. Não havia liberdade de criação de outras classes que incorporassem mais propriedades, então decidi enfeitar cada propriedade com atributos personalizados.

Dessa forma, foi possível no código acessar mais informações com base somente nessa propriedade string.

O código foi usado em uma aplicação de aluguel de móveis para eventos. Segue o código:

Classe do Objeto amarrado (Furniture.cs) com as classes de atributos no mesmo arquivo (para facilitar o entendimento)

using System;
using System.ComponentModel;
using System.Globalization;
using static ADX18.Library.Helpers.ADX18Helper;
 
namespace ADX18.Library.Models
{
    public class Furniture : iTestObject
    {
        [DisplayName("Mode Cupboard"),
            MemberPrice("225.50"),
            NonMemberPrice("247.50")]
        public string ModeCupboard_Quantity { get; set; }
        public string ModeCupboard_Colour { get; set; }
        [DisplayName("Poly Voge Chair"),
            MemberPrice("52.25"),
            NonMemberPrice("55.00")]
        public string PolyVogue_Quantity { get; set; }
        public string PolyVogue_Colour { get; set; }
        [DisplayName("Poly Vogue Stool"),
            MemberPrice("74.25"),
            NonMemberPrice("80.85")]
        public string PolyVogueStool_Quantity { get; set; }
        public string PolyVogueStool_Colour { get; set; }
        [DisplayName("Club Stool"),
            MemberPrice("77.00"),
            NonMemberPrice("82.50")]
        public string ClubStool_Quantity { get; set; }
        public string ClubStool_Colour { get; set; }
        [DisplayName("Ikon Dry Bar"),
            MemberPrice("165.00"),
            NonMemberPrice("177.65")]
        public string IkonDryBar_Quantity { get; set; }
        public string IkonDryBar_Colour { get; set; }
        [DisplayName("Ikon Meeting Table"),
            MemberPrice("143.00"),
            NonMemberPrice("154.00")]
        public string IkonMeetingTable_Quantity { get; set; }
        public string IkonMeetingTable_Colour { get; set; }
        [DisplayName("Ikon Illuminated Dry Bar"),
            MemberPrice("192.50"),
            NonMemberPrice("209.00")]
        public string IkonIlluminatedDryBar_Quantity { get; set; }
        public string IkonIlluminatedDryBar_Colour { get; set; }
        [DisplayName("Mode Dry Bar Table"),
            MemberPrice("104.50"),
            NonMemberPrice("108.90")]
        public string ModeDryBarTable_Quantity { get; set; }
        public string ModeDryBarTable_Colour { get; set; }
        [DisplayName("Mode Meeting Table"),
            MemberPrice("99.00"),
            NonMemberPrice("107.25")]
        public string ModeMeetingTable_Quantity { get; set; }
        public string ModeMeetingTable_Colour { get; set; }
        [DisplayName("Mode Coffee Table"),
            MemberPrice("93.50"),
            NonMemberPrice("100.65")]
        public string ModeCoffeeTable_Quantity { get; set; }
        public string ModeCoffeeTable_Colour { get; set; }
        [DisplayName("Cuba Lounge One Seater"),
            MemberPrice("165.00"),
            NonMemberPrice("189.75")]
        public string CubaLoungeOneSeater_Quantity { get; set; }
        public string CubaLoungeOneSeater_Colour { get; set; }
        [DisplayName("Cuba Lounge One Seater Corner"),
            MemberPrice("165.00"),
            NonMemberPrice("189.75")]
        public string CubaLoungeOneSeaterCorner_Quantity { get; set; }
        public string CubaLoungeOneSeaterCorner_Colour { get; set; }
        [DisplayName("Cuba Lounge One Seater No Arm Rests"),
            MemberPrice("165.00"),
            NonMemberPrice("189.75")]
        public string CubaLoungeOneSeaterNoArms_Quantity { get; set; }
        public string CubaLoungeOneSeaterNoArms_Colour { get; set; }
        [DisplayName("Cuba Lounge"),
            MemberPrice("275.00"),
            NonMemberPrice("308.00")]
        public string CubaLounge_Quantity { get; set; }
        public string CubaLounge_Colour { get; set; }
        [DisplayName("Quattro Display Plinth 600mm"),
            MemberPrice("165.00"),
            NonMemberPrice("178.75")]
        public string QuattroDisplay600_Quantity { get; set; }
        public string QuattroDisplay600_Colour { get; set; }
        [DisplayName("Quattro Display Plinth 900mm"),
            MemberPrice("187.00"),
            NonMemberPrice("207.90")]
        public string QuattroDisplay900_Quantity { get; set; }
        public string QuattroDisplay900_Colour { get; set; }
        [DisplayName("Zig Zag Brochure Stand"),
            MemberPrice("115.50"),
            NonMemberPrice("123.20")]
        public string ZigZagStand_Quantity { get; set; }
        public string ZigZagStand_Colour { get; set; }
        [DisplayName("Carpet Floor Tiles"),
            MemberPrice("247.50"), NonMemberPrice("287.10"),
            NonMemberUnitPrice("31.90"),
            MemberUnitPrice("27.50")]
        public string CarpetFloorTiles_Quantity { get; set; }
        public string CarpetFloorTiles_Colour { get; set; }
        [DisplayName("Raised Floor Tiles"),
            MemberPrice("940.50"),
            NonMemberPrice("1188.00"),
            NonMemberUnitPrice("132.00"),
            MemberUnitPrice("104.50")]
        public string RaisedFloorTiles_Quantity { get; set; }
        public string RaisedFloorTiles_Colour { get; set; }
 
    }
// Classes de Atributos personalizadas
    public class NonMemberUnitPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public NonMemberUnitPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
    public class NonMemberPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public NonMemberPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
    public class MemberUnitPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public MemberUnitPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
    public class MemberPrice : Attribute
    {
        private decimal value;
        public decimal Value => value;
        public MemberPrice(string number)
        {
            value = decimal.Parse(number, CultureInfo.InvariantCulture);
        }
    }
 
 
}

Código de acesso desses atributos listados como um Dictionary<string,object> pois os valores retornados em cada atributo podem variar:

using ADX18.Library.Models;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
 
namespace ADX18.Library.Helpers
{
    public static class ADX18Helper
    {
      #region Furniture Helpers
        public interface iField { }
        public interface iTestObject { }
        //tweaked from https://stackoverflow.com/questions/6637679/reflection-get-attribute-name-and-value-on-property 
        //and https://stackoverflow.com/questions/23329087/get-custom-attribute-from-specific-object-property-field
        public static Dictionary<string, object> GetAttributes<T>(this T source, Expression<Func<object>> field) where T : class
        {
            MemberExpression member = (MemberExpression)field.Body;
            if (member == null) { return null; }

            Dictionary<string, object> _dict = new Dictionary<string, object>();
            object[] propertyAttributes = typeof(T).GetProperty(member.Member.Name)
                .GetCustomAttributes(true);

            foreach (var propertyAttribute in propertyAttributes)
            {
                var attributeAttributes = propertyAttribute.GetType().GetProperties();
                foreach (var attributeAttribute in attributeAttributes)
                {
                    var propName = $"{propertyAttribute.GetType().Name}.{attributeAttribute.Name}";
                    if (!_dict.ContainsKey(propName) && attributeAttribute.Name != "TypeId")
                        _dict.Add(propName, attributeAttribute.GetValue(propertyAttribute, null));
                }
            }
            return _dict;
        }

        #endregion

    }
}

Com essas classes definidas, fica mais fácil acessar os atributos no Razor:

@model Furniture
@using ADX18.Library.Controllers
@using ADX18.Library.Models
@using ADX18.Library.Helpers;

                <div class="col-md-8">
                        @{
                            var modeCupboard = Model.GetAttributes<Furniture>(() => Model.ModeCupboard_Quantity);
                        }
                    <h5>@modeCupboard["DisplayNameAttribute.DisplayName"]</h5>
                    <p>
                        Available in four colours, this includes a lockable cupboard with shelves.<br />
                        L: 1200mm x W: 600mm x H: 1000mm
                    </p>
                    <div>
                        Member: <span class="memberPrice">$@modeCupboard["MemberPrice.Value"] each</span><br />
                        Non-member: <span class="memberPrice">$@modeCupboard["NonMemberPrice.Value"] each</span>
                    </div>
                    Quantity: <input type="text" name="ModeCupboard_Quantity" style="width: 10%;" />
                    <select name="ModeCupboard_Colour">
                        <option value="White">White</option>
                        <option value="Black">Black</option>
                        <option value="Red">Red</option>
                        <option value="Blue">Blue</option>
                    </select>
                </div>

Então, quando não puder mudar uma propriedade para incluir objetos complexos, use atributos personalizados!

Criando um sistema básico de Short Url (parte 2)

Criando um sistema básico de Short Url (parte 2)

Criando um sistema básico de Short Url (parte 2)

Depois de vários meses usando o sistema e tendo que atualizar a lista de links manualmente no XML via FTP, decidi que era hora de melhorar o sistema para que incluísse um banco de dados e um meio de incluir, editar e remover links online.

Portanto, melhorei o sistema e hoje está disponível em http://puul.ga

 

O código fonte (open source) está disponível no Github

Foram feitas várias melhorias no sistema, de forma a ser responsivo e fácil de usar. Mas seu visual e utilidades continuam simples. Cria-se um short link (mesmo conceito que bit.ly) que pode ser compartilhado em qualquer lugar, deixando o link curto e fácil de ser digitado.

O projeto continua sendo em C#, MVC, Entity Framework, Html5, Css3 e jQuery (Bootstrap)

Primeiramente, é necessário criar o objeto/classe que irá conter os dados do Link gerado:

using System;
using System.ComponentModel.DataAnnotations;

namespace Shorten_Urls.Models
{
    public class Url
    {
        public int Id { get; set; }
        [RegularExpression(@"^.{8,}$", ErrorMessage = "Minimum 8 characters required")]
        [Required(ErrorMessage = "Required")]
        [StringLength(30, MinimumLength = 8, ErrorMessage = "Invalid")]
        [Display(Name ="Shareable Url")]
        public string Src { get; set; }
        [Url]
        [Display(Name = "Redirects To")]
        public string Redirect { get; set; }
        public string UserId { get; set; }
        [Display(Name = "Created By")]
        public string Username { get; set; }
        //[DataType(DataType.Date)]
        //[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        [Display(Name = "Created On")]
        public DateTime CreatedOn { get; set; }
        //[DataType(DataType.Date)]
        //[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        [Display(Name = "Expires")]
        public DateTime? Expires { get; set; }
    }
}

Também é necessário criar o Repositório (ou Data Layer). Esse repositório permite o uso tanto de XML quanto SQL, sendo configurada a opção no

web.config
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.AspNet.Identity;
using System.Configuration;
using System.Data.Entity;

namespace Shorten_Urls.Models
{
    public class UrlRepository : IRepository<Url>
    {
        private XDocument _urls;
        private List<Url> urls;
        ApplicationDbContext _context;
        private readonly string DbProvider = ConfigurationManager.AppSettings["DbProvider"];
        private const string SQL = "Sql";
        private const string XML = "Xml";
        public bool needLastId { get { return DbProvider.Equals(XML); } }

        public List<Url> Urls { get { return urls; } }
        public int LastId
        { get { return urls.Last().Id; } }

        public UrlRepository()
        {
            urls = new List<Url>();

            if (DbProvider == XML)
            {
                _urls = MvcApplication.IO.Forwards;

                IEnumerable<XElement> rootDescendants = _urls.Descendants("url");
                foreach (XElement e in rootDescendants)
                    urls.Add(MapXElement(e));
            }
            else if (DbProvider == SQL)
            {
                _context = new ApplicationDbContext();
                urls = _context.Forwarders.ToList();
            }
        }

        public bool Exists(string src)
        {
            var yes = from y in urls where y.Src == src select y;
            if (yes.Count() > 0) return true;
            else return false;
        }

        public IEnumerable<Url> GetAll()
        {
            return urls;
        }

        public void Add(Url url)
        {
            urls.Add(url);
            if (DbProvider == XML)
                MvcApplication.IO.AddForward(url);
            else if (DbProvider == SQL)
            {
                _context.Forwarders.Add(url);
                _context.SaveChanges();
            }
        }
        public Url GetById(int Id)
        {
            Url e = (from url in urls where url.Id == Id select url).Single();
            if (DbProvider == SQL)
            {
                PopulateUsername(e);
            }
            return e;
        }
        public List<Url> GetByUserId(string userId)
        {
            List<Url> list = (from urlList in urls where urlList.UserId == userId select urlList).ToList();
            if (DbProvider == SQL)
            {
                foreach (Url url in list) PopulateUsername(url);
            }
            return list;
        }
        public Url GetBySrc(string src)
        {
            var e = (from url in urls where url.Src.Equals(src) select url);
            if (e.Count() == 0) return null;
            else {
                if (DbProvider == SQL)
                {
                    PopulateUsername(e.Single());
                }
                return e.Single();
            }
        }

        public void Remove(int id)
        {
            Url remove = (from url in urls where url.Id == id select url).Single();
            urls.Remove(remove);
            Remove(remove);
        }

        public void Remove(Url url)
        {
            urls.Remove(url);
            if (DbProvider == XML)
                MvcApplication.IO.RemoveForward(url);
            else if (DbProvider == SQL)
            {
                _context.Forwarders.Remove(url);
                _context.SaveChanges();
            }
        }

        public void Remove(string src)
        {
            Url remove = (from url in urls where url.Src == src select url).Single();
            urls.Remove(remove);
            Remove(remove);
        }

        public void RemoveAll()
        {
            urls.Clear();
        }

        public void RemoveByUserId(string userId)
        {
            Url remove = (from url in urls where url.UserId == userId select url).Single();
            urls.Remove(remove);
            Remove(remove);
        }

        public void Update(Url url)
        {
            Url old = (from urlE in urls where urlE.Id == url.Id select urlE).Single();
            urls.Remove(old);
            urls.Add(url);
            if (DbProvider == XML)
            {

                MvcApplication.IO.RemoveForward(old);
                MvcApplication.IO.AddForward(url);
            }
            else if (DbProvider == SQL)
            {
                _context.Entry(url).State = EntityState.Modified;
                _context.SaveChanges();
            }
        }


        private Url PopulateUsername(int id)
        {
            Url url = (from u in urls where u.Id == id select u).Single();
            string userId = url.UserId == null ? "" : url.UserId;
            UserRepository userRepo = new UserRepository();
            url.Username = userRepo.GetUsername(userId);
            return url;
        }
        private Url PopulateUsername(Url url)
        {
            UserRepository userRepo = new UserRepository();
            string userId = url.UserId == null ? "" : url.UserId;
            url.Username = userRepo.GetUsername(userId);
            return url;
        }
        private Url MapXElement(XElement element)
        {
            DateTime expires = element.Attribute("expires").Value == "" ? new DateTime(2999, 12, 31) : DateTime.Parse(element.Attribute("expires").Value);
            DateTime createdOn = element.Attribute("created").Value == "" ? DateTime.Now : DateTime.Parse(element.Attribute("created").Value);
            string userId = element.Attribute("userid").Value;
            int id = int.Parse(element.Attribute("id").Value);
            Url url = new Url
            {
                Id = id,
                CreatedOn = createdOn,
                Expires = expires,
                UserId = userId,
                Redirect = element.Value.Trim(),
                Src = element.Attribute("src").Value
            };
            return PopulateUsername(url);
        }
    }
}

A parte mais importante do projeto é o Controller que vai servir os Links, e as rotas de leitura dos links. Portanto, o

RouteConfig.cs

também tem que estar correto:

ForwardsController.cs
using Shorten_Urls.Models;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;

namespace Shorten_Urls.Controllers
{
    public class ForwardsController : Controller
    {
        private UrlRepository _repo;
        private UserRepository _usersRepo;
        private ApplicationUser user;

        public ForwardsController()
        {
            _repo = new UrlRepository();
            _usersRepo = new UserRepository();
        }
        //
        // GET: /Forwards/

        public ActionResult SendToSrc(string src)
        {
            string error404 = "404.html";
            string redirect = "/Home/Index";

            Url url = _repo.GetBySrc(src);
            if (url != null)
            {

                if (!url.Redirect.Equals(string.Empty) && (url.Expires > DateTime.Now || url.Expires == null))
                    redirect = url.Redirect;
                else
                    redirect = error404;
            }
            return Redirect(redirect);
        }
        [Authorize]
        public ActionResult List()
        {

            List<Url> urls;
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            if (user.UserType == UserTypes.Administrator)
                urls = _repo.Urls;
            else urls = _repo.GetByUserId(user.Id);

            return View(urls);
        }

        //
        // GET: /Forwards/Details/5
        [Authorize]

        public ActionResult Details(int id)
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            if (id == 0)
                Redirect("/Home/Index");
            Url url = _repo.GetById(id);
            return View(url);
        }

        //
        // GET: /Forwards/Create
        [Authorize]

        public ActionResult Create()
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            Url url = new Url();
            string random = Helpers.GenerateRandomgUrl();
            while (_repo.Exists(random))
            {
                random = Helpers.GenerateRandomgUrl();
            }
            ViewBag.RandomUrl = random;
            return View(url);
        }

        [HttpGet]
        public string Available(string Src)
        {
            bool exists = _repo.Exists(Src);
            return new JavaScriptSerializer().Serialize(!exists);
        }
        //
        // POST: /Forwards/Create

        [Authorize]
        [HttpPost]
        public ActionResult Create(string Redirect, string Src, DateTime? Expires)
        {
            try
            {
                // TODO: Add insert logic here
                if (User != null)
                    user = _usersRepo.GetById(User.Identity.GetUserId());

                Url newUrl = new Url
                {
                    Redirect = Redirect,
                    Src = Src,
                    CreatedOn = DateTime.Now,
                    Expires = Expires,
                    UserId = user.Id,
                    Username = _usersRepo.GetUsername(user.Id)
                };

                if (_repo.needLastId)
                    newUrl.Id = _repo.LastId + 1;

                _repo.Add(newUrl);
                TempData["Success"] = "Short Url has been created";
                return RedirectToAction("List");
            }
            catch
            {
                TempData["Error"] = "There was an error saving your new Url";
                return View("List");
            }
        }

        //
        // GET: /Forwards/Edit/5
        [Authorize]

        public ActionResult Edit(int id)
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());

            Url editUrl = _repo.GetById(id);
            return View(editUrl);
        }

        //
        // POST: /Forwards/Edit/5
        [Authorize]
        [HttpPost]
        public ActionResult Edit(int Id, string Redirect, string Src, DateTime? Expires = null)
        {
            try
            {
                // TODO: Add update logic here
                if (User != null)
                    user = _usersRepo.GetById(User.Identity.GetUserId());

                Url editUrl = _repo.GetById(Id);
                editUrl.Redirect = Redirect;
                editUrl.Src = Src;
                editUrl.UserId = user.Id;
                editUrl.Expires = Expires;
                editUrl.Username = _usersRepo.GetUsername(user.Id);

                _repo.Update(editUrl);
                TempData["Success"] = "Short Url has been updated";
                return RedirectToAction("List");
            }
            catch
            {
                TempData["Error"] = "There was an error saving your Url";
                return View();
            }
        }

        //
        // GET: /Forwards/Delete/5
        [Authorize]
        public ActionResult Delete(int id)
        {
            if (User != null)
                user = _usersRepo.GetById(User.Identity.GetUserId());
            Url url = _repo.GetById(id);
            return View(url);
        }

        [Authorize]
        [HttpPost]
        public ActionResult Delete(int id, bool confirm)
        {
            try
            {
                if (User != null)
                    user = _usersRepo.GetById(User.Identity.GetUserId());

                _repo.Remove(id);
                TempData["Success"] = "Short Url has been deleted";
                return RedirectToAction("List");

            }
            catch
            {
                TempData["Error"] = "There was an error deleting your Url";
                Url url = _repo.GetById(id);
                return View(url);
            }
        }

    }
}
RouteConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Shorten_Urls
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Forward",
                url: "{src}",
                defaults: new { controller = "Forwards", action = "SendToSrc", src = "" }
                );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

Ao final, configura-se o

web.config

com as seguintes chaves:

 <appsettings>
    <add key="ForwardsFile" value="~/App_Data/forwards.xml"></add>
    <add key="LenghtOfRandomString" value="8"></add>
    <add key="WebsiteName" value="Puulga"></add>
    <add key="WebsiteUrl" value="http://puul.ga/"></add>
    <add key="Website" value="true"></add>
    <!--email provider section-->
    <add key="siteadmin" value="email@puul.ga"></add>
    <add key="email-subject" value="Email from Puul.ga"></add>
    <add key="email-from" value="email@puul.ga"></add>
    <add key="email-from-name" value="Puul.ga"></add>
    <add key="email-require-auth" value="false"></add>
    <add key="email-username" value="puul.ga"></add>
    <add key="email-password" value="password"></add>
    <add key="email-port" value="3535"></add>
    <add key="useSSL" value="false"></add>
    <add key="email-server" value="localhost"></add>
    <add key="email-html-share" value="~/Email Templates/share.html"></add>
    <add key="email-html-default" value="~/Email Templates/puulga.html"></add>
    <add key="email-html-forgotten" value="~/Email Templates/puulga.html"></add>
    <add key="email-html-confirmation" value="~/Email Templates/puulga.html"></add>
    <add key="email-html-welcome" value="~/Email Templates/puulga.html"></add>
    <!--Database options. possible values: Xml, Sql-->
    <add key="DbProvider" value="Sql"></add>
  </appsettings>

Basicamente, esses são os elementos mais importantes para o funcionamento do projeto. Todo o código fonte está disponível no Github em código aberto.

Visite o site para ver como funciona.

Módulo Smtp com modelo de Html

Módulo Smtp com modelo de Html

Módulo Smtp com modelo de Html

Todas as vezes que crio um novo projeto que precisa enviar emails acabo indo atrás de projetos anteriores e faço o famoso ctrl+C, ctrl+V mas recentemente cansei desse hábito horrendo e decidi criar uma biblioteca simples que pode ser referenciada em qualquer projeto.

Caso esteja com pressa, baixe a biblioteca aqui, ou o demo completo aqui.

Página do projeto no Github

Para testar o módulo, use Smtp4Dev.

Para começar, criei a classe que gerencia o SmtpClient e SmtpServer:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Web.Hosting;

namespace Simple_Mail
{
    #region Enums
    /// <summary>
    /// Types of Emails that can be sent
    /// </summary>
    public enum EmailType
    {
        Default,
        Confirmation,
        Forgotten,
        Welcome,
        Error
    }
    /// <summary>
    /// Type of Application this module is being ran from
    /// </summary>
    public enum ApplicationType
    {
        Desktop,
        Web
    }
    #endregion

    #region Class
    /// <summary>
    /// Email Module Class
    /// </summary>
    public class Email
    {
        #region Public Members
        // <summary>
        /// The Email address of the Email Module Admin
        /// </summary>
        public string SITE_ADMIN { get; set; }
        /// <summary>
        /// Subject of the Email
        /// </summary>
        public string SUBJECT { get; set; }
        /// <summary>
        /// Body of the Email (default is html)
        /// </summary>
        public string BODY { get; set; }
        /// <summary>
        /// From Email Address (who's sending the email)
        /// </summary>
        public string FROM { get; set; }
        /// <summary>
        /// From Name (name of who is sending the email)
        /// </summary>
        public string FROM_NAME { get; set; }
        /// <summary>
        /// To Email Address (who is going to receive this email)
        /// </summary>
        public string TO { get; private set; }
        /// <summary>
        /// To Name (name of who is going to receive this email)
        /// </summary>
        public string TO_NAME { get; set; }
        /// <summary>
        /// Username of Smtp Service / Server
        /// </summary>
        public string USERNAME { get; private set; }
        /// <summary>
        /// Password for the smtp service / server
        /// </summary>
        public string PASSWORD { get; private set; }
        /// <summary>
        /// The port number for sending the email address (normal ports are 25, 587, 465)
        /// </summary>
        public int PORT { get; private set; }
        /// <summary>
        /// Whether to use SSL Encryption
        /// </summary>
        public bool USESSL { get; private set; }
        /// <summary>
        /// Uri of the Smtp Server
        /// </summary>
        public string SERVER { get; private set; }
        /// <summary>
        /// Whether the smtp server requires authentication
        /// </summary>
        public bool REQUIREAUTH { get; private set; }
        /// <summary>
        /// Name of the Website or App running the Module
        /// </summary>
        public string WEBSITE_NAME { get; set; }
        /// <summary>
        /// Default Html Body for the Email
        /// </summary>
        public string HTML_DEFAULT { get; private set; }
        /// <summary>
        /// Html body for the Forgotten Email
        /// </summary>
        public string HTML_FORGOTEN { get; private set; }
        /// <summary>
        /// Html Body for the Confirmation Email
        /// </summary>
        public string HTML_CONFIRMATION { get; private set; }
        /// <summary>
        /// Html Body for the Welcome Email
        /// </summary>
        public string HTML_WELCOME { get; private set; }
        /// <summary>
        /// Html for the Social Share
        /// </summary>
        public string HTML_SHARE_PATH { get; private set; }
        /// <summary>
        /// Path for the Default Html File
        /// </summary>
        public string HTML_DEFAULT_PATH { get; private set; }
        /// <summary>
        /// Path for the Forgotten Html File
        /// </summary>
        public string HTML_FORGOTEN_PATH { get; private set; }
        /// <summary>
        /// Path for the Confirmation Html File
        /// </summary>
        public string HTML_CONFIRMATION_PATH { get; private set; }
        /// <summary>
        /// Path for the Welcome Html File
        /// </summary>
        public string HTML_WELCOME_PATH { get; private set; }
        /// <summary>
        /// Path for the Social Share Html File
        /// </summary>
        public string HTML_SHARE { get; private set; }
        #endregion

        #region Constructors
        /// <summary>
        /// Manually configures a Smtp Server Instance (when you do not want to use config files)
        /// </summary>
        /// <param name="smtpServer">The Uri for the smtp server</param>
        /// <param name="smtpPort">The Smtp Port</param>
        /// <param name="useAuth">Whether the Smtp Server Requires Authentication</param>
        /// <param name="username">The Smtp Username</param>
        /// <param name="password">The Smtp Password</param>
        /// <param name="useSSL">Whether to Use SSL Encryption</param>
        public Email(string smtpServer, int smtpPort, bool useAuth = false, string username = "", string password = "", bool useSSL = false)
        {
            SERVER = smtpServer;
            PORT = smtpPort;
            REQUIREAUTH = useAuth;
            USERNAME = username;
            PASSWORD = password;
            USESSL = useSSL;
        }
        /// <summary>
        /// Instanciates a new Email Module based on Config Files and Application Type (Web or Desktop)
        /// </summary>
        /// <param name="type"></param>
        public Email(ApplicationType type)
        {
            switch (type)
            {
                case ApplicationType.Desktop:
                    HTML_CONFIRMATION_PATH = ConfigurationManager.AppSettings["email-html-confirmation"];
                    HTML_WELCOME_PATH = ConfigurationManager.AppSettings["email-html-welcome"];
                    HTML_FORGOTEN_PATH = ConfigurationManager.AppSettings["email-html-forgotten"];
                    HTML_DEFAULT_PATH = ConfigurationManager.AppSettings["email-html-default"];
                    HTML_SHARE_PATH = ConfigurationManager.AppSettings["email-html-share"];
                    break;
                case ApplicationType.Web:
                    HTML_CONFIRMATION_PATH = HostingEnvironment.MapPath(ConfigurationManager.AppSettings["email-html-confirmation"]);
                    HTML_WELCOME_PATH = HostingEnvironment.MapPath(ConfigurationManager.AppSettings["email-html-welcome"]);
                    HTML_FORGOTEN_PATH = HostingEnvironment.MapPath(ConfigurationManager.AppSettings["email-html-forgotten"]);
                    HTML_DEFAULT_PATH = HostingEnvironment.MapPath(ConfigurationManager.AppSettings["email-html-default"]);
                    HTML_SHARE_PATH = HostingEnvironment.MapPath(ConfigurationManager.AppSettings["email-html-share"]);
                    break;
                default:
                    break;
            }


            HTML_CONFIRMATION = GetEmailHtml(HTML_CONFIRMATION_PATH);
            HTML_WELCOME = GetEmailHtml(HTML_DEFAULT_PATH);
            HTML_FORGOTEN = GetEmailHtml(HTML_FORGOTEN_PATH);
            HTML_DEFAULT = GetEmailHtml(HTML_WELCOME_PATH);
            HTML_SHARE = GetEmailHtml(HTML_SHARE_PATH);


            WEBSITE_NAME = ConfigurationManager.AppSettings["WebsiteName"];

            SITE_ADMIN = ConfigurationManager.AppSettings["siteadmin"];

            SERVER = ConfigurationManager.AppSettings["email-server"];
            PORT = int.Parse(ConfigurationManager.AppSettings["email-port"]);
            USESSL = bool.Parse(ConfigurationManager.AppSettings["useSSL"]);
            PASSWORD = ConfigurationManager.AppSettings["email-password"];
            USERNAME = ConfigurationManager.AppSettings["email-username"];
            REQUIREAUTH = bool.Parse(ConfigurationManager.AppSettings["email-require-auth"]);

            TO_NAME = "New " + ConfigurationManager.AppSettings["WebsiteName"] + " Member";
            TO = ConfigurationManager.AppSettings["email-to"];
            FROM_NAME = ConfigurationManager.AppSettings["email-from-name"];
            FROM = ConfigurationManager.AppSettings["email-from"];

            SUBJECT = ConfigurationManager.AppSettings["email-subject"];


        }

        #endregion

        #region Private Members

        private EmailType emailMessageType;
        private string emailMessageHtml;
        private System.Net.Mail.SmtpClient SmtpServer;
        #endregion

        #region Private Methods
        private string GetEmailHtml(string path)
        {
            return File.ReadAllText(path);
        }

        private string GetEmailFilePath(EmailType type)
        {
            emailMessageType = type;

            switch (type)
            {
                case EmailType.Confirmation:
                    emailMessageHtml = HTML_CONFIRMATION;
                    break;
                case EmailType.Forgotten:
                    emailMessageHtml = HTML_FORGOTEN;
                    break;
                case EmailType.Welcome:
                    emailMessageHtml = HTML_WELCOME;
                    break;
                case EmailType.Default:
                    emailMessageHtml = HTML_DEFAULT;
                    break;
                default:
                    emailMessageHtml = "";
                    break;
            }
            return emailMessageHtml;
        }

        private bool SetupServer()
        {
            try
            {
                SmtpServer = new System.Net.Mail.SmtpClient();
                SmtpServer.Port = PORT;
                SmtpServer.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
                if (REQUIREAUTH)
                {
                    SmtpServer.UseDefaultCredentials = false;
                    SmtpServer.Credentials = new System.Net.NetworkCredential(USERNAME, PASSWORD);
                }
                else SmtpServer.UseDefaultCredentials = true;

                SmtpServer.Host = SERVER;
                SmtpServer.EnableSsl = USESSL;

                return true;
            }
            catch
            {

                return false;
            }
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Attempts to send an email
        /// </summary>
        /// <param name="type">The Type of Email Being Sent</param>
        /// <param name="custom">Whether to Customize the email</param>
        /// <param name="body">If customized, set the html body for the email</param>
        /// <param name="To">If customized, defines the TO field</param>
        /// <param name="ToName">If customized, defines the To name</param>
        /// <returns>Returns a KeyValuePair<bool,string> with success (true/false) and a message from the smtp server</returns>
        public KeyValuePair<bool, string> SendMail(EmailType type, bool custom = false, string body = "", string To = "", string ToName = "New Member")
        {

            if (SetupServer())
            {
                System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage();

                mail.Subject = SUBJECT;
                mail.From = new System.Net.Mail.MailAddress(FROM, FROM_NAME);
                mail.IsBodyHtml = true;

                if (ToName != "") TO_NAME = ToName;

                if (To == "") mail.To.Add(new System.Net.Mail.MailAddress(TO, TO_NAME));
                else mail.To.Add(new System.Net.Mail.MailAddress(To, TO_NAME));

                if (custom)
                    mail.Body = body;
                else
                {
                    BODY = GetEmailFilePath(type);
                    mail.Body = BODY;
                }

                try
                {
                    SmtpServer.Send(mail);
                    return new KeyValuePair<bool, string>(true, "Email sent succesfully");
                }
                catch (Exception ex)
                {
                    return new KeyValuePair<bool, string>(false, ex.Message);
                }
                finally
                {
                    SmtpServer.Dispose();
                }
            }
            else return new KeyValuePair<bool, string>(false, "Error Setting UP Email");

        }
        /// <summary>
        /// Manually Sets the Body for the Email Message
        /// </summary>
        /// <param name="message">The Html Body for the Email</param>
        public void SetBody(string message)
        {
            BODY = message;
        }
        #endregion

    } 
    #endregion
}

Também inclui algumas configurações no App.Config como modelo, que podem ser usadas no Web.Config caso não queira inserir os dados via código.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!--email provider section-->
    <add key="siteadmin" value="postmaster@simpleemail.com"/>
    <add key="email-subject" value="Email from Email Website"/>
    <add key="email-from" value="postmaster@puul.ga"/>
    <add key="email-from-name" value="Simple Email"/>
    <add key="email-require-auth" value="false"/>
    <add key="email-username" value="simpleemail"/>
    <add key="email-password" value=""/>
    <add key="email-port" value="3535"/>
    <add key="useSSL" value="false"/>
    <add key="email-server" value="localhost"/>
    <add key="email-html-share" value="~/Email Templates/share.html"/>
    <add key="email-html-default" value="~/Email Templates/default.html"/>
    <add key="email-html-forgotten" value="~/Email Templates/default.html"/>
    <add key="email-html-confirmation" value="~/Email Templates/default.html"/>
    <add key="email-html-welcome" value="~/Email Templates/default.html"/>
  </appSettings>
</configuration>

Mas como também havia interesse em mandar emails em html mais bonitos, usei um modelo do MailChimp™ para ficar mais apresentável. Inclui na pasta de “Email Templates”

<!--MAILCHIMP EMAIL TEMPLATE-->
<!--DON'T CHANGE THE *|VALUES|* AS THEY ARE REPLACED IN THE SERVER - CHANGE THE HTML, TEXT IS AUTOMATICALLY ADDED-->
<td align="center" valign="top" id="bodyCell" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;height: 100%;margin: 0;padding: 10px;width: 100%;border-top: 0;">
    <!-- BEGIN TEMPLATE // -->
						<!--[if gte mso 9]>
						<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
						<tr>
						<td align="center" valign="top" width="600" style="width:600px;">
                          <![endif]-->
                          <table border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;border: 0;max-width: 600px !important;">
                            <tbody><tr>
                                <td valign="top" id="templatePreheader" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #FAFAFA;border-top: 0;border-bottom: 0;padding-top: 9px;padding-bottom: 9px;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                    <tbody class="mcnTextBlockOuter">
                                        <tr>
                                            <td valign="top" class="mcnTextBlockInner" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">

                                                <table align="left" border="0" cellpadding="0" cellspacing="0" width="366" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                    <tbody><tr>

                                                        <td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-left: 18px;padding-bottom: 9px;padding-right: 0;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #656565;font-family: Helvetica;font-size: 12px;line-height: 150%;text-align: left;">

                                                            *|EMAIL_PREVIEW|*
                                                        </td>
                                                    </tr>
                                                </tbody></table>

                                                <table align="right" border="0" cellpadding="0" cellspacing="0" width="197" class="mcnTextContentContainer" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                    <tbody><tr>

                                                        <td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #656565;font-family: Helvetica;font-size: 12px;line-height: 150%;text-align: left;">
                                                        </td>
                                                    </tr>
                                                </tbody></table>

                                            </td>
                                        </tr>
                                    </tbody>
                                </table></td>
                            </tr>
                            <tr>
                                <td valign="top" id="templateHeader" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #FFFFFF;border-top: 0;border-bottom: 0;padding-top: 9px;padding-bottom: 0;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnImageBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                    <tbody class="mcnImageBlockOuter">
                                        <tr>
                                            <td valign="top" style="padding: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnImageBlockInner">
                                                <table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" class="mcnImageContentContainer" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                    <tbody><tr>
                                                        <td class="mcnImageContent" valign="top" style="padding-right: 9px;padding-left: 9px;padding-top: 0;padding-bottom: 0;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">

                                                            <table style="width: 564px;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcpreview-image-uploader"></table>

                                                        </td>
                                                    </tr>
                                                </tbody></table>
                                            </td>
                                        </tr>
                                    </tbody>
                                </table></td>
                            </tr>
                            <tr>
                                <td valign="top" id="templateBody" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #FFFFFF;border-top: 0;border-bottom: 2px solid #EAEAEA;padding-top: 0;padding-bottom: 9px;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                    <tbody class="mcnTextBlockOuter">
                                        <tr>
                                            <td valign="top" class="mcnTextBlockInner" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">

                                                <table align="left" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnTextContentContainer">
                                                    <tbody><tr>

                                                        <td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;">

                                                            <h1 style="display: block;margin: 0;padding: 0;color: #202020;font-family: Helvetica;font-size: 26px;font-style: normal;font-weight: bold;line-height: 125%;letter-spacing: normal;text-align: left;">

                                                                *|HEADER|*

                                                            </h1>

                                                            <p style="margin: 10px 0;padding: 0;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;">

                                                                *|FIRSTPARAGRAPH|*
                                                            </p>
                                                            <p style="margin: 10px 0;padding: 0;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;">
                                                                *|SECONDPARAGRAPH|*
                                                            </p>
                                                            <p style="margin: 10px 0;padding: 0;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #202020;font-family: Helvetica;font-size: 16px;line-height: 150%;text-align: left;">
                                                                If you need a bit of inspiration, you can 
                                                                <a href="http://inspiration.mailchimp.com" class="mc-template-link" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #2BAADF;font-weight: normal;text-decoration: underline;">
                                                                    see what other MailChimp users are doing
                                                                </a>, or <a href="http://mailchimp.com/resources/email-design-guide/" class="mc-template-link" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #2BAADF;font-weight: normal;text-decoration: underline;">
                                                                learn about email design
                                                            </a> 
                                                            and blaze your own trail.
                                                        </p>
                                                    </td>
                                                </tr>
                                            </tbody>
                                        </table>

                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
                <tr>
                    <td valign="top" id="templateFooter" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;background-color: #FAFAFA;border-top: 0;border-bottom: 0;padding-top: 9px;padding-bottom: 9px;"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnFollowBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                        <tbody class="mcnFollowBlockOuter">
                            <tr>
                                <td align="center" valign="top" style="padding: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnFollowBlockInner">
                                    <table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnFollowContentContainer" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                        <tbody><tr>
                                            <td align="center" style="padding-left: 9px;padding-right: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                <table border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnFollowContent">
                                                    <tbody><tr>
                                                        <td align="center" valign="top" style="padding-top: 9px;padding-right: 9px;padding-left: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                            <table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                <tbody><tr>
                                                                    <td align="center" valign="top" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                    <!--[if mso]>
                                    <table align="center" border="0" cellspacing="0" cellpadding="0">
                                    <tr>
                                        <![endif]-->

                                        <!--[if mso]>
                                        <td align="center" valign="top">
                                            <![endif]-->


                                            <table align="left" border="0" cellpadding="0" cellspacing="0" style="display: inline;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                <tbody><tr>
                                                    <td valign="top" style="padding-right: 10px;padding-bottom: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnFollowContentItemContainer">
                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnFollowContentItem" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                            <tbody><tr>
                                                                <td align="left" valign="middle" style="padding-top: 5px;padding-right: 10px;padding-bottom: 5px;padding-left: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                    <table align="left" border="0" cellpadding="0" cellspacing="0" width="" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                        <tbody><tr>

                                                                            <td align="center" valign="middle" width="24" class="mcnFollowIconContent" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                                <a href="http://www.twitter.com/" target="_blank" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><img src="http://cdn-images.mailchimp.com/icons/social-block-v2/color-twitter-48.png" style="display: block;border: 0;height: auto;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" height="24" width="24" class=""></a>
                                                                            </td>
                                                                            
                                                                            
                                                                        </tr>
                                                                    </tbody></table>
                                                                </td>
                                                            </tr>
                                                        </tbody></table>
                                                    </td>
                                                </tr>
                                            </tbody></table>

                                        <!--[if mso]>
                                        </td>
                                        <![endif]-->

                                        <!--[if mso]>
                                        <td align="center" valign="top">
                                            <![endif]-->


                                            <table align="left" border="0" cellpadding="0" cellspacing="0" style="display: inline;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                <tbody><tr>
                                                    <td valign="top" style="padding-right: 10px;padding-bottom: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnFollowContentItemContainer">
                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnFollowContentItem" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                            <tbody><tr>
                                                                <td align="left" valign="middle" style="padding-top: 5px;padding-right: 10px;padding-bottom: 5px;padding-left: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                    <table align="left" border="0" cellpadding="0" cellspacing="0" width="" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                        <tbody><tr>

                                                                            <td align="center" valign="middle" width="24" class="mcnFollowIconContent" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                                <a href="http://www.facebook.com" target="_blank" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><img src="http://cdn-images.mailchimp.com/icons/social-block-v2/color-facebook-48.png" style="display: block;border: 0;height: auto;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" height="24" width="24" class=""></a>
                                                                            </td>
                                                                            
                                                                            
                                                                        </tr>
                                                                    </tbody></table>
                                                                </td>
                                                            </tr>
                                                        </tbody></table>
                                                    </td>
                                                </tr>
                                            </tbody></table>

                                        <!--[if mso]>
                                        </td>
                                        <![endif]-->

                                        <!--[if mso]>
                                        <td align="center" valign="top">
                                            <![endif]-->


                                            <table align="left" border="0" cellpadding="0" cellspacing="0" style="display: inline;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                <tbody><tr>
                                                    <td valign="top" style="padding-right: 0;padding-bottom: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnFollowContentItemContainer">
                                                        <table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnFollowContentItem" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                            <tbody><tr>
                                                                <td align="left" valign="middle" style="padding-top: 5px;padding-right: 10px;padding-bottom: 5px;padding-left: 9px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                    <table align="left" border="0" cellpadding="0" cellspacing="0" width="" style="border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                        <tbody><tr>

                                                                            <td align="center" valign="middle" width="24" class="mcnFollowIconContent" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                                                                                <a href="http://mailchimp.com" target="_blank" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;"><img src="http://cdn-images.mailchimp.com/icons/social-block-v2/color-link-48.png" style="display: block;border: 0;height: auto;outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;" height="24" width="24" class=""></a>
                                                                            </td>
                                                                            
                                                                            
                                                                        </tr>
                                                                    </tbody></table>
                                                                </td>
                                                            </tr>
                                                        </tbody></table>
                                                    </td>
                                                </tr>
                                            </tbody></table>

                                        <!--[if mso]>
                                        </td>
                                        <![endif]-->

                                    <!--[if mso]>
                                    </tr>
                                    </table>
                                    <![endif]-->
                                </td>
                            </tr>
                        </tbody></table>
                    </td>
                </tr>
            </tbody></table>
        </td>
    </tr>
</tbody></table>

</td>
</tr>
</tbody>
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnDividerBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;table-layout: fixed !important;">
<tbody class="mcnDividerBlockOuter">
    <tr>
        <td class="mcnDividerBlockInner" style="min-width: 100%;padding: 10px 18px 25px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
            <table class="mcnDividerContent" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width: 100%;border-top-width: 2px;border-top-style: solid;border-top-color: #EEEEEE;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                <tbody><tr>
                    <td style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
                        <span></span>
                    </td>
                </tr>
            </tbody></table>
<!--            
                <td class="mcnDividerBlockInner" style="padding: 18px;">
                <hr class="mcnDividerContent" style="border-bottom-color:none; border-left-color:none; border-right-color:none; border-bottom-width:0; border-left-width:0; border-right-width:0; margin-top:0; margin-right:0; margin-bottom:0; margin-left:0;" />
            -->
        </td>
    </tr>
</tbody>
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
<tbody class="mcnTextBlockOuter">
    <tr>
        <td valign="top" class="mcnTextBlockInner" style="mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">

            <table align="left" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width: 100%;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;" class="mcnTextContentContainer">
                <tbody><tr>

                    <td valign="top" class="mcnTextContent" style="padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;word-break: break-word;color: #656565;font-family: Helvetica;font-size: 12px;line-height: 150%;text-align: center;">

                        <em>Copyright &copy; *|CURRENT_YEAR|* *|LIST:COMPANY|*, All rights reserved.</em>
                        <br>
                        <br>
                        <br>

                    </td>
                </tr>
            </tbody></table>

        </td>
    </tr>
</tbody>
</table></td>
</tr>
</tbody></table>
						<!--[if gte mso 9]>
						</td>
						</tr>
						</table>
						<![endif]-->
                        <!-- // END TEMPLATE -->
                    </td>

Para usar o módulo, instancia-se desta forma

            Email _email = new Email(ApplicationType.Web);
            string defaultHtml = _email.HTML_CONFIRMATION;
            string body = defaultHtml
                .Replace("*|EMAIL_PREVIEW|*", "This is a Simple Email")
                .Replace("*|HEADER|*", "Simple Email Website")
                .Replace("*|FIRSTPARAGRAPH|*", "The message below has been customized")
                .Replace("*|SECONDPARAGRAPH|*", Message)
                .Replace("*|WEBSITE_URL|*", Request.Url.ToString())
                .Replace("*|WEBSITE_NAME|*", _email.WEBSITE_NAME)
                .Replace("*|CURRENT_YEAR|*", DateTime.Now.Year.ToString());


            _email.SendMail(EmailType.Confirmation, true, body, Email);

Basicamente, está criado um módulo que pode ser reusado em outros projetos, sem o horrendo copy/paste. Claro, melhorias sempre podem ser feitas, por isso o projeto é open source e pode ser baixado do Github.

Para testar a funcionalidade, recomendo usar Smtp4Dev que é bastante prático e não requer nem instalação.

Alerta não Intrusivo

Como criar um Alerta Não Intrusivo

Veja como é fácil criar um alerta não Intrusivo, com algumas poucas linhas de código, criar um alerta que não atrapalha o usuário, e pode ser atualizado de várias formas. Neste exemplo, estou usando um arquivo javascript que contém os alertas (veja abaixo).

  1. Copie o Html que é bastante simples
  2. Copie o CSS que já está bastante completo, mas fique a vontade em modificar
  3. Copie o jQuery (que também pode ser modificado)
  4. Se quiser usar o método de alertas deste demo, copie os alertas
  5. Ou baixe o demo completo em zip

Para este demo, foram usados os seguintes recursos:

 

Veja em ação no demo

Html


<div id="alert">

<div id="close-alert">X</div>

        <span>
            <i class='fa fa-2x' id="alert-icon"></i>
        </span>
        <span id="alert-message"></span>
    </div>

CSS

 #alert {
     z-index: 999;
     position: fixed;
     left: 0;
     width: 100%;
     height: 60px;
     text-align: center;
     padding: 12px 0 30px 0;
     background-color: #000;
     opacity: 0.6;
     color: #fff;
     text-shadow: 0 0 2px #ccc;
     bottom: 0px;
     /*Mude aqui para definir se o 
     alerta vai ser encima ou no rodape; 
     use bottom para rodapé e top para cabeçalho*/
 }
 #alert #close-alert {
     border: 1px solid #000;
     position: absolute;
     right: 0px;
     top: 0;
     color: #fff;
     width: 32px;
     height: 32px;
     line-height: 32px;
     transition: all 0.2s ease;
     cursor: pointer;
 }
 #alert #close-alert:hover {
     background-color: #980000;
     border: 1px solid #fff;
 }
 #alert span {
     max-width: 80%;
 }
 #alert-icon{
     padding-right: 15px
 }
 #alert #alert-message a, #alert #alert-message a:visited{
    color:#c2c2c2;
    font-weight: 800;
    text-decoration: none;
    transition: all 0.2s ease-in;
}
#alert #alert-message a:hover{
    color:#fff;
    text-shadow:0 0 3px #ccc;
}

jQuery

$(function () {

  Rollnews(); //inicia a rolagem de alertas
  
  $("#close-alert").click(function(){
    $("#alert").fadeOut(); //fecha o alerta
  });

});
//Rolagem de alertas
function Rollnews(){
  var i=0;
  $("#alert-icon").addClass(news[i].icon);
  $("#alert-message").html(news[i].text);
  $("#alert-icon,#alert-message").fadeIn();
  setInterval(function(){
    var previous = i;
    i++;
    if(i>news.length-1){
      i = 0;
    }
    $("#alert-icon").removeClass(news[previous].icon).addClass(news[i].icon);
    $("#alert-message").html(news[i].text);
  },3*1000);
}

Alertas

var news = [
        {    
    text : "Este é o primeiro alerta com um &lt;a href='http://www.zueuz.com.br' target='_blank'&gt;Link&lt;/a&gt;", 
    icon : "fa-info-circle"
        },
        {
    text : "Este é o segundo alerta, com outra mensagem qualquer",
    icon : "fa-exclamation-circle"
        },
        {
    text : "Ainda mais um alerta, que pode ser uma mensagem, uma imagem, ou um tweet (tweets necessitam de outro plugin)",
    icon : "fa-bell-o"
        }];

O Problema de Tecnologia é a Cultura

O Problema de Tecnologia é a Cultura

Sua empresa ainda está com dificuldades de entregar seus projetos de tecnologia satisfatoriamente? Ainda passam do prazo, do orçamento ou tem baixa qualidade?

Sem dúvidas, o problema não está no uso da tecnologia, nem na capacidade dos seus técnicos e desenvolvedores de trabalhar mais rápido. O PROBLEMA ESTÁ NA CULTURA DE SUA EMPRESA!

A indústria de software teve tempo suficiente para amadurecer e solidificar os processos que são eficazes e promovem sucesso, visando resultados expressivos e a satisfação de todos os envolvidos.

Em quase todos os projetos de sucesso, são notáveis as características similares de processos. A característica mais importante é que esses projetos implementaram processos de Gerenciamento de Ciclo de Vida de Software, mais conhecido como ALM (Application Lifecycle Management).

O ALM compreende toda a vida de um software, desde sua idealização até o dia em que o mesmo é aposentado e deixa de ser usado. A noção de que um software continua a ser responsabilidade de quem o criou mesmo depois de entregue, muda completamente o compromisso e dedicação aplicados durante o desenvolvimento inicial. Saber que, mesmo depois de entregue, o desenvolvedor terá que continuar a trabalhar no mesmo código, faz com que ele pense duas vezes antes de entregar algo com baixa qualidade. Afinal, ele é quem terá que “consertarâEUR(TM) o bug depois.

Existem inúmeras outras vantagens em usar ALM:

  • Menores iterações, aumentando o número de entregas e diminuindo o tempo de resposta do cliente em avaliar o progresso.
  • Metas objetivas e curtas que mantém a moral e o comprometimento dos envolvidos.
  • Entrega e manutenção tornam-se um só processo, ou seja, um ciclo constante.
  • Menos desperdício; maior envolvimento e satisfação da equipe.

Quanto mais entregas, maior o retorno financeiro. Entío, por que o ALM ainda é tío ilusivo e utópico? O problema de implementar-se ALM no Brasil não é a tecnologia, nem capacidade dos desenvolvedores. Trata-se de algo cultural.

A cultura tecnológica no Brasil ainda é extremamente deficitária e infantil, mistificando a área como uma lâmpada mágica que quando esfregada concede três desejos que aparecem milagrosamente.

Aos alheios da área, quem trabalha com tecnologia sabe fazer de tudo e completa qualquer tarefa imediatamente. Consequentemente, não há noção de esforço ou complexidade e por isso não entende os custos de tais serviços.

Portanto, implementar ALM no Brasil é uma questío cultural e não tecnológica. Na metodologia ígil, as entregas são curtas, objetivas e rápidas. No entanto, não há planejamento em longo prazo. Cada iteração é planejada em si mesma, com seus objetivos e metas, sucessivamente. Para quem está acostumado com processos burocráticos e paternalistas, que exigem vários documentos de aprovação – documentação, especificação, assinados por vários gerentes -, a ideia de deixar que a equipe de desenvolvimento decida o que será entregue é assustadora.

Porém, desenvolvimento de software não é igual a outros projetos e deve ser encarado de forma mais abstrata, mais criativa e artística. Criação de software, apesar de fundamentalmente lógica, é uma arte.

Levando em consideração os fundamentos de ALM, como um processo delgado e fluído, torna-se extremamente necessário que esses paradigmas de caixa preta sejam desmistificados e superados. Somente dessa forma poderemos ver um crescimento concreto da área.

Por esse motivo, discutirei alguns dos princípios que ajudam í  implementação de um ciclo de vida de software eficaz:

  1. Em primeiro lugar, e talvez o mais importante, é aceitar que desenvolver softwares não é algo trivial e que os processos de sua empresa precisam mudar se deseja ter projetos concluídos com sucesso.
  2. Otimização do sistema, ao invés de otimização de indivíduos produz maior retorno e diminuição de desperdício.
  3. Projetos pequenos tem maior probabilidade de ter mais sucesso. Considere cada iteração como um pequeno projeto, entregue em cadência estável e frequente.
  4. Desvincule-se do paternalismo e necessidade de controle. Delegue a responsabilidade de auto-organização para a equipe (ou equipes). Permita que decidam como e quanto irão entregar em cada iteração.
  5. Prioridade deve ser sua prioridade. Não fique apagando incêndios!
  6. Melhoramento contínuo exige otimizações contínuas.
  7. Valorize seus recursos, especialmente os recursos humanos. O retorno do investimento no indivíduo é maior do que a quantidade de horas pagas pelos clientes.

Há outros pontos que podem ser mencionados e que estío diretamente relacionados í  implementação de processos ígeis (xtreme programming, scrum, etc.). No entanto, focarei nos pontos acima, que mostram-se como pontos fracos da área de desenvolvimento no país.

Desenvolver software não é algo trivial. Assim, os processos de sua empresa precisam mudar se deseja ter projetos concluídos com sucesso.

Como já mencionado anteriormente, nenhum software é codificado da mesma maneira. Blocos de código podem ser copiados, reutilizados e refatorados. No final, a solução nunca é igual a nenhuma outra. Ou seja, software personalizado responde í s necessidades específicas do negócio. De outra maneira, softwares de prateleira (pacotes fechados) seriam suficientes para suprir as necessidades do fluxo da empresa.

Por essa razío, quem desenvolve softwares torna-se perito nas regras de negócio da empresa e encontra maneiras de codificar essas regras de negócio, afim de automatizar o fluxo. Quem desenvolve torna-se especialista, tanto em programação, quanto no domínio do negócio de seus clientes.

Otimização do sistema, ao invés de otimização de indivíduos produz maior retorno e diminuição de desperdício.

É muito comum os funcionários serem avaliados pelo índice de utilização que apresentam. Se um desenvolvedor estiver com os braços atrás da cabeça, olhando para o teto, imediatamente presume-se que esteja livre, ou enrolando.

Essa é a forma mais errada de se avaliar uma equipe, a não ser que se espere estar numa linha de produção de automóveis.

Um desenvolvedor pode estar pensando em uma solução, pode estar criando fluxos, imaginando estruturas ou mesmo pode estar liberando o stress de ficar olhando para um monitor de computador por horas seguidas. Isso não significa que sua capacidade e efetividade estejam comprometidas.

Da mesma forma, focar em cada indivíduo como um produtor unitário, diminui grandemente a performance do sistema.

O Problema de Tecnologia é a CulturaExistem várias maneiras de se apresentar esse conceito, mas mencionarei o jogo de moedas como base para a discussão:

Esse exemplo ajuda a explorar a diferença entre otimização do indivíduo versus a otimização do sistema.

Quando um sistema depende do indivíduo para processar tarefas, a média de tempo de entrega é 60% maior do que em sistemas que ignoram o indivíduo e focam-se apenas no uso do próprio sistema.

íEUR primeira vista, pensa-se que haverá desperdício, pois, o indivíduo que estiver parado, esperando o sistema prosseguir, irá custar muito para a empresa. No entanto, nota-se que quando o fluxo do sistema é otimizado, a quantidade, a qualidade, a rapidez e a cadência de entregas aumenta; consequentemente, aumenta o lucro, mesmo com o aparente desperdício de utilização de indivíduos.

Por esse motivo, é muito mais importante entender e otimizar as engrenagens dos processos utilizados pela empresa, do que simplesmente aumentar a pressão nos indivíduos para que produzam mais e mais rápido.

Projetos pequenos têm maior probabilidade de ter sucesso. Considere cada iteração como um pequeno projeto, entregue em cadência estável e frequente.

Esse é outro paradigma que denigre o sucesso dos projetos. A maioria dos gestores e gerentes de projeto estío acostumados com os grandes blocos de entregas, que apresentam grandes fases de contrato e grande foco de atenção. Contudo, quando esses grandes blocos têm problemas, o mesmo fica perceptivelmente enorme, chegando até a causar o fracasso do mesmo.

Mas, em pequenos projetos, ou iterações, se houver falha, será de umas poucas semanas. Se houver sucesso, as entregas serío a cada poucas semanas. Entío, deixa-se de lado a noção de grandes entregas, para pequenas e constantes entregas. Com pequenas entregas, que acontecem a cada duas semanas, por exemplo, o cliente sempre verá novidades a cada duas semanas, de forma quase transparente, sem burocracia e sem preocupações.

Os ganhos, dessa forma, são abrangentes e agradam todos os stakeholders do projeto.

Desvincule-se do paternalismo e necessidade de controle. Delegue a responsabilidade de auto-organização para a equipe (ou equipes). Permita que decidam como e quanto irão entregar em cada iteração.

Relacionado ao ponto 2 (Otimização do sistema) o paternalismo e controle causam desinteresse, infelicidade e queda na qualidade e produtividade dos envolvidos. Claro que sempre existirão aqueles que trazem um ótimo currículo, mas não são o que vendem. Por isso, o processo de contratação deve ser meticuloso e embasado (analise de personalidade, validação do histórico do currículo, etc.).

Mas, quando uma equipe de qualidade é montada, deve-se confiar que farío o melhor trabalho possível. Deve-se confiar que farío as entregas do jeito correto, com a qualidade esperada e no tempo determinado.

Micro gerenciamento é algo que deteriora o relacionamento da empresa com os colaboradores. Assédio moral destrói a confiança, e o paternalismo pode criar um ambiente de controle.

E, sob pressão desnecessária, colaboradores acabam por sair e buscar melhores oportunidades.

Prioridade deve ser sua prioridade. Não fique apagando incêndios!

Filas são o pior inimigo de qualquer projeto. Mas, pior que filas são os subprojetos que aparecem de forma emergencial e acabam sendo feitos em detrimento de outras prioridades. Portanto, é necessário comprometer-se em manter as prioridades de forma a manter a lista de atividades fluindo da maneira como foram planejadas. Se alguma atividade emergencial aparece, deve ser negociada e planejada de forma a entrar na lista de atividades e não sobrescrevê-la. A forma mais fácil de avaliar a criticidade de uma emergência é calculando o prejuízo que essa emergência vai causar no projeto principal se o mesmo atrasar. Caso a emergência tenha um valor muito maior, entío é justificado (como rede caindo, erros que impedem de um comercio digital vender, etc.). Mas se a emergência se baseia em quem pediu, por exemplo, o prejuízo pode ser muito maior.

Melhoramento contínuo exige otimizações contínuas.

É impossível chegar í  conclusão de que não há mais otimizações a serem feitas. Sempre haverá novos colaboradores, novos projetos, mudanças do mercado, mudanças na tecnologia. As intempéries da tecnologia forçam o melhoramento contínuo. A filosofia da metodologia ígil, Scrum, Kanban, Storyboarding, levam ao melhoramento contínuo, í  análise granular dos processos e fluxos que comprazem o ciclo de vida dentro da empresa. Portanto, sempre que houver a impressão que tudo está funcionando bem, é porque está na hora de buscar melhorias.

Valorize seus recursos, especialmente os recursos humanos. O retorno do investimento no indivíduo é maior do que a quantidade de horas pagas pelos clientes.

É difícil reunir uma equipe eficaz e profissional, que seja capaz de funcionar com o mínimo de interação gerencial e que seja coesa suficientemente para fluir e entregar com qualidade e no prazo. Portanto, valorize seus colaboradores, valorize o feedback constante tanto dos clientes quanto dos recursos. Invista em um departamento de Recursos humanos. Invista em benefícios secundários para seus funcionários, como liberdade de horários, trabalho remoto, bí´nus, etc. Claro que ninguém é insubstituível, mas num mercado que está sempre em avanço, o desperdício de ter que treinar novos colaboradores constantemente pode trazer um prejuízo maior do que investir mais em manter satisfeitos e felizes os que ficam.

A implementação do ALM não tem muito a ver com tecnologia ou custo de investimento, mas sim com um paradigma cultural que perpetuou-se por mais de 50 anos e mostrou-se ineficaz para projetos tecnológicos. Essa transição não é fácil, mas tem profundos resultados em projetos de quaisquer tamanhos e orçamentos.

 

Carlos Augusto A. Casalicchio
Executivo de Tecnologia e Negócios
carlos.casalicchio@zueuz.com
(15) 99142-4586

 

Referências

CAPODAGLI, B., JACKSON, L.., The Disney Way, 2007, McGrall Hill, New York, 312 p.

COCKERELL, L., Creating Magic, 2008, DoubleDay, New York, 270 p.

___, Be Our Guest, 2001, Disney Institute, New York, 207 p.

NADER, G., A Magia do Império Disney – 3-ª Ed. 2012, 2012, Senac, Sío Paulo, 536 p.

___, O jeito Disney de encantar os clientes: do atendimento excepcional ao nunca parar de crescer e acreditar, Disney Institute; [prefácio de Michael D. Eisner; tradução Cristina Yamagami]. âEUR” Sío Paulo : Saraiva, 2011, 141 p.

Innovation and Storyboard, video disponível em:

http://www.youtube.com/watch?v=5QtZRJ5JZA4–  Acessado em 28/04/2014

SANTOS, Danilo S. dos, Baixa Qualidade no Atendimento ao Cliente como Fator de Insucesso nas Organizações, disponível em:

http://revistas.unijorge.edu.br/conexao/pdf/1.pdf–  Acessado em 29/04/2014

BORIS, B. , O problema da qualidade de Atendimento , A ísltima Instancia, Universo Online, 16/01/2014. Disponivel em:

http://ultimainstancia.uol.com.br/conteudo/colunas/68590/o+problema+da+qualidade+de+atendimento.shtml–  Acessado em 29/04/2014

PARKS, R., Disney Show & Tell, HR Magazine, 1998. Citação de Leon Rubis

____, Cinco dicas para tornar a sua empresa mais sustentável, Terra Online, Disponível em:

http://economia.terra.com.br/vida-de-empresario/cinco-dicas-paratornar-a-sua-empresa-maissustentavel,52ed970548b83410VgnVCM20000099cceb0aRCRD.html Acessado em 29/04/2014

____, Falta de profissionais qualificados é problema grave, diz Flávia Oliveira, Globo News, 29/10/2013, Disponível em:

http://g1.globo.com/globonews/noticia/2013/10/falta-de-profissionais-qualificados-e-problema-grave-diz-flaviaoliveira.html Acesso em 29/04/2014

PABLO, Importância Dos Recursos Humanos Nas Micro E Pequenas Empresas, Disponivel em:

http://www.rhportal.com.br/recursos-humanos/Importancia-DosRecursos-Humanos-Nas-Micro-E-Pequenas-Empresas.htm Acessado em 30/04/2014
___, Lessons Learned, PMBOK Reference, disponível em:

http://www2.cdc.gov/cdcup/library/pmg/implementation/ll_description.htm

CURLEE, W, GORDON, R., – Complexity Theory and Project Management, Disponível online em:

http://bit.ly/1K0dqA7

NANTES, R. Pesquisa de Clima Organizacional, disponível em:

http://www.endeavor.org.br/artigos/gente-gestao/ferramentas/como-implantar-uma-pesquisa-de-clima-organizacional

MARSLOW, Piramide de Marslow, Wikipedia, disponível em

http://pt.wikipedia.org/wiki/Ficheiro:Hierarquia_das_necessidades_de_Maslow.svg

___, Avaliação de Desempenho, Wikipedia, disponível em:

http://pt.wikipedia.org/wiki/Avalia%C3%A7%C3%A3o_de_desempenho

DOURADO, A, Orientação: Avaliação de desempenho; motivação e resultados, disponível em:

http://www.portaldoservidor.ba.gov.br/noticias/orientacao/orientacao-avaliacao-de-desempenho-motivacao-e-resultados

ARAísJO, A., Lesão por Esforço Repetitivo, InfoEscola, disponível em:

http://www.infoescola.com/doencas/lesao-por-esforco-repetitivo/

PMBOK-® Guide and Standards, Project Management Institute, Disponível em:

http://www.pmi.org/PMBOK-Guide-and-Standards.aspx

Guia de Avaliação ALM http://vsaralmassessment.codeplex.com/

Avaliação ALM http://www.microsoft.com/visualstudio/almcatalyst/Assessment/

Microsoft ALM http://www.visualstudio.com/en-us/explore/app-lifecycle-management-vs.aspx

Sobre ALM http://en.wikipedia.org/wiki/Application_lifecycle_management

Criando um widget de compartilhamento somente com CSS e SVG

Criando um widget de compartilhamento somente com CSS e SVG

Encher seu site de jquery (javascript) pode causar lentidão na hora de carregar. Pode-se argumentar que isso não é problema se mimificar o script. OK…. não é o ponto discutir performance neste artigo Criando um widget de compartilhamento somente com CSS e SVG.

Este artigo mostra como criar um widget de compartilhamento usando somente CSS3 e SVG. Claro que existe o requisito que o navegador suporte essas duas funcionalidades. Mas a maioria dos navegadores de hoje já estão no padrão Html5 e CSS3.

Para criação deste widget usei o plugin de compartilhamento Ridiculous Responsive Social Sharing Buttons e o Share Link Generator que cria os urls de compartilhamento.

Veja o DEMO

Vamos ao código:

Nota: como é svg, parece muito código, mas não é..
 <ul class="rrssb-buttons clearfix">
                <li class="share-button" title="Compartilhar"></li>
                <li class="email" >
                    <a title="Envie para um amigo" href="mailto:?subject=Conhe&ccedil;a Hebrom Benef&iacute;cios&amp;body=Acabei de conhecer o Hebrom Benef&iacute;cios e gostaria de compartilhar com voc&ecirc;!    http://www.hebromsaude.com.br">
                        <span class="icon-share">
                            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve"><g><path d="M20.111 26.147c-2.336 1.051-4.361 1.401-7.125 1.401c-6.462 0-12.146-4.633-12.146-12.265 c0-7.94 5.762-14.833 14.561-14.833c6.853 0 11.8 4.7 11.8 11.252c0 5.684-3.194 9.265-7.399 9.3 c-1.829 0-3.153-0.934-3.347-2.997h-0.077c-1.208 1.986-2.96 2.997-5.023 2.997c-2.532 0-4.361-1.868-4.361-5.062 c0-4.749 3.504-9.071 9.111-9.071c1.713 0 3.7 0.4 4.6 0.973l-1.169 7.203c-0.388 2.298-0.116 3.3 1 3.4 c1.673 0 3.773-2.102 3.773-6.58c0-5.061-3.27-8.994-9.303-8.994c-5.957 0-11.175 4.673-11.175 12.1 c0 6.5 4.2 10.2 10 10.201c1.986 0 4.089-0.43 5.646-1.245L20.111 26.147z M16.646 10.1 c-0.311-0.078-0.701-0.155-1.207-0.155c-2.571 0-4.595 2.53-4.595 5.529c0 1.5 0.7 2.4 1.9 2.4 c1.441 0 2.959-1.828 3.311-4.087L16.646 10.068z"/></g></svg>
                        </span>
                    </a>
                </li>
                <li class="facebook">
                    <a target="_blank" title="Poste no Facebook" href="https://www.facebook.com/sharer/sharer.php?u=https://www.ccasalicchio.com/criando-um-widget-de-compartilhamento-somente-com-css-e-svg/" class="popup">
                        <span class="icon-share">
                            <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
                                <path d="M27.825,4.783c0-2.427-2.182-4.608-4.608-4.608H4.783c-2.422,0-4.608,2.182-4.608,4.608v18.434
                                c0,2.427,2.181,4.608,4.608,4.608H14V17.379h-3.379v-4.608H14v-1.795c0-3.089,2.335-5.885,5.192-5.885h3.718v4.608h-3.726
                                c-0.408,0-0.884,0.492-0.884,1.236v1.836h4.609v4.608h-4.609v10.446h4.916c2.422,0,4.608-2.188,4.608-4.608V4.783z"/>
                            </span>
                        </a>
                    </li>
                    <li class="linkedin">
                        <a target="_blank" title="Poste no LinkedIn" href="https://www.linkedin.com/shareArticle?mini=true&url=https://www.ccasalicchio.com/criando-um-widget-de-compartilhamento-somente-com-css-e-svg/&title=Widget%20de%20CSS3%20e%20SVG%20para%20compartilhamento&summary=Uso%20de%20CSS3%20somente,%20para%20compartilhamento%20em%20m%C3%ADdia%20social&source=" class="popup">
                            <span class="icon-share">
                                <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
                                    <path d="M25.424,15.887v8.447h-4.896v-7.882c0-1.979-0.709-3.331-2.48-3.331c-1.354,0-2.158,0.911-2.514,1.803
                                    c-0.129,0.315-0.162,0.753-0.162,1.194v8.216h-4.899c0,0,0.066-13.349,0-14.731h4.899v2.088c-0.01,0.016-0.023,0.032-0.033,0.048
                                    h0.033V11.69c0.65-1.002,1.812-2.435,4.414-2.435C23.008,9.254,25.424,11.361,25.424,15.887z M5.348,2.501
                                    c-1.676,0-2.772,1.092-2.772,2.539c0,1.421,1.066,2.538,2.717,2.546h0.032c1.709,0,2.771-1.132,2.771-2.546
                                    C8.054,3.593,7.019,2.501,5.343,2.501H5.348z M2.867,24.334h4.897V9.603H2.867V24.334z"/>
                                </svg>
                            </span>
                        </a>
                    </li>
                    <li class="twitter" >
                        <a target="_blank" title="Poste no Twitter" href="https://twitter.com/home?status=%23widget%20%23css3%20%26%20%23svg%20%23sharing%0Ahttps://www.ccasalicchio.com/criando-um-widget-de-compartilhamento-somente-com-css-e-svg/%0A@c_casalicchio" class="popup">
                            <span class="icon-share">
                                <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
                                width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
                                <path d="M24.253,8.756C24.689,17.08,18.297,24.182,9.97,24.62c-3.122,0.162-6.219-0.646-8.861-2.32
                                c2.703,0.179,5.376-0.648,7.508-2.321c-2.072-0.247-3.818-1.661-4.489-3.638c0.801,0.128,1.62,0.076,2.399-0.155
                                C4.045,15.72,2.215,13.6,2.115,11.077c0.688,0.275,1.426,0.407,2.168,0.386c-2.135-1.65-2.729-4.621-1.394-6.965
                                C5.575,7.816,9.54,9.84,13.803,10.071c-0.842-2.739,0.694-5.64,3.434-6.482c2.018-0.623,4.212,0.044,5.546,1.683
                                c1.186-0.213,2.318-0.662,3.329-1.317c-0.385,1.256-1.247,2.312-2.399,2.942c1.048-0.106,2.069-0.394,3.019-0.851
                                C26.275,7.229,25.39,8.196,24.253,8.756z"/>
                            </svg>
                        </span>
                    </a>
                </li>
                <li class="googleplus" >
                    <a target="_blank" title="Poste no Google+" href="https://plus.google.com/share?url=https://www.ccasalicchio.com/criando-um-widget-de-compartilhamento-somente-com-css-e-svg/" class="popup">
                        <span class="icon-share">
                            <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
                                <g>
                                    <g>
                                        <path d="M14.703,15.854l-1.219-0.948c-0.372-0.308-0.88-0.715-0.88-1.459c0-0.748,0.508-1.223,0.95-1.663
                                        c1.42-1.119,2.839-2.309,2.839-4.817c0-2.58-1.621-3.937-2.399-4.581h2.097l2.202-1.383h-6.67c-1.83,0-4.467,0.433-6.398,2.027
                                        C3.768,4.287,3.059,6.018,3.059,7.576c0,2.634,2.022,5.328,5.604,5.328c0.339,0,0.71-0.033,1.083-0.068
                                        c-0.167,0.408-0.336,0.748-0.336,1.324c0,1.04,0.551,1.685,1.011,2.297c-1.524,0.104-4.37,0.273-6.467,1.562
                                        c-1.998,1.188-2.605,2.916-2.605,4.137c0,2.512,2.358,4.84,7.289,4.84c5.822,0,8.904-3.223,8.904-6.41
                                        c0.008-2.327-1.359-3.489-2.829-4.731H14.703z M10.269,11.951c-2.912,0-4.231-3.765-4.231-6.037c0-0.884,0.168-1.797,0.744-2.511
                                        c0.543-0.679,1.489-1.12,2.372-1.12c2.807,0,4.256,3.798,4.256,6.242c0,0.612-0.067,1.694-0.845,2.478
                                        c-0.537,0.55-1.438,0.948-2.295,0.951V11.951z M10.302,25.609c-3.621,0-5.957-1.732-5.957-4.142c0-2.408,2.165-3.223,2.911-3.492
                                        c1.421-0.479,3.25-0.545,3.555-0.545c0.338,0,0.52,0,0.766,0.034c2.574,1.838,3.706,2.757,3.706,4.479
                                        c-0.002,2.073-1.736,3.665-4.982,3.649L10.302,25.609z"/>
                                        <polygon points="23.254,11.89 23.254,8.521 21.569,8.521 21.569,11.89 18.202,11.89 18.202,13.604 21.569,13.604 21.569,17.004
                                        23.254,17.004 23.254,13.604 26.653,13.604 26.653,11.89      "/>
                                    </g>
                                </g>
                            </svg>
                        </span>
                    </a>
                </li>
                <li class="pinterest" >
                    <a target="_blank" title="Poste no Pinterest" href="https://pinterest.com/pin/create/button/?url=https://www.ccasalicchio.com/demos/compartilharwidget/images/node.png&media=Node&description=Widget%20de%20compartilhamento%20usando%20somente%20CSS3%20e%20Svg">
                        <span class="icon-share">
                            <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
                                <path d="M14.021,1.57C6.96,1.57,1.236,7.293,1.236,14.355c0,7.062,5.724,12.785,12.785,12.785c7.061,0,12.785-5.725,12.785-12.785
                                C26.807,7.294,21.082,1.57,14.021,1.57z M15.261,18.655c-1.161-0.09-1.649-0.666-2.559-1.219c-0.501,2.626-1.113,5.145-2.925,6.458
                                c-0.559-3.971,0.822-6.951,1.462-10.116c-1.093-1.84,0.132-5.545,2.438-4.632c2.837,1.123-2.458,6.842,1.099,7.557
                                c3.711,0.744,5.227-6.439,2.925-8.775c-3.325-3.374-9.678-0.077-8.897,4.754c0.19,1.178,1.408,1.538,0.489,3.168
                                C7.165,15.378,6.53,13.7,6.611,11.462c0.131-3.662,3.291-6.227,6.46-6.582c4.007-0.448,7.771,1.474,8.29,5.239
                                c0.579,4.255-1.816,8.865-6.102,8.533L15.261,18.655z"/>
                            </svg>
                        </span>
                    </a>
                </li>
            </ul>

Agora o CSS

.rrssb-buttons{
   width:350px;
   height:32px;
   list-style: none;
   opacity: 0.8;
}
.rrssb-buttons li{
    float:left;
    padding:0 5px 0 5px;
    opacity: 0;
    width: 0;
    transition:all 0.6s ease;
}
.rrssb-buttons:hover{
    opacity: 1;
}
.rrssb-buttons:hover >li{
    width:32px;
    opacity: 1;
}
.icon-share{
    fill:#fff;
}
.share-button{
    background:url('../images/node.png')no-repeat center;
    width: 32px !important;
    height: 32px;
    display: block !important;
    opacity: 1 !important;
    cursor:pointer;
}

Só isso! Bem simples mas muito prático!

DEMO

Código Fonte

Simples botão de Fechar com CSS3 e Jquery

Simples botão de Fechar com CSS3 e Jquery

Neste artigo, Simples botão de Fechar com CSS3 e Jquery, mostrarei como usar um código simples, ao invés de ficar criando imagens para botões, por quê não criar um somente com CSS3? Sem mais delongas, segue o código:

Html do botão:

	<div id="alert">
		<div class="close">X</div>
		<h1>Aviso</h1>
		<p>Texto do seu alerta</p>
	</div>

CSS3 do botão:

.close{
	width: 24px; /*largura*/
	height: 24px; /*altura*/
	font-size: 8px; /*tamanho da fonte (X)*/
	line-height: 24px; 
/*tamanho da linha que contem o texto - 
24 é o mesmo tamanho do botão para o X 
ficar alinhado no centro (cima-baixo)*/
	text-align: center; /*alinhamento to texto no meio do botão (esq-dir)*/
	position: relative; /*relativo para ficar por posicionado como está 
- acima e para fora da caixa de alerta*/
	right: -288px; /*posição em relação à  linha da caixa de alerta - horizontal*/
	top: -12px; /*posição em relação à  linha da caixa de alerta - vertical*/
	float: left; /*desacopla o botão do resto dos componentes*/
	border-radius: 50%; /*raio de circunferencia dos cantos - 50% faz cada lado ficar totalmente redondo, criando um botão circular*/
	border: 1px solid #ccc; /*borda do botão*/
	background-color: #F55555; /*cor de fundo do botão*/
	color: #000; /*cor do text*/
	transition: all 0.2s ease; /*animação que ocorre quando alguma ação é realizada*/
	cursor: pointer; /*mostra o dedo de link mostrando que é clicável*/
}

Jquery de fechamento:

$('.close').click(function(event){
		$('#alert').fadeOut();
		event.preventDefault();
	});

Download da fonte: botao_fechar

DEMO

Busca específica em texto usando jQuery

Busca específica em texto usando jQuery

Nem todo usuário conhece o atalho ctrl+F (buscar) que está presente em quase todos os apps de hoje em dia.

Por esse motivo, é sempre válido criar uma caixinha de busca em páginas que contém muito texto. Busca específica em texto usando jQuery mostra como alcançar essa funcionalidade.

Busca específica em texto usando jQuery

Para este demo foi usado o jQuery highlight Plugin do Johann Burkard.

HTML

<html>
<head>
  <meta name="robots" content="noindex, nofollow"/>
  <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
  <script type="text/javascript" src="scripts/page.js"></script>
  <link href='css/style.css' rel="stylesheet" type="text/css"/>
  <title>Busca com JQuery</title>
</head>
<body>
  <div id="keys">
    <div id="searchBox">
      <input type="text" id="txtsearch" placeholder="search" name="search"/>
      <div id="btnsearch" title="Buscar no texto"></div>
      <div class="break"></div>
      <div class='back-top'>inicio &#8593;</div>
    </div>
    <h1>Texto a ser pesquisado</h1>
    <section>
      TEXTO AQUI
    </section>
    <script src="scripts/jquery.highlight-4.closure.js" type="text/javascript"></script>
  </body>
  </html>

CSS

#searchBox{
position: fixed;
top: 0;
right: 0;
width: 190px;
background-color: #000;
box-shadow: 0 0 8px #ccc;
padding: 0 5px 10px 0;
margin: -4px 0 0 0;
border-radius: 4px;
color: #fff;
text-align: right;
}
#searchBox a{
    color:#fff !important;
}
#txtsearch
{
   border: 4px solid #000;
   line-height: 35px;
   font-size: 14px;
   padding-left: 10px;
   font-family: 'Segoe UI';
   border-bottom-right-radius: 8px;
   border-bottom-left-radius: 8px;
   float:left;
   margin-left:5px;
}
#txtsearch:focus{
    outline:none;
}
#btnsearch{
   background:url('../images/search.png') no-repeat center;
   background-size: 24px;
   width: 24px;
   height: 24px;
   float:left;
   cursor: pointer;
   z-index: 999999;
   margin: 10px 0 0 -40px;
}
.highlight { background-color: #F6E8B9; font-weight: bold; }

.back-top{
   color:#fff;
   font-weight: bold;
   padding-left:15%;
   cursor: pointer;
   transition:letter-spacing 0.3s ease;
}
.back-top:hover{
    letter-spacing:3px;
}

#txtsearch
{
   border: 1px solid #000;
   line-height: 18px;
   font-size: 16px;
   padding-left: 10px;
   font-family: 'Segoe UI';
   border-bottom-right-radius: 8px;
   border-bottom-left-radius: 8px;
   float:left;
}
#btnsearch{
   background:url('../images/search.png') no-repeat center;
   background-size: 16px;
   width: 16px;
   height: 16px;
   float:left;
   cursor: pointer;
   z-index: 999999;
   margin: 7px 0 0 -25px;
}

jQuery

 var counter = 0;
 $(function(){
  $('.back-top').click(function(){
   $('html, body').animate({
    scrollTop: 0
  }, 300);
 });

  $("#btnsearch").click(function(e){
    $("body").removeHighlight();
    $("body").highlight($("#txtsearch").val());
    var curr = $('.highlight')[counter];
    var top =  $(curr).offset().top-180;
    $('html, body').animate({
      scrollTop: top
    }, 300);
    counter++;
    if(counter>=$('.highlight').length)counter=0;
  });
  $("#txtsearch").click(function(){
    $(this).select().focus();
  });
  $("#txtsearch").keypress(function(e){
    if(e.which===13) {
      $("#btnsearch").click();
    };
  });

})

Veja o Demo

Código Fonte

Como criar um formulário de contato em Ajax para Umbraco

Como criar um formulário de contato em Ajax para Umbraco

por Carlos Casalicchio

Ao criar uma página responsiva em Umbraco rapidamente encontra-se algumas restrições relativas ao que a atual oferece. E, apesar da comunidade já ter desenvolvido várias coisas úteis, algumas vezes não encontramos o que precisamos.

Por esse motivo acabei por desenvolvendo um formulário de email simples que possibilite o envio de e-mails em Ajax, sem Postback da página.

Para tanto, criei o formulário na página (está fora do escopo discutir o html envolvido na criação do formulário) como na figura:

Como criar um formulário de contato em Ajax para Umbraco

Neste artigo, mostro como criar um formulário de contato em Ajax para Umbraco. O código do formulário é este:

Html

div class="contact_form">
    <div id="note"></div>
    <div id="fields">
        <form id="ajaxSendmail" action="">
            <input type="text" name="name" value="" placeholder="Name" />
            <input type="text" name="email" value="" placeholder="Email" />
            <textarea name="message" id="message" placeholder="Message"></textarea>
            <div class="clear"></div>
            <input type="reset" class="contact_btn" value="Clear Form" />
            <input type="submit" class="contact_btn send_btn" value="Send" />
            <div class="clear"></div>
        </form>
    </div>
</div>

CSS

#note {
            position: relative;
        }

            #note .notification_ok {
                position: absolute;
                top: 0;
                margin-top: 20px;
                padding: 7px 10px;
                text-align: center;
                text-transform: uppercase;
                background: #444;
                font-family: 'Lato', sans-serif;
                font-weight: 400;
                font-size: 14px;
                color: #fff;
            }

            #note .notification_error {
                position: absolute;
                top: 115px;
                font-family: 'Lato', sans-serif;
                font-weight: 400;
                font-size: 16px;
                color: #f00;
            }

Script AJAX

$("#ajaxSendmail").submit(function () {

            $(this).children().each(function () {
                $(this).css("border", "");
            });
            $('#note').html("");

            if ($("input[name='name']").val() === "" || $("input[name='email']").val() === ""
                || $("input[name='message']").val() === "") {
                $("#ajaxSendmail").children().each(function () {
                    if ($(this).val() === "") $(this).css("border", "2px solid #FF0000");
                });

                $('#note').html('<div class="notification_error">All values are required</div>');
                return false;
            }
            else {

                var str = $(this).serialize();

                $.ajax({
                    type: "POST",
                    url: "/SendMail.aspx",
                    data: str,
                    success: function (msg) {
                        // Message Sent - Show the 'Thank You' message and hide the form
                        if (msg.trim() == 'OK') {
                            result = '<div class="notification_ok">Your message has been sent. Thank you!</div>';
                            $("#fields").hide();
                        } else {

                            result = msg;
                        }
                        $('#note').html(result);
                    }
                });
                return false;
            }
        });

Para fazer o formulário funcionar, é preciso criar uma página macro (em Razor) que faça o processo de enviar o email:

Razor

@inherits umbraco.MacroEngines.DynamicNodeContext
@{
	var name = Request["name"];
	var email = Request["email"];
	var message = Request["message"];

	string host = "relay-hosting.secureserver.net",
                subject = "Contact Received from Site",
                body = "<h3>{0}</h3><p>From: {1}<br /><br /> Email: {2} </p><p>Message: {3}</p>",
                from = "mailer@zueuz.com",
                to = "email@myemail.com";
            int port = 25;

           System.Net.Mail.SmtpClient SmtpServer = new System.Net.Mail.SmtpClient(host, port);
           System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage();

            mail.From = new System.Net.Mail.MailAddress(from, "Corporate Mailer");
            mail.To.Add(new System.Net.Mail.MailAddress(to, "Admin"));
            mail.Subject = subject;
            mail.Body = string.Format(body, subject, name, email, message);
            mail.IsBodyHtml = true;

	try
            {

                SmtpServer.Send(mail);
                <text>OK</text>;
            }
            catch (Exception e)
            {
                <text>@e.Message</text>;
            }

}

E também uma página sendmail
Template

Webforms do Umbraco

<%@  Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>

       <asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
           <umbraco:macro alias="Sendmail" runat="server"></umbraco:macro>
       </asp:Content>

Com esses componentes o formulário já envia e-mails

contact_form_success

contact_form_error

Criando um sistema básico de Short Url

Criando um sistema básico de Short Url

por Carlos Casalicchio

Mídias sociais são parte corriqueira da vida de qualquer usuário de computador, especialmente aqueles que são profissionais na área.

Por isso, tenho o costume de inserir links de várias páginas em minhas assinaturas de email. No entanto, serviços como Yahoo e Outlook (online) restringem a quantidade de caracteres, dificultando a criação de assinaturas completas.

Criando um sistema básico de Short Url

Para resolver esse problema, resolvi criar um Webservice simples que fosse capaz de redirecionar qualquer short link para o url completo da assinatura.

Esse artigo mostra como é fácil criar um serviço de short url usando ASP.NET MVC com C#

Inicialmente, criei a lista de short links num xml mesmo (no momento não senti necessidade de um gerenciador de short links):

Xml

<?xml version="1.0" encoding="utf-8" ?>
<forwards>
  <url src="6uzcQsmW" id="1" created="" expires="" userid="">
    https://www.facebook.com/carlos.casalicchio
  </url>
  <url src="g89Tsdxq" id="1" created="" expires="" userid="">
     </url>
  <url src="h9KyWTfu" id="1" created="" expires="" userid="">
    https://plus.google.com/u/0/+CarlosCasalicchio/posts
  </url>
  <url src="4R7oViGj" id="1" created="" expires="" userid="">
    http://www.linkedin.com/in/ccasalicchio
  </url>
  <url src="udVmFUV8" id="1" created="" expires="" userid="">
    http://www.youtube.com/user/casalicchio
  </url>
  <url src="iTSVtpg3" id="1" created="" expires="" userid="">
    https://www.ccasalicchio.com/
  </url>
  <url src="YcbmDf4s" id="1" created="" expires="" userid="">
    http://www.lastfm.com.br/user/c_casalicchio
  </url>
  <url src="HrRbfS7u" id="1" created="" expires="" userid="">
    https://profile.yahoo.com/COUAKGLAHFWMJCPD73Z4EK5PTA/
  </url>
<url src="sx2GkN7N" id="1" created="" expires="" userid="">
    http://www.mellaniealanny.info/
  </url>
</forwards>

Como é possível ver, já deixei preparado para futuros melhoramentos, mas por enquanto só o source e o url de destino são os dados que interessam.

Depois, criei só um método para a página inicial do site, dentro do ForwardController – afinal, quero os url o mais simples possível

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Xml.Linq;

namespace Short_Links.Controllers
{
    public class ForwardController : Controller
    {
        //
        // GET: /Forward/
        public ActionResult Index(string src)
        {
            var url = from e in MvcApplication.Forwards.Root.Descendants("url") where e.Attribute("src").Value == src select e;
            string redirect = "404.html";
            if (url.DescendantNodes().Count() > 0)
             redirect = url.DescendantNodes().Single().ToString().Trim();

            return Redirect(redirect);
        }

    }
}

O próximo problema é fixar o MVC Routing para aceitar links curtos. Para isso, modifiquei o RouteConfig.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Short_Links
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Forward",
                url: "{src}",
                defaults: new { controller = "Forward", action = "Index", src="" }
                );
            //routes.MapRoute(
            //    name: "Default",
            //    url: "{controller}/{action}/{id}",
            //    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            //);
        }
    }
}

Como pode-se ver, os routings originais foram comentados, pois por enquanto o site não tem nenhuma outra função a não ser redirecionar short urls. Mas, para servir páginas normais é preciso ter outros routes.

A Última coisa feita foi definir uma variável global que contenha todos os short urls, que carregue com a aplicação. Dessa forma, evita-se ler o xml toda vez que um novo request entrar. Isso aumenta a performance do redirecionamento.

Note que só isso foi adicionado, o resto do Global.ascx não foi modificado:

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using System.Xml.Linq;

namespace Short_Links
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode,
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static XDocument Forwards {get;set;} 

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //Load xml with all forwards
            string path =
                Server.MapPath("~/App_Data/forwards.xml");
            Forwards = XDocument.Load(path);
        }
    }
}

Mas o que fazer se alguém tentar acessar o site sem um short link? Para mitigar esse problema, criei uma página 404.html simples, que informa o usuário que ele não submeteu nenhum short link:

Html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Url Not Found</title>
    
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0A%20%20%20%20%20%20%20%20body%20%7Bwidth%3A100%25%3Btext-align%3Acenter%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20url('%2FImages%2Fgrumpy-cat-04.jpg')%20no-repeat%20top%20right%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20background-size%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A'Segoe%20UI'%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A30px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20margin%3A0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20padding%3A0%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.not-found-body%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3Aabsolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20bottom%3A0%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20float%3Aleft%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background-color%3A%23fff%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20opacity%3A0.6%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20box-shadow%3A%200%20-2px%202px%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-shadow%3A%200%200%202px%20%23000%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-transform%3Auppercase%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.not-found-body%20span%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A12px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

</head>

<body>
    
<div class="not-found-body">

<h3>Damn you 404!</h3>

    <span>You did not insert any short urls to be redirected...</span></div>

</body>
</html>

Em código é só isso! Subi o site publicado para o provedor e voilá, agora tenho um serviço de short urls.

Baixe o código fonte do projeto Short Links.

Outras sugestões são:

  • Compre um domínio curto. No meu caso, estou usando o http://puul.ga que foi comprado através do http://dot.tk
  • Meu provedor de hosting é o http://www.godaddy.com e apontei o DNS do domínio para o Godaddy.
  • Se for usar imagens, coloque-as numa pasta de nome curto – neste projeto nomeei a pasta como ‘i’ e deixei todas as imagens com nomes curtos também. Assim, se for referenciar suas imagens em outro lugar, o link também fica curto, como por exemplo https://puul.ga/i/face.png .