Running Blazor Web App

My web site is running on Linux/Apache server. So, I wanted Blazor Web App to run on that server too.

Developing Blazor app was quite easy and straight forward process but writing the right .htaccess file was not so easy. I searched the internet and found sample on Microsoft web pages (https://docs.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/?view=aspnetcore-6.0&tabs=visual-studio), but provided .htaccess file sample was not enough.

I think that running Blazor on Apache is interesting for others too, so here is .htaccess file I am using in production (just update string ‘sub-directory’ (2x) with correct directory path of your app on Apache file system).

Link to GitHub gist: Apache .htaccess file for running Blazor Web App (github.com)

<IfModule mod_headers.c>
    # Serve brotli compressed files if they exist and the client accepts brotli.
    RewriteCond "%{HTTP:Accept-encoding}" "br"
    RewriteCond "%{REQUEST_FILENAME}\.br" "-s"
    RewriteRule "^(.*)\.(js|json|css|dll|dat|blat|wasm)$" "$1\.$2\.br" [QSA]

    # Serve gzip compressed files if they exist and the client accepts gzip.
    RewriteCond "%{HTTP:Accept-encoding}" "gz"
    RewriteCond "%{REQUEST_FILENAME}\.gz" "-s"
    RewriteRule "^(.*)\.(js|json|css|dll|dat|blat|wasm)$" "$1\.$2\.gz" [QSA]
    
    # Serve correct content types, and prevent double compression.
    RewriteRule "\.css\.br$" "-" [T=text/css,E=no-brotli:1]
    RewriteRule "\.js\.br$"  "-" [T=text/javascript,E=no-brotli:1]
    RewriteRule "\.json\.br$"  "-" [T=application/json,E=no-brotli:1]
    RewriteRule "\.dll\.br$"  "-" [T=T=application/octet-stream,E=no-brotli:1]  
    RewriteRule "\.dat\.br$"  "-" [T=application/octet-stream,E=no-brotli:1]  
    RewriteRule "\.blat\.br$"  "-" [T=application/octet-stream,E=no-brotli:1]  
    RewriteRule "\.wasm\.br$"  "-" [T=application/wasm,E=no-brotli:1]  
    
    # Serve correct content types, and prevent double compression.
    RewriteRule "\.css\.gz$" "-" [T=text/css,E=no-gzip:1]
    RewriteRule "\.js\.gz$"  "-" [T=text/javascript,E=no-gzip:1]
    RewriteRule "\.json\.gz$"  "-" [T=application/json,E=no-gzip:1]
    RewriteRule "\.dll\.gz$"  "-" [T=T=application/octet-stream,E=no-gzip:1]  
    RewriteRule "\.dat\.gz$"  "-" [T=application/octet-stream,E=no-gzip:1]  
    RewriteRule "\.blat\.gz$"  "-" [T=application/octet-stream,E=no-gzip:1]  
    RewriteRule "\.wasm\.gz$"  "-" [T=application/wasm,E=no-gzip:1]  
    
    <FilesMatch "(\.js\.br|\.css\.br|\.json\.br|\.dll\.br|\.dat\.br|\.blat\.br|\.wasm\.br)$">
      # Serve correct encoding type.
      Header set Content-Encoding br
      # Force proxies to cache brotli & non-brotli
      Header append Vary Accept-Encoding
    </FilesMatch>
    
    <FilesMatch "(\.js\.gz|\.css\.gz|\.json\.gz|\.dll\.gz|\.dat\.gz|\.blat\.gz|\.wasm\.gz)$">
      # Serve correct encoding type.
      Header set Content-Encoding gzip
      # Force proxies to cache gzipped & non-gzipped
      Header append Vary Accept-Encoding
    </FilesMatch>
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /sub-directory/
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /sub-directory/index.html [L]
</IfModule>

Elektronsko glasovanje

Včeraj sem se spet ujel v zanko elektronskih volitev in spet sem bil strogo proti in spet sem želel razložiti zakaj pa je to težko v par stavkih.

A navkljub vsemu bom v današnji kolumni poskušal zares na kratko in zares preprosto razložiti, zakaj smo računalničarji proti elektronskim volitvam. Pri razlagi ne bom uporabil nobenega strokovnega termina, temveč bom pisal z besedami, ki jih razume vsakdo. Ob tem pa bom veliko težav preprosto izpustil. Torej, težav je še mnogo več in tukaj bom izpostavil samo nekatere, ki pa so že dovolj, da sem strogo proti elektronskim volitvam.

Glasovanje od doma

Takoj lahko izločimo glasovanje od doma, iz domačega naslonjača, iz domačega računalnika. Glasovanje bi šlo iz mojega računalnika – preko interneta – na centralni strežnik. Glede na število vlomov in možnosti prestrezanja je ta opcija najslabša.

Protiargument so vedno – banke! Vsi že poslujemo z bankami preko interneta in vse je OK.

To je res. Vendar banka VE, kdo sem, in VE, kaj počnem. Pri glasovanju pa bi moral nekdo vedeti:

  • Da sem jaz in ne kdo drug (to bi šlo)
  • Da sem glasoval največ enkrat (to bi še šlo)
  • Kaj sem glasoval – no, tega pa ne sme vedeti – tega SPLOH ne sme vedeti (to pa ne gre več)

Glasovanje na volišču

Torej pridem na volišče in tam je računalnik, ki sprejme moj glas:

  • Kako naj vem, kakšna programska koda teče na tem računalniku?
    • Ja, odprtokodna, lahko jo pogledam – Super(!)
    • Kdo mi zagotavlja, da je na TEM računalniku programska oprema, ki sem jo jaz pogledal in sem z njo zadovoljen?
    • Vem, kar je digitalno podpisana in ima neko »HASH kodo«… Kaj pa je to? Rekel sem da ne bom uporabil strokovnih izrazov. Moja mama, ki glasuje, ve, kaj je to HASH koda?
  • Če sem zadovoljen s programsko kodo, kaj pa strojna oprema? Kaj, če programska koda sicer deluje OK in je ni nihče »popravljal«. Kdo mi zagotavlja, da se tisto, kar zapiše programska koda, potem res tudi zapiše na disk, in to NESPREMENJENO(!)?
    • Protiargument je list, ki ga dobim v roke in ga natisne računalnik. Ah, na listku je lahko napisano karkoli, računalnik lahko gor nariše sličico, pa sledenje še vedno ne pomeni, da je to res zapisal tudi na disk.

Tu se bom raje ustavil, saj bo stvar sicer predolga in je ne bo nihče bral.

Sedaj lahko imate še 100 protiargumentov. Pri vsakem protiargumentu pa se vedno vprašajte, koliko znanja bi moral imeti vsakdo, ki bi nadziral takšne volitve – VSAKDO!

Pri glasovanju z lističi pa imamo listke, zapečatene glasovalne skrinjice, predstavnike več političnih strank v ISTEM prostoru, in to je to. Temu lahko VSAKDO zaupa. In če mogoče ne zaupa nekemu konkretnemu volišču, je volišč po Sloveniji več 3000 in nekdo, ki goljufa, mora goljufati na 3000 voliščih z 10.000 udeleženimi ljudmi, ki razumejo glasovanje in vidijo lističe ter glasovalne skrinjice.

Ne hvala. Ostanimo pri listkih in 10.000 udeleženih ljudeh – takšnim volitvam najbolj zaupam, elektronskim volitvam ne bom zaupal nikoli, saj imam preveč znanja.

Tiskanje strani v spojenem dokumentu

Ko izberete spajanje dokumentov, lahko v zadnjem koraku spajanja spojite celoten dokument in s tem dobite velik dokument s spojenimi vsemi izbranimi elementi.

Pojavi pa se težava, ko želite v tem dokumentu natisniti samo določene strani, saj to ne deluje. Ko dokument odprete lahko vidite, da ima dokument recimo 124 strani, vi pa bi želeli izpisati samo strani od 13-24.

Izberete tiskanje in vpišete območje strani 13-24, pa se ne zgodi nič. 🙁

To je težava in menim, da bi jo MS lahko bolje rešil, vendar imamo pač kar imamo. V spojenem dokumentu ne morete tiskati strani od – do. Težava je namreč v dejstvu, da je takšen dokument spojen iz več odsekov.

Spojeni dokument je namreč nastal tako, da sta spojili nek pred-pripravljen vzorec (recimo 5 strani) z nekim seznamom (recimo seznam 20 oseb). S tem ste dobili dokument, ki je velik 100 strani (5 strani x 20 oseb = 100 strani). V tem dokumentu so podatki o prvi osebi zapisani na straneh 1-5, podatki o drugi osebi od 6-10, podatki o 13-ti osebi pa od 61-65. Verjetno je torej res dokaj nesmiselno izpisovati dokument od 47-53 strani. S tem bi namreč izpisali zadnje štiri strani za 10-to osebo in prve 3 strani 11-te osebe.

V takšnem dokumentu torej ne izpisujete posameznih strani temveč posamezne odseke in to izberete kot ‘s5-s7’ – torej, kot odsek (section) 5 do odseka 7. V našem konkretnem primeru bi to pomenilo, da želite izpisati vse liste za osebe 5, 6 in 7 oz. izpisali bi strani 21-35. (21-25 = oseba 5, 26-30 = oseba 6 in 31-35 = oseba 7).

Takšen izbor torej ne deluje:

Izbira posameznih strani NE DELUJE

Takšen izbor (odsekov) pa deluje:

Izbor odsekov oz. tiskanje odsekov pa deluje

Da bi v dokumentu lažje videli v katerem odseku se nahajate lahko wordu zaukažete, da spodaj v statusni vrstici prikazuje tudi številko odseka, kar naredite tako, da desno kliknete na statusno vrstico, s čimer se vam odpre hitri meni in na tem meiju izberite, da želite prikazovati tudi številke odsekov.

Za lažje delo lahko Word-u zaukažete naj v statusni vrstici prikazuje tudi odseke.

Using Vue SPA (created with webpack) in subfolder

There is a common use case where Vue SPA will run in some subfolder in production environment.

When you develop Vue app using Vue-cli all your links are targeting root folder. There are some topics on Vue forums about that problem but there are not complete, so here is a solution that works. I am not saying that it is the best or. only possible, but it works!

Solution:

I am using two index.html files. One for development (index.html) and the other for production (index_prod.html). Files are 95% the same, except some minor changes in index_prod.html. So, if this is basic index.html

<!DOCTYPE html>
<html>
  <head>
    <metacharset="utf-8">
    <metaname="viewport" content="width=device-width,initial-scale=1.0">
  </head>

  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script src="/static/config.js"></script>
  </body>
</html>

For production you must use base tag and change src link (if you need one!)

<!DOCTYPE html>
<html>
  <head>
    <strong>&lt;basehref="/sub-folder/" /&gt;</strong>
    &lt;metacharset="utf-8"&gt;
    &lt;metaname="viewport" content="width=device-width,initial-scale=1.0"&gt;
  &lt;/head&gt;
  
  &lt;body&gt;
    &lt;div id="app"&gt;&lt;/div&gt;
    &lt;!-- built files will be auto injected --&gt;
    &lt;script src="<strong>/sub-folder</strong>/static/config.js"&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;

Because I am using index_prod for production I must inform webpack about that. So, I need to change webpack.prod.config.js, by adding template property to HtmlWebpackPlugin:
newHtmlWebpackPlugin({
  filename: config.build.index,
<strong>  template: 'index_prod.html',</strong>
  inject: true,
  ...

Next, I must inform webpack to use new public path, so I must again change webpack.prod.config.js, by adding publicPath property to webpackConfig.output:
output: {
  path: config.build.assetsRoot,
<strong>  publicPath: '/sub-folder/',</strong>
  filename:utils.assetsPath('js/[name].[chunkhash].js'),
  chunkFilename:utils.assetsPath('js/[id].[chunkhash].js')
},

Then I must inform router to use new public path, so I must add base property to router object
let router = new Router({
  routes: […]
  base: ‘/sub-folder/’,
  mode: ‘history’
})
And that’s it! Now I can copy DIST folder to /sub-folder/ on production.

Parse XML documents in JavaScript!

The problem on webpage was simple. Read XML file and process it. 1,2,3 and script was written, except it wasn’t working in IE. Ok, IE is an odd creature, but still. It wasn’t working even in IE 11!

The problem was .innerHTML property which (at first, I thought) was returning undefined, but then I found out that the method was undefined, it was not there!

JavaScript DOM object in IE didn’t have .innerHTML property! WTF

After few lost hours I found the bug!

If you parse XML string with this code:

return (new window.DOMParser().parseFromString(xmlStr, <strong>'text/xml'</strong>))

you get totally different object than this one:
return (new window.DOMParser().parseFromString(xmlStr, <strong>'text/html'</strong>))

If you use ‘text/html’ instead of ‘text/xml’ in IE everything works as expected. All objects (all nodes, elements …) will have all the expected properties!

As I said; few lost hours 🙁

Running C++ Builder 6.0 on new win 10

As my main source of income is still written in C++ Builder 6.0, I am stuck with it. Today I updated my system to Windows 10 (build 1709) and Builder refuses to run.

I was left with registry form and registration was not the solution. I just couldn’t register it.

To make the story short. Here is the solution:

  1. Open registry
  2. Delete LM value from HKCU\Software\Borland\C++Builder\6.0
  3. Export the whole key HKEY_CURRENT_USER\Software\Borland
  4. In exported file change \Software\ ==> \Software\Wow6432Node\
  5. Import changed registry file
  6. Reregister Borland C++ Builder

At it is working again! 🙂

PS: The solution is to move (copy) the whole Borland subtree to HKCU\Software\Wow6432Node\ subtree, because it is an old x86 app.

Davčne blagajne

V januarju 2016 bo v Sloveniji obvezna uporaba davčnih blagajn za vse gotovinska plačila. Na spletni strani FURS-a si lahko preberete vse tehnične specifikacije potrebne za davčno potrjevanje računov.

Kako poteka davčno potrjevanje računov

V osnovi je to zelo preprost in uporaben sistem. Pred izpisom (izdajo računa) se morate namreč povezati na strežnik davčne uprave in nanj prenesti podatke o računu, FURS pa vam vrne edinstveno številko EOR (Edinstvena Oznaka Računa), ki jo morate izpisati na računu in s katero dokazujete, da ste račun tudi zares poslali na FURS.

Ozadje

Seveda pa se podrobnosti vedno skrivajo v ozadju in povezava na FURS niti ni tako trivialno preprosta oz. povezava je preprosta, saj gre za standarden internetni protokol, vendar pa je tja potrebno poslati digitalno podpisan račun in tukaj se stvari malce zapletejo.

Zastonj rešitev na dlani

Da bi se ne bilo potrebno vsakumur ukvarjati s:

  • podrobnostmi digitalnega podpisa,
  • podrobnostmi izračuna zaščitne številke (ZOI)
  • podrobnostmi izrisa QR kode

sem na GITHub-u odprl rešitev, ki je vsakomur na voljo povsem brezplačno. Za uporabo rešitve potrebujete le računalnik z operacijskim sistemom Windows. Ker je v SLO še kar nekaj blagajn, ki tečejo na operacijskem sistemu Windows XP, je podprt tudi slednji.

Minimalne zahteve za uporabo te povsem zastonj rešitve je:

  1. Win XP, SP 3, .NET 4.0 – ali seveda katerikoli novejši WIN sistem
  2. Uporaba .NET razvojnega okolja (C#, VB.NET ali pa tudi kakšnega bolj eksotičnega, a teh verjetno ni veliko 🙂 )

Izgled testnega programa
Izgled testnega programa

Kaj pa če ne uporabljate .NET razvojnega okolja

V tem primeru pa seveda ponujene knjižnice ne morete neposredno izkoristiti, a dokler vaš program teče v WIN okolju je problem rešen. Ponudim vam lahko namreč sledeč možnosti povezave:

  1. EXE program, ki ga pokličete iz vašega programa in slednji
    • Pregleda račun ali slednji vsebuje vse zahtevane elemente
    • Izračuna zaščitno kodo (ZOI)
    • Obda račun z zahtevano ovojnico
    • Ga digitalno podpiše in pošlje na FURS
    • Od FURS-a sprejme odgovor in ga posreduje v vaš program
    • Podroben opis EXE programa najdete tukaj.
  2. WIN service, ki nadzoruje dve poljubni mapi in čim vi odložite podatke o računu v prvo mapo, jih program vzame, obdela, pošlje na FURS (enako kot v primeru EXE programa) in rezultat izpiše v izhodno mapo
  3. V kolikor želite uporabiti knjižnico sami in elemente zvezati neposredno v vaš program, vam lahko ponudim ActiveX komponento, ki naredi vse našteto
  4. Če imate zares veliko blagajn pa lahko vse skupaj zavrtite na nekem strežniku in ponudim vam lahko ASP.NET spletne storitve ki izvedejo vse omenjeno.

V kolikor torej potrebujete rešitev pišite na info@matjazev.net in rešitev boste dobili na dlani 😉

XAML DataGrid using one DataTable as lookup

Problem:

I have a DataTable with lookup column into another table. Another table is a small table so all the records can fit in small combobox and the user wants to be able to select lookup record with combobox.

Solution:

XAML has DataGridComboBoxColumn, but it is difficult to bind, because DataGridComboBoxColumn can’’t bind to Windows binding source. It is obviously quite know problem, but I could’n’t find an easy solution.

Test tables:

MAIN_TABLE 
{ 
  ID, 
  SubTableID, 
  ... other fields ...
}

SUB_TABLE 
{ 
  ID, 
  Description 
}

Naive

Naive solution, that doesn’’t work

<DataGrid ItemsSource="{Binding mainTable}">
  <DataGrid.Columns>
    <DataGridTextColumn Header="ID" 
                        Binding="{Binding ID}" />
    <DataGridTextColumn Header="SubID" 
                        Binding="{Binding SubTableID }" />
    <DataGridComboBoxColumn Header="Desc" 
                            ItemsSource="{Binding subTable}}" <!-- can't bind! -->
                            SelectedValueBinding="{Binding SubTableID}"
                            SelectedValuePath="ID" 
                            DisplayMemberPath="Description" />
  </DataGrid.Columns>            
</DataGrid>

The solution doesn’’t work because – for some reason– DataGridComboBoxColumn can’’t bind subTable even if it is defined exactly as mainTable in datasource for the whole xaml window.

Solution

The solution is quite easy. Create a resource that binds to sub table and then use that resurce sa data source 🙂


<DataGrid ItemsSource="{Binding mainTable}">
  <DataGrid.Resources>
    <CollectionViewSource x:Key="cvsSubTable" 
                          Source="{Binding subTable }"/> <!-- can bind! -->
  </DataGrid.Resources>
  <DataGrid.Columns>
    <DataGridTextColumn Header="ID" 
                        Binding="{Binding ID}" />
    <DataGridTextColumn Header="SubID" 
                        Binding="{Binding SubTableID }" />
    <DataGridComboBoxColumn Header="Desc" 
                            ItemsSource="{Binding Source={StaticResource cvsSubTable }}"  <!-- now it is ok! --> 
                            SelectedValueBinding="{Binding SubTableID}"
                            SelectedValuePath="ID" 
                            DisplayMemberPath="Description" />
  </DataGrid.Columns>            
</DataGrid>         

Lost 4 hours on that! Grrrr..

Lepljenje podatkov v že oblikovane celice

Danes sem na spletišču Slo-tech zasledil vprašanje, ki je kar pogosto, pa se ga dosedaj še nisem dotaknil.

Vprašanje je, kako vnaprej formirati številke da imajo v celici nek predpisan izgled. Primer je npr. telefonska številka, za katero večinoma želimo, da je prikazana v obliki (031) 668 532, čeravno je sama številka pač 031668532.

Oblikovanje takšnega izgleda je trivialno in ga bralci mojega bloga že poznateVendar pa se pojavi problem, ko v tako oblikovano celico prekopirate podatek iz druge celice.

Kopiranje

Po privzetem Excel vedno kopira tako vsebino, kot obliko celice in v našem primeru pač prekopira tudi obliko izvorne celice in zato se oblika v celici poruši.

Moj odgovor na tovrstne zagate je vedno, da vsebino vnašajte na enem mestu, končno (pravo, lepo) obliko pa prikazujte v drugih celicah, ki se na prve samo skljucujejo. S tem lepo ločite vnos in prikaz.

Seveda pa to ne gre vedno in mnogokrat preprost želite vnašati v celice, ki jih navkljub vsemu želite imeti drugače oblikovane. V tem primeru imate dve možnosti:

  1. Vedno pazite da izberete opcijo ‘Prilepi vrednosti’ in v tem primeru pač ne morete uporabiti bljižnice CTRL+V
  2. Na bljižnico CTRL+V postavite makro, ki izvede lepljenje po vrednosti. V tem primeru je problem, ker morate makro vedno priložiti in tudi oseba, ki uporablja takšen zvezek mora dovoliti izvajanje makrov.

Makro za lepljenje vrednosti

Makro za lepljenje samo vrednosti je zelo preprost:

Sub PrilepiSamoVrednosti()
 Selection.PasteSpecial Paste:=xlPasteValues, _
                        Operation:=xlNone, _
                        SkipBlanks:=False, _
                        Transpose:=False
End Sub

Vse kar je potrebno še storiti je, da temu makru določite bljižnico CTRL+V in vedno kadar boste uporabili CTRL+V (lepljenje) bo Excel poklical ta makro in prilepil samo vrednosti, ne pa tudi oblike.

Določanje bljižnice makra:

  1. Pritisnite ALT+F8 – odpre se seznam vseh markov
  2. V seznamu izberite makro ‘ PrilepiSamoVrednosti’ in pritisnite gumb Možnosti
  3. V kvadratek ob ‘CTRL+’ vpišite V

To je to.

Gmail koledar zna pošiljati SMS-e

Danes me je opomnik koledarja spet zmotil. Seveda sem ga prekinil in potem pa nanj pozabil. Malce sem razmišljal, da bi mi zelo ustrezalo, če bi namesto klasičnega opomnika dobil raje SMS.

Ne bodi len! Seveda sem se takoj zagnal na internet in pogledal, katera aplikacija mi zna za opomnik poslati SMS. Malce iščem med aplikacijami in njihovimi cenami/zmožnostmi… nakar opazim omembo Google koledarja 🙂

In res! Google koledar lahko – med vsemi ostalimi možnostmi – kot opomnik pošlje tudi SMS in to seveda zastonj.

Kako vključiti SMS pošiljanje opomnikov?

  1. Zaženete aplikacijo Google koledar in v gornjem desnem kotu Nastavitve.
    gmail_sms1
  2. Med nastavitvami koledarja boste našli tudi ‘Mobilne nastavitve’
    gmail_sms2
  3. Tu izberete državo in vpišete vašo telefonsko številko ter zahtevate varnostno kodo
    gmail_sms3
  4. Google vam po SMS-u pošlje varnostno kodo, ki jo vpišete v spodnje polje in to je to.
  5. Sedaj lahko kot opomnike uporabljate tudi SMS-e
    gmail_sms4