Upload und Speichern von Dateien in einer DB mit einer C# ASP.NET MVC Applikation

uploadMVC mit C# macht ja grundsätzlich Spass (obwohl für einige Leute ja auch Auspeitschen angenehm ist :)). Ich habe ein paar spezielle Aufgaben zum Realisieren gefasst und gebe hier ein paar Weisheiten von mir, die wahrscheinlich allgemein bekannt sind und ich einfach nur nicht finden kann.

In diesem Beitrag wird beschrieben, wie man verschiedene Medien (Bilder, Filme, Audio) hochladen, in einer Datenbank speichern und (mit HTML 5) wieder darstellen kann. Ich gehe aus von einer frisch erstellten und herausgeputzten MVC 3 Webapplikation.

Am Schluss sollte die Liste in etwa so aussehen:

Liste der Medien

Weiterlesen

Ich weiss (nicht mehr) ob Du im Facebook bist…

Update 27.01.2011: *übel_fluch*! Facebook hat ein Captcha eingebaut, dass diese Detection momentan nicht mehr funktionieren lässt. Das Prinzip bleibt ok, die Realisierung brauchte nun etwas mehr Zeit.

Big BrotherEigentlich hätte ich ja ganz Anderes zu tun, aber per Zufall bin ich über den genialen Blogartikel von Mike Cardwell gestolpert. Er hat eine Idee gehabt, wie es möglich wird herauszufinden, ob Benutzer auf Facebook und Konsorten eingeloggt sind oder nicht.

Umgesetzt in eine kleine Library läuft es als Proof-of-Concept auf Firefox und sieht folgendermassen aus:

Loggen Sie in Gmail, Facebook, Twitter oder Digg ein um Resultate zu sehen.
Sind Sie nicht abgelenkt?

Neuer Check

Grundsätzlich wäre es ja ganz einfach: Man schaut vom Client-Browser aus, ob man Zugriff auf Bilder oder Seiten hat, die nur für Eingeloggte zur Verfügung stehen. Leider gestaltet sich dieser simple Ansatz nicht so einfach, weil mit Ajax nur zur Domain der Website verbunden werden darf und nicht an einen x-beliebigen Ort. Ein Proxy hilft auch nicht weiter, da es die Cookies des Browsers braucht, und die werden nur an Facebook, etc direkt abgeliefert.

Wie kann man also diese Restriktion umgehen?

Weiterlesen

PHP Funktionstests aus Netbeans mit Selenium

SeleniumIch hab da so ein Projekt, dass ich schon mehr als ein Jahr vor mir herschiebe: Eine Überarbeitung und Erweiterung einer bestehenden PHP-Applikation. Sie läuft eigentlich super, das Pflichtenheft ist gut und alle sind glücklich. Leider hat sich die „Erweiterung“ als Operation am Herzen herausgestellt. Der ursprüngliche Cöder hat eine ziemlich komplexe Struktur erarbeitet, in der er sich selbst ab und zu verheddert hat. Das bedeutet, dass zuerst ganz hässliche Strukturbugs als solche identifiziert und gefixt werden mussten. Im Zuge dieser Operation habe ich so ziemlich jeden Teil der Software berührt, beflucht und daran herumgewurschtelt.

Um etwas Sicherheit und Stabilität in die Sache zu bringen hätte ich gerne Unit Tests gehabt. Bei diesem Kludge sind aber so viele Komponenten beteiligt, dass es nicht so einfach klappt. Der Hammer musste eine Nummer grösser gewählt werden: Selenium.

Mit Selenium verbindet mich eine unerwiderte Liebe. Immer wieder versuche ich an das Tool heranzukommen, immer wieder scheitere ich irgendwie weil ich abgewiesen werde. So auch zu Beginn dieses Anlaufs: Die Funktionstests laufen (vielleicht), finden es aber unangebracht sich irgendwie zu melden. Die Doku ist gut, aber nirgends wird ein Überblick vermittelt und man muss die fragilen Einzelteile mit gutem Zureden zueinander bringen.

Weiterlesen

Do you Poken? Die Brücke zwischen dem echten Leben und dem Web 2.0.

[Trigami-Anzeige]
POKEN Auch im Zeitalter von Web 2.0 kann es passieren, dass man Maus und Tastatur verlässt um höchstselbst an einer Veranstaltung mit realen, oftmals menschlichen Personen teilzunehmen. Man unterhält sich per akustischem Netzwerk, versteht sich gut, möchte weiter in Kontakt bleiben und dann geht es los: Handynummer? Hm, „<schrei>4 9 2 oder 3? Hä?</schrei>“, ein Stift ist nie zur Hand und Kajal auf Handrücken ist auch sehr unzuverlässig und darum nicht zu empfehlen. Bist Du auf Facebook? Nein aber auf Xing? Welcher Namen? MySpace? Da bin ich nicht…
Und so zieht es sich hin… Pokens sind eine gute und einfache Lösung des Problems, ohne heiser zu werden oder am Austausch eines Links zu verzweifeln.

Vier Pokens versammelt

Vier Pokens versammelt

Die Idee

Gute Ideen sind einfach, so auch diese: Man legt auf www.doyoupoken.com eine oder mehrere Identitäten an, in denen man sowohl Daten aus dem echten Leben (Name, Adresse, Telefonnummern, Mailadressen, Blog-URL, …) als auch Links zu den Profilen in verschiedenen Social Networks (von Xing, LinkedIn über Flicker, Facebook, MySpace, Twitter bis hin zu usgang.ch, etc) angeben kann. Wenn nun das Gegenüber auch ein Poken dabei hat, dann hält man die Pfötchen kurz aneinander und schon ist die ID ausgetauscht. An einem beliebigen Compie mit Internetanschluss und USB können die ID’s vom Poken abgerufen werden und man sieht die entsprechenden Profile auf www.doyoupoken.com:

Die Identität, die mit Pokens ausgetauscht werden kann.

Die Identität, die mit Pokens ausgetauscht werden kann.

Die hochgeladenen Freundesprofile können verschieden sortiert, abgelegt und gesucht werden. Die Ganze Sache ist also sehr effizient und benutzerfreundlich…

Am Schluss dieses Beitrages könnt Ihr lesen, wie Ihr zwei Pokens gewinnen könnt!

Weiterlesen

MySQL berücksichtigt keine Gross- und Kleinschreibung bei select?

Manchmal habe ich echt das Gefühl, ein paar Jahre ganz feste kognitiv eingeschränkt gewesen zu sein. Per Zufall stellte ich heute fest, was das „ci“ bei den Datenbank Collations in Mysql heisst.

<musik typ="leicht-nervig" creator="Synthesizer" dauer="10" />
Moderator: Wahaaas bedeutet das Zeh Ih (_ci) bei den Datenbank Collations in MySQL?
Kandidat: Hmmm, uff, kann ich es im Kontext haben?
Moderator: Natürlich! Zeh Ih wie in utf8_general_ci.
Kandidat: Ui, das weiss ich nicht, gibt es Auswahlmöglichkeiten?
Moderator: Ja sicher:

  1. collation identifier
  2. childish internet
  3. clown institut
  4. corporate identity
  5. case insensitive


Und die Lösung: CASE INSENSITIVE

Was bedeutet _ci in der Praxis?

Tabelle:

+----+----------+----------+------------------------------------------+
| id | username | salt     | password                                 |
+----+----------+----------+------------------------------------------+
|  1 | Admin    | 2IT8mKiX | 8fd334609270a4b78c536dbdee4182b5b1d00bb7 |
+----+----------+----------+------------------------------------------+

Tja, folgender Tabelleneintrag wird gefunden mit folgenden Selects:

SELECT * FROM users WHERE username='Admin'; -- Klar, logo
SELECT * FROM users WHERE username='ADMIN'; -- Ui
SELECT * FROM users WHERE username='admiN'; -- Uiui
SELECT * FROM users WHERE username='admin'; -- Uiui, huch

Und das impliziert wiederum, dass ein User, der sich ADMIN nennt, unter Umständen vor dem eigentlichen Admin gefunden wird. Das ist doch eher hässlich. Wieso ist mir das nicht früher aufgefallen..?

Und die Lösung?

Die Collation ändern! Von utf8_general_ci zu utf8_bin und schon verhält sich die Datenbank erwartungskonform.

Was bedeutet dies für Rails?

Zum Einen sollte die Collation in der Datei config/database.yml festgelegt werden:

development:
  adapter: mysql
  encoding: utf8
  collation: utf8_bin
  database: lomas_development
  pool: 5
  username: lomas
  password: sagichnit
  socket: /var/run/mysqld/mysqld.sock

Leider wird die Collation bei Tests in Ruby on Rails nicht berücksichtigt! Das treibt mich zur Verzweiflung, echt!

Ausserdem gibt es noch etwas zu beachten bei validates_uniqueness_of. validates_uniqueness_of ist case-sensitive. Das heisst, auch hier werden admin und ADMIN akzeptiert. Erst ein weiterer Parameter verbietet dies:

  validates_uniqueness_of :username, :case_sensitive => false

Kleine Tricks in Ruby on Rails

Hier eine kleine Sammlung von Dingen, die mir geholfen haben beim Lösen der Probleme die sich einem N00b so stellen.

Exportieren von Datensätzen ins YAML Format

Rails Fixtures sind Testdatensätze, die im relativ übersichtlichen YAML-Format gespeichert werden. Möchte man bestehende Datenbankinhalte als Testdatensätze verwenden, so können Sie direkt in YAML exportiert werden.

Ich halte mich an den Beitrag von NetManiac. Den Code habe ich nur soweit ergänzt, als dass ich das Exportieren der magischen Felder „id“, „created_at“ und „updated_at“ verhindere und den Dateinamen ändere, damit keine bestehenden Fixturen überschrieben werden.

Aufgerufen mit rake extract_fixtures['mymodel'] speichert es mymodel.dumped.yml im richtigen Verzeichnis.

Datei: libs/tasks/extract_fixtures.rake:

desc 'Create YAML test fixtures from data in an existing database.
Defaults to development database. Set RAILS_ENV to override. Use args
table and limit to dump one table and limit number of records'

task :extract_fixtures, :table, :limit, :needs => :environment do |t, args|
  args.with_defaults(:table => nil, :limit => nil)
  limit = args.limit.nil? ? "" : "LIMIT #{args.limit}"
  sql = "SELECT * FROM %s #{limit}"
  skip_tables = ["schema_info" ]
  if args.table.nil?
    tables = ActiveRecord::Base.connection.tables - skip_tables
  else
    tables = [ "#{args.table}"]
  end

  ActiveRecord::Base.establish_connection
  tables.each do |table_name|
    i = "000"
    File.open("#{RAILS_ROOT}/test/fixtures/#{table_name}.dumped.yml" , 'w' ) do |file|
      data = ActiveRecord::Base.connection.select_all(sql % table_name)
      file.write data.inject({}) { |hash, record|
        record.delete_if{|key, value| ['id', 'created_at', 'updated_at'].include?(key)}
        hash["#{table_name}_#{i.succ!}"] = record
        hash
      }.to_yaml
    end
  end
end

ActiveRecord Attribute beim Auslesen mit Defaultwerten überschreiben

Wenn ein Attribut immer beim Auslesen mit eineb bestimmten Wert belegt werden soll wenn noch nichts vorhanden ist, so geht dies mittels folgendem Codefragment:

def my_attribute
  read_attribute(:my_attribute) or another_attribute.capitalize+" "+yet_another_attribute
end

has_and_belongs_to_many mit Checkboxen in Fieldsets

Eine Rolle hat viele Permissions und umgekehrt. Um solche Assoziationen abzubilden, kann man collection_select verwenden, das aber leider nur ein hässliches Listfeld ausgibt. Eine ambitioniertere Variante arbeitet mit Checkboxen.

HABTM mit Checkboxen

HABTM mit Checkboxen

Zuerst wird in Permission.get_indexed_by_controller ein Hash of Hashes gemacht, der als ersten Schlüssel die Überschriften hat (controller) als zweiten Schlüssel die Feldbeschriftungen mit dem Zustand als Inhalt. Folgendes Partial gibt ein Formular aus und versieht sie mit „(Un-)Check all“ Links:

<%= link_to_function 'Check all', "$$('input.permission').each(function(checkbox) { checkbox.checked = true; })" %> |
<%= link_to_function 'Uncheck all', "$$('input.permission').each(function(checkbox) { checkbox.checked = false; })" %>
<% @permissions_indexed_by_controller=Permission.get_indexed_by_controller %>
<br />
<%- for controller in @permissions_indexed_by_controller.keys -%>
  <% field_set_tag controller do %>
    <%- for permission in @permissions_indexed_by_controller[controller] -%>
      <%= check_box_tag 'role[permission_ids][]', permission.id, @role.permissions.include?(permission),{ :id => "role_permission_ids_"+ permission.id.to_s, :class => "permission"} -%><%= permission.description %>
    <%- end -%>
  <% end %>
<%- end -%>

Wichtig ist, dass im entsprechenden Controller in der update Funktion ein leeres Array erzeugt wird wenn nichts übergeben wird, sonst ändert sich nichts wenn man alle Häckchen rausnimmt:

params[:role][:permission_ids] ||= []

Erzeugen eines Hash of Hashes in Ruby

Die Hash/Array implementierung in PHP ist immernoch ungeschlagen. Will man in Ruby ein Hash of Hashes machen und einen Wert mittels << hinzufügen wenn noch kein leerer Hash besteht, so tut es blöd. Insbesondere wenn man Records in einem Hash abspeichen will, hilft folgendes Idiom:

      (ret[map_key] ||= []) << record

Dynamisches Navigationsmenü mit Highlightning in Ruby on Rails

Und es kam so: Gerade eben aufgestanden und am Gliedersortieren, sprach der Duschkopf zu mir: „Hey, du!“. Obwohl überrascht über diese ungewohnte Intimität des Duzens, war meine Aufmerksamkeit geweckt. „Jetzt mal ehrlich“, so sprachs weiter, „mit dem Symfony Framework hälst Du es gleich wie mit dem Latein: Oftmals angefangen, aber nie ganz warm geworden, gelle.“. Mein Hirn lief schon an der Grenze seiner morgentlichen Leistungsfähigkeit, aber eine Antwort darauf war wahrscheinlich auch gar nicht verlangt. „Du weisst schon, dass PHP eine Templatesprache ist, oder? Wieso hörst du nicht auf mit der Verulkung und versuchst Dich endlich mal am Original?“. Ich entgegnete was in der Richtung, dass die genuinen Römer (TM) schon ziemlich lange die Arena von unten ansehen. „Nein, du Dumpfbacke! Ruby on Rails sollst du versuchen und Freude daran haben! Frohlocken! Beweg deinen alternden Denkapparat du Assemblerheini!“

Natürlich folge ich allen Anweisungen meiner Badezimmerarmaturen, und so habe ich mich unverzüglich an die Arbeit gemacht. Die Standardinstallation hat es natürlich nicht getan (…und Zeit hat man zu viel…), Rails 2 musste es sein, mittlerweile released als Rails 2-2. Auch eine Entwicklungsumgebung musste her, und ich habe mich für Netbeans entschieden, da sie für zwei weitere Projekte (Handygames und JavaFX) eh angesehen werden wollte. Wie das Glück so spielt, hatten auch die Netzbohnen gerade ein grösseres Update auf 6.5 durchgemacht und kleinere Fluchereien mit verwurschtelten Projekten ausgelöst. Naja.

Mit verschiedenen Offline- und Onlinequellen habe ich mich an die Arbeit gemacht, und muss sagen, Ruby on Rails ist, hmm, ungewohnt. Bis jetzt geht es überraschend flüssig, auch wenn ich bei jeder Zeile 4 verschiedene Quellen konsultieren muss um es richtig (TM) zu machen.
Weiterlesen

Websites tunen mit YSlow

Zwar werden die Netzwerkanbindungen immer schneller, aber in gleichem Masse wächst auch die zu übertragende Datenmenge: Kiloweise HTML, CSS und Javascript, dynamische Bildchen, Favicons, Ads, Cookies, Ajax, Mr. Proper und Schiessmichtot machen alleine auf diesem Blog über ein Megabyte aus. <einschub>Ich weiss noch, als ich den Teilnehmer eines Internetkurses um 1995 gesagt habe, dass 1 MB ungefähr 1 h zum Download braucht</einschub>. Ich möchte Euch Lesern ein gutes Erlebnis bieten und darum bin ich mal ans Tuning des Blogs gegangen. Mein Ziel: Möglichst rascher Download und Seitenaufbau mit minimalem Qualitäts-, Inhalts- und Informationsverlust. Die Optimierung sollte schliesslich auch gemessen werden um den Erfolg bestimmen zu können.

Werkzeuge

Ganz sicher brauchen wir den Firefox und das Firebug Add-on. Dies sind die Grundlagen für eine nette Erweiterung, die Yahoo für uns gecoded hat: YSlow. Sobald alles installiert ist und Firefox seine Restarts durchlitten hat, kann es losgehen.
Weiterlesen

UWA Widgets coden

Web 2.0, Just Do It…

Je besser man etwas kennt, desto besser kann man darüber lästern… Ich habe beschlossen, mich ganz praktisch durch das Tal der Tränen zu kämpfen und mich auf dieser Wanderung mit den technischen Teilen von Web 2.0 herumzuschlagen. Wie fühlt sich Ajax an? Wieviel Schatten spendet der DOM-Tree? XML oder JSON, JavaScript und APIs sollten die Dämonen auf meinem Weg sein.

Das Resultat

Das Resultat kann im Ecosystem angesehen und installiert werden. Es ist ein Widget, das Adressen über tel.search.ch sucht.
Tel.search.ch bietet ein geniales, gut dokumentiertes REST API an.

Ein paar Screenies?
So siehts in Netvibes aus:
Das tel.search.ch Widget in Netvibes

So in iGoogle:
Das tel.search.ch Widget in iGoogle

Und so in der Sidebar von Windows Vista:
tel.search.ch UWA Widget in Windows Vista Sidebar

Weiterlesen

PHP: NHL-Spielerstatistiken in Excel ausgeben für Fantasy-Leagues

Das Problem

HockeyIch habe mir sagen lassen, dass es auch hier bei uns Leute gibt die in einer Fantasy League mitspielen. Irgendwie werden da virtuelle aber real existierende Sportler gezogen und verkauft und nach einem speziellen Algorithmus werden Punkte verteilt, die abhängig von den tatsächlichen Resultaten sind, die dieser Spieler erzielt. Nun, auf jeden Fall wird da viel mit Statistiken gewurstelt. Ein Fantasy League-Mitglied ist an mich herangetreten, weil er die online publizierten Resultate der Skaters und Goalies der NHL in einem Excel haben wollte.

Zuerst dachte ich, das sei ein Fall für Dapper. War es aber nicht, da auf der Webpage reine Text-Exporte waren.

Die Erfahrung hat gezeigt, dass die Form der Ausgabe auf diesen Seiten oftmals ändert und man sich auf so wenig wie möglich verlassen sollte.

Die Felder dieser Tabelle haben keine Trennzeichen, sondern eine feste Breite und sind links- oder rechtsbündig formatiert.

Einige Punkte der Lösung

Download der Webseite in eine Variable

Zuerst muss man mal an die Seite rankommen. In PHP den Inhalt einer URL in einen String laden ist in der Theorie relativ einfach. In der Praxis jedoch ist aus Sicherheitsgründen der Zugriff auf URLs für die file* Funktionen oftmals eingeschränkt. Nunja, dann wird halt ein cURL-Fallback mit eingebaut, so dass wir auf jeden Fall in $page ein Array aus den Zeilen der HTML-Datei haben:

  if(!($page = @file($url))) {
    $ch = curl_init();
    curl_setopt ($ch, CURLOPT_URL, $url);
    curl_setopt ($ch, CURLOPT_USERAGENT, 'User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.6) Gecko/20070723 Iceweasel/2.0.0.6 (Debian-2.0.0.6-0etch1+lenny1)');
    curl_setopt ($ch, CURLOPT_HEADER, 0);
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec ($ch);
    curl_close ($ch);
    $page=explode("\n",$result);
  }

Überschriften auslesen

Ich lese die Tabellenübberschriften mit einem regulären Ausdruck aus (man beachte dass die Sternchen sich der Formatierung (links/rechts) anpassen), aus dem danach die Spaltenbreiten gelesen werden können. Aus den Spaltenbreiten wiederum kann ein regulärer Ausdruck gebaut werden über den auf die Werte zugegriffen werden kann.

Beispielsweise steht auf der Webpage:

NAME                                   POS GP   G    A PTS SOG +/- PIM PPG SHG

daraus lese ich mit folgendem Regexp die Feldbreiten:

'@^(NAME *)(POS *)(GP)( *G)( *A)( *PTS)( *SOG)( *\\+/\\-)( *PIM)( *PPG)( *SHG)$@'

Damit baue ich dynamisch einen neuen regulären Ausdruck, der die Tabellendaten auslesen kann:

/^(.{39})(.{4})(.{2})(.{4})(.{5})(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})$/

Codiert sieht es so aus. $tline im Codebeispiel beinhaltet eine getrimmte Zeile aus dem HTML.

// Überschriften und Feldgrössen
if(preg_match('@^(NAME *)(POS *)(GP)( *G)( *A)( *PTS)( *SOG)( *\\+/\\-)( *PIM)( *PPG)( *SHG)$@',$tline,$matches)) {
  if(!$inresults) {
    // Nach den Überschriften kommen die Tabelleneinträge
    $inresults=true;
    // Überschriften auslesen
    $headercaptions=preg_split('/ +/', $tline, -1, PREG_SPLIT_NO_EMPTY);
    // Überschriften ausgeben
    echo "\r\n".join("\t",$headercaptions)."\r\n";
  }
  // Wie breit ist ein Feld (in Zeichen)
  $sizes=array();
  for($i=1;$i<count($matches);$i++) {
    $sizes[$i-1]=strlen($matches[$i]);
  }

  // Suchmuster für Einträge aufbauen
  $searchpattern="";
  foreach($sizes as $size) {
    $searchpattern.='(.{'.$size.'})';
  }
  //print $searchpattern."<br />";
  $searchpattern='/^'.$searchpattern.'$/';
  continue;
}
// Tabellenzeilen auslesen
if($inresults && preg_match($searchpattern, $tline, $matches)) {
   $row=array();
   for($i=1; $i<count($matches); $i++) {
     $row[]=trim($matches[$i]);
   }
   // Und ausgeben
   echo join("\t",$row)."\r\n";
   continue;
}

Ausgabe als Excel

Hier bescheisse ich… Ich gebe nur eine tabulatorseparierte Liste aus. Excel schnallts, OpenOffice leider nicht (hat da wer eine gescheitere Lösung? HTML-Tabellen?)…

Zuerst die Headers:

header("Content-type: application/vnd.ms-excel");
header('Content-Disposition: inline; filename="'.$dateiname.'.xls"');

Einzelne Zellen werden mittels Tabulator (\t) voneinander getrennt, Zeilenenden werden durch "\r\n" markiert:

echo join("\t",$row)."\r\n";

Die Lösung

Die komplette Lösung kann hier fantasy-league heruntergeladen werden und eine Demo gibts auf apps.oncode.info