diff --git a/.github/BUILD.md b/.github/BUILD.md index fabe3caf67..ee0584528a 100644 --- a/.github/BUILD.md +++ b/.github/BUILD.md @@ -27,7 +27,7 @@ If you want to run a build without debugging, see [Building from source](#buildi In order to build the Umbraco source code locally with Visual Studio Code, first make sure you have the following installed. * [Visual Studio Code](https://code.visualstudio.com/) - * [dotnet SDK v6.0.2+](https://dotnet.microsoft.com/en-us/download) + * [dotnet SDK v7+](https://dotnet.microsoft.com/en-us/download) * [Node.js v14+](https://nodejs.org/en/download/) * npm v7+ (installed with Node.js) * [Git command line](https://git-scm.com/download/) @@ -72,7 +72,7 @@ When the page eventually loads in your web browser, you can follow the installer In order to build the Umbraco source code locally with Visual Studio, first make sure you have the following installed. - * [Visual Studio 2019 v16.8+ with .NET 6.0.2+](https://visualstudio.microsoft.com/vs/) ([the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects) + * [Visual Studio 2022 v17+ with .NET 7+](https://visualstudio.microsoft.com/vs/) ([the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects) * [Node.js v14+](https://nodejs.org/en/download/) * npm v7+ (installed with Node.js) * [Git command line](https://git-scm.com/download/) diff --git a/.github/workflows/issues-first-response.yml b/.github/workflows/issues-first-response.yml index 134a368785..30eee62afe 100644 --- a/.github/workflows/issues-first-response.yml +++ b/.github/workflows/issues-first-response.yml @@ -18,8 +18,6 @@ jobs: uses: actions/github-script@v6 with: script: | - const fetch = require('node-fetch') - const response = await fetch('https://collaboratorsv2.euwest01.umbraco.io/umbraco/api/comments/PostComment', { method: 'post', body: JSON.stringify({ diff --git a/.github/workflows/pr-first-response.yml b/.github/workflows/pr-first-response.yml index 991a5d0808..f880c5e7bf 100644 --- a/.github/workflows/pr-first-response.yml +++ b/.github/workflows/pr-first-response.yml @@ -18,8 +18,6 @@ jobs: uses: actions/github-script@v6 with: script: | - const fetch = require('node-fetch') - const response = await fetch('https://collaboratorsv2.euwest01.umbraco.io/umbraco/api/comments/PostComment', { method: 'post', body: JSON.stringify({ diff --git a/.github/workflows/up-for-grabs-response.yml b/.github/workflows/up-for-grabs-response.yml index eb16eb7779..b571a7bf9f 100644 --- a/.github/workflows/up-for-grabs-response.yml +++ b/.github/workflows/up-for-grabs-response.yml @@ -20,7 +20,6 @@ jobs: uses: actions/github-script@v6 with: script: | - const fetch = require('node-fetch'); const response = await fetch('https://collaboratorsv2.euwest01.umbraco.io/umbraco/api/comments/PostComment', { method: 'post', body: JSON.stringify({ diff --git a/Directory.Build.props b/Directory.Build.props index 37b2e66977..01cd3b1064 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,7 +31,7 @@ false true - 11.0.0-rc1 + 11.0.0 true true diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index deccda508c..cf19248899 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -89,10 +89,10 @@ stages: arguments: '--configuration $(buildConfiguration) --no-restore -p:ContinuousIntegrationBuild=true' - script: | version="$(Build.BuildNumber)" - echo "varsion: $version" + echo "Version: $version" major="$(echo $version | cut -d '.' -f 1)" - echo "major version: $major" + echo "Major version: $major" echo "##vso[task.setvariable variable=majorVersion;isOutput=true]$major" displayName: Set major version @@ -113,10 +113,7 @@ stages: } } - foreach($csproj in Get-ChildItem -Recurse -Filter *.csproj) - { - dotnet pack $csproj --configuration $(buildConfiguration) --no-build --output $(Build.ArtifactStagingDirectory)/nupkg - } + dotnet pack $(solution) --configuration $(buildConfiguration) --no-build --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg - script: | sha="$(Build.SourceVersion)" sha=${sha:0:7} diff --git a/src/Umbraco.Cms.Api.Management/Umbraco.Cms.Api.Management.csproj b/src/Umbraco.Cms.Api.Management/Umbraco.Cms.Api.Management.csproj index 28e15c20c7..aad0f5626c 100644 --- a/src/Umbraco.Cms.Api.Management/Umbraco.Cms.Api.Management.csproj +++ b/src/Umbraco.Cms.Api.Management/Umbraco.Cms.Api.Management.csproj @@ -24,7 +24,6 @@ - diff --git a/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj b/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj index f627057d24..d537b116c0 100644 --- a/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj +++ b/src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj @@ -10,8 +10,6 @@ - - diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index a8ef00e9c0..67137ab487 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Core.Configuration.Grid; +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridConfig : IGridConfig { public GridConfig( diff --git a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs index 7e82fe2e95..290836d31e 100644 --- a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs @@ -158,6 +158,7 @@ public class ContentSettings internal const bool StaticDisableDeleteWhenReferenced = false; internal const bool StaticDisableUnpublishWhenReferenced = false; internal const bool StaticAllowEditInvariantFromNonDefault = false; + internal const bool StaticShowDomainWarnings = true; /// /// Gets or sets a value for the content notification settings. @@ -267,4 +268,10 @@ public class ContentSettings /// Gets or sets the allowed external host for media. If empty only relative paths are allowed. /// public string[] AllowedMediaHosts { get; set; } = Array.Empty(); + + /// + /// Gets or sets a value indicating whether to show domain warnings. + /// + [DefaultValue(StaticShowDomainWarnings)] + public bool ShowDomainWarnings { get; set; } = StaticShowDomainWarnings; } diff --git a/src/Umbraco.Core/Constants-HealthChecks.cs b/src/Umbraco.Core/Constants-HealthChecks.cs index 94633c4502..4bf250e4d6 100644 --- a/src/Umbraco.Core/Constants-HealthChecks.cs +++ b/src/Umbraco.Core/Constants-HealthChecks.cs @@ -50,6 +50,7 @@ public static partial class Constants public const string ClickJackingCheck = "https://umbra.co/healthchecks-click-jacking"; public const string HstsCheck = "https://umbra.co/healthchecks-hsts"; public const string NoSniffCheck = "https://umbra.co/healthchecks-no-sniff"; + [Obsolete("This link is not used anymore in the XSS protected check.")] public const string XssProtectionCheck = "https://umbra.co/healthchecks-xss-protection"; public const string ExcessiveHeadersCheck = "https://umbra.co/healthchecks-excessive-headers"; diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/bs.xml b/src/Umbraco.Core/EmbeddedResources/Lang/bs.xml new file mode 100644 index 0000000000..f7b763bdfa --- /dev/null +++ b/src/Umbraco.Core/EmbeddedResources/Lang/bs.xml @@ -0,0 +1,2878 @@ + + + + Smayke95 + https://github.com/Smayke95 + + + Kultura i imena hostova + Revizije + Pregledaj čvor + Promijeni Tip Dokumenta + Kopiraj + Kreiraj + Izvezi + Kreiraj Paket + Kreiraj grupu + Obriši + Onemogući + Uredi postavke + Isprazni kantu za smeće + Omogući + Izvezi Tip Dokumenta + Uvezi Tip Dokumenta + Uvezi Paket + Uredi u Platnu + Izađi + Pomakni + Obavještenja + Ograničite javni pristup + Objavi + Poništi objavu + Ponovo učitaj + Ponovo objavite cijelu stranicu + Ukloni + Preimenuj + Vrati + Odaberite gdje ćete kopirati + Odaberite gdje ćete pomaknuti + Odaberite gdje ćete uvesti + do u strukturi stabla ispod + Odaberite gdje želite kopirati odabrane stavke + Odaberite gdje želite pomaknuti odabrane stavke + je pomaknuta u + je kopirana u + je obrisana + Dozvole + Vraćanje unazad + Pošalji na objavljivanje + Pošalji na prijevod + Postavi grupu + Sortiraj + Prevedi + Ažuriraj + Postavi dozvole + Otključaj + Kreirajte Predložak Sadržaja + Ponovo pošaljite pozivnicu + + + Sadržaj + Administracija + Struktura + Ostalo + + + Dozvolite pristup za dodjelu kulture i imena hostova + Dozvolite pristup za pregled dnevnika historije čvora + Dozvolite pristup za pregled čvora + Dozvolite pristup za promjenu Tipa Dokumenta za čvor + Dozvolite pristup za kopiranje čvora + Dozvolite pristup za kreiranje čvora + Dozvolite pristup za brisanje čvora + Dozvolite pristup za pomicanje čvora + Dozvolite pristup za postavljanje i promjenu ograničenja pristupa za čvor + Dozvolite pristup za objavljivanje čvora + Dozvolite pristup da poništavanje objave čvora + Dozvolite pristup za promjenu dozvola za čvor + Dozvolite pristup za vraćanje čvora u prethodno stanje + Dozvolite pristup za slanje čvora na odobrenje prije objavljivanja + Dozvolite pristup za slanje čvora na prijevod + Dozvolite pristup za promjenu sortiranja čvorova + Dozvolite pristup za prevođenje čvora + Dozvolite pristup za spremanje čvora + Dozvolite pristup za kreiranje Predloška Sadržaja + Dozvolite pristup za podešavanje obaviještenja za čvorove + + + Sadržaj + Info + + + Dozvola odbijena. + Dodaj novu domenu + Dodaj postojeću domenu + ukloni + Nevažeći čvor. + Jedna ili više domena imaju nevažeći format. + Domena je već dodijeljena. + Jezik + Domena + Nova domena '%0%' je kreirana + Domena '%0%' je obrisana + Domena '%0%' je već dodijeljena + Domena '%0%' je ažurirana + Uredi trenutne domene + + + Naslijedi + Kultura + + ili naslijedite kulturu od roditeljskih čvorova. Također će se primijeniti
+ na trenutni čvor, osim ako se domena u nastavku ne primjenjuje.]]> +
+ Domene + + + Obriši odabir + Odaberi + Uradi nešto drugo + Boldiraj + Otkaži uvlačenje pasusa + Umetni polje obrasca + Umetnite grafički naslov + Uredi Html + Uvuci pasus + Kurziv + Centriraj + Poravnaj lijevo + Poravnaj desno + Umetni link + Umetni lokalni link (sidro) + Lista + Numerička lista + Umetni makro + Umetni sliku + Objavi i zatvori + Objavi sa potomcima + Uredite odnose + Povratak na listu + Spremi + Spremi i zatvori + Spremi i objavi + Spremi i pošalji na odobrenje + Spremi prikaz liste + Zakaži + Spremi i pregledaj + Pregled je onemogućen jer nije dodijeljen predložak + Odaberi stil + Prikaži stilove + Umetni tabelu + Spremi i generiši modele + Poništi + Ponovi + Obriši tag + Otkaži + Potvrdi + Više opcija za objavljivanje + Pošalji + + + Pregled za + Sadržaj je izbrisan + Sadržaj nije objavljen + Sadržaj spremljen i objavljen + Sadržaj spremljen i objavljen za jezike: %0% + Sadržaj spremljen + Sadržaj spremljen za jezike: %0% + Sadržaj premješten + Sadržaj kopiran + Sadržaj vraćen + Sadržaj poslan na objavljivanje + Sadržaj poslan na objavljivanje za jezike: %0% + Sortiranje podređenih stavki je izvršio korisnik + %0% + Čišćenje je onemogućeno za verziju: %0% + Čišćenje je omogućeno za verziju: %0% + Kopiraj + Objavljeno + Objavi + Pomakni + Spremljeno + Spremi + Obriši + Poništi objavu + Vrat + Pošalji na objavljivanje + Pošalji na objavljivanje + Sortiraj + Prilagođeno + Spremi + Spremi + Historija (sve varijante) + + + Naziv mape ne može sadržavati nedozvoljene znakove. + Nije uspjelo brisanje stavke: %0% + + + Da li je objavljeno + O ovoj stranici + Alias + (kako biste opisali sliku preko telefona) + Alternativni linkovi + Kliknite da uredite ovu stavku + Kreirao + Originalni autor + Ažurirao + Kreirano + Datum i vrijeme kreiranja ovog dokumenta + Tip dokumenta + Uređivanje + Ukloni na + Ova stavka je promijenjena nakon objavljivanja + Ova stavka nije objavljena + Posljednje objavljeno + Nema stavki za prikaz + Nema stavki za prikaz na listi. + Nije dodan sadržaj + Nijedan član nije dodan + Tip medija + Link do medijske stavke + Grupa članova + Uloga + Tip člana + Nisu napravljene nikakve promjene + Nije odabran datum + Naslov stranice + Ova medijska stavka nema vezu + Svojstva + Ovaj dokument je objavljen, ali nije vidljiv jer nadređeni '%0%' nije objavljen + Ova kultura je objavljena, ali nije vidljiva jer nije objavljena na nadređenom '%0%' + Ovaj dokument je objavljen, ali nije u kešu + Nije moguće dobiti URL + Ovaj dokument je objavljen, ali njegov URL je u koliziji sa sadržajem %0% + Ovaj dokument je objavljen, ali njegov URL se ne može preusmjeriti + Objavi + Objavljeno + Objavljeno (čeka izmjene) + Status publikacije + + %0% i sve stavke sadržaja ispod i time čineći njihov sadržaj javno dostupnim.]]> + + + Objavi na + Poništi objavu na + Obriši datum + Postavi datum + Sortiranje je ažurirano + Da biste sortirali čvorove, jednostavno prevucite čvorove ili kliknite na jedno od zaglavlja kolona. Možete odabrati + više čvorova držeći tipku "shift" ili "control" dok birate + + Statistika + Naslov (opcionalno) + Alternativni tekst (opcionalno) + Natpis (opcionalno) + Tip + Poništi objavu + Neobjavljeno + Nije kreirano + Poslednji put uređeno + Datum/vrijeme uređivanja ovog dokumenta + Ukloni fajlove + Kliknite ovdje da uklonite sliku iz medijske stavke + Kliknite ovdje da uklonite fajl iz medijske stavke + Link do dokumenta + Član grupe + Nije član grupe + Dječiji artikli + Meta + Ovo se prevodi kao sljedeće vrijeme na serveru: + + Šta ovo znači?]]> + Jeste li sigurni da želite izbrisati ovu stavku? + Svojstvo %0% koristi uređivač %1% koji nije podržan za Ugniježđeni + Sadržaj. + + Jeste li sigurni da želite izbrisati sve stavke? + Za ovo svojstvo nisu konfigurirani tipovi sadržaja. + Dodajte tip elementa + Odaberi tip elementa + Odaberite grupu čija svojstva trebaju biti prikazana. Ako je ostavljeno prazno, + koristit će se prva grupa na tipu elementa. + + Unesite angular izraz za procjenu svake stavke za njeno + ime. Koristi + + za prikaz indeksa stavke + Odabrani tip elementa ne sadrži nijednu podržanu grupu (ovaj uređivač ne podržava kartice, promijenite ih u grupe ili koristite uređivač liste blokova). + Dodajte još jedan okvir za tekst + Uklonite ovaj okvir za tekst + Korijen Sadržaja + Uključite neobjavljeni sadržaj. + Ova vrijednost je skrivena. Ako vam je potreban pristup da vidite ovu vrijednost, obratite se + administratoru web stranice. + + Ova vrijednost je skrivena. + Koje jezike želite da objavite? + Koje jezike želite poslati na odobrenje? + Koje jezike želite da zakažete? + Odaberite jezike za poništavanje objavljivanja. Poništavanje objavljivanja obaveznog jezika će + poništiti objavljivanje svih jezika. + + Sve nove varijante će biti sačuvane. + Koje varijante želite da objavite? + Odaberite koje varijante želite sačuvati. + Za objavljivanje su potrebne sljedeće varijante: + Nismo spremni za objavljivanje + Spremno za objavljivanje? + Spremno za spremanje? + Resetuj fokusnu tačku + Pošalji na odobrenje + Odaberite datum i vrijeme za objavljivanje i/ili poništavanje objave stavke sadržaja. + Napravi novi + Zalijepi iz međuspremnika + Ova stavka je u korpi za otpatke + + + %0%]]> + Prazno + Odaberite predložak sadržaja + Predložak sadržaja kreiran + Predložak sadržaja je kreiran od '%0%' + Drugi predložak sadržaja sa istim imenom već postoji + Predložak sadržaja je unaprijed definiran sadržaj koji uređivač može odabrati da koristi + kao osnovu za kreiranje novog sadržaja + + + + Kliknite za učitavanje + ili kliknite ovdje da odaberete fajlove + Nije moguće učitati ovu datoteku, ona nema odobreni tip datoteke + Nije moguće učitati ovu datoteku, tip medija sa pseudonimom '%0%' nije dozvoljen ovdje + Nije moguće učitati ovu datoteku, ona nema važeći naziv datoteke + Maksimalna veličina datoteke je + Korijen medija + Kreiranje foldera pod ID-om roditelja nije uspjelo %0% + Preimenovanje foldera sa ID-om %0% nije uspjelo + Prevucite i ispustite svoje datoteke u područje + + + Kreirajte novog člana + Svi članovi + Grupe članova nemaju dodatna svojstva za uređivanje. + Dvostruka provjera autentičnosti + + + Kopiranje tipa sadržaja nije uspjelo + Premještanje tipa sadržaja nije uspjelo + + + Kopiranje tipa medija nije uspjelo + Premještanje tipa medija nije uspjelo + Automatski odabir + + + Kopiranje tipa člana nije uspjelo + + + Gdje želite kreirati novi %0% + Kreirajte stavku pod + Odaberite vrstu dokumenta za koju želite da napravite predložak sadržaja + Unesite naziv foldera + Odaberite vrstu i naslov + + Dokument Tip unutar sekcije Postavke, uređivanjem Dozvoljeni tipovi podređenih čvorova unutar Dozvole.]]> + + Dokument Tip unutar sekcije Postavke.]]> + Odabrana stranica u stablu sadržaja ne dozvoljava nijednu stranicu + biti kreiran ispod njega. + + Uredi dozvole za ovaj tip dokumenta + Kreiraj novi tip dokumenta + + Dokument Tip unutar sekcije Postavke, izmjenom Dozvoli kao root opcije unutar Dozvole.]]> + + Media Tip unutar sekcije Postavke, uređivanjem Dozvoljeni tipovi podređenih čvorova unutar Dozvole.]]> + Odabrani medij u stablu ne dopušta bilo koji drugi medij + kreiran ispod njega. + + Uredi dozvole za ovaj tip medija + Tip dokumenta bez predloška + Tip dokumenta sa predloškom + Definicija podataka za stranicu sadržaja koja se može kreirati + uređivača u stablu sadržaja i direktno je dostupan preko URL-a. + + Tip dokumenta + Definicija podataka za komponentu sadržaja koju mogu kreirati urednici u + stablo sadržaja i biti izabran na drugim stranicama, ali nema direktan URL. + + Tip elementa + Definira shemu za ponavljajući skup svojstava, na primjer, u 'Bloku + Uređivač svojstava Lista' ili 'Ugniježđeni sadržaj'. + + Kompozicija + Definira višekratni skup svojstava koja se mogu uključiti u definiciju + više drugih vrsta dokumenata. Na primjer, skup 'Common Page Settings'. + + Mapa + Koristi se za organiziranje tipova dokumenata, sastava i tipova elemenata kreiranih u ovome + Stablo vrste dokumenta. + + Nova mapa + Novi tip podatka + Novi JavaScript fajl + Novi prazan djelomični prikaz + Novi djelomični prikaz za makro + Novi djelomični prikaz iz isječka + Novi djelomični prikaz za makro iz isječka + Novi djelomični prikaz za makro (bez makroa) + Novi CSS fajl + Novi Rich Text Editor CSS fajl + + + Pregledajte svoju web stranicu + - Sakrij + Ako se Umbraco ne otvara, možda ćete morati dozvoliti iskačuće prozore sa ove stranice + je otvoren u novom prozoru + Restart + Posjetite + Dobrodošli + + + Ostani + Odbacite promjene + Imate nesačuvane promjene + Jeste li sigurni da želite otići s ove stranice? - imate nesačuvane + promjene + + Objavljivanjem će odabrane stavke biti vidljive na stranici. + Poništavanje objavljivanja će ukloniti odabrane stavke i sve njihove potomke sa + stranice. + + Poništavanje objavljivanja će ukloniti ovu stranicu i sve njene potomke sa stranice. + Imate nesačuvane promjene. Promjenom vrste dokumenta odbacit će se promjene. + + + Završeno + Izbrisana %0% stavka + Izbrisano %0% stavki + Izbrisana %0% od %1% stavka + Izbrisano %0% od %1% stavki + Objavljeno %0% stavka + Objavljeno %0% stavki + Objavljeno %0% od %1% stavka + Objavljeno %0% od %1% stavki + Ukinuta objava za %0% stavku + Ukinuta objava za %0% stavki + Ukinuta objava za %0% od %1% stavku + Ukinuta objava za %0% od %1% stavki + Pomjerena %0% stavka + Pomjereno %0% stavki + Pomjereno %0% od %1% stavku + Pomjereno %0% od %1% stavki + Kopirana %0% stavka + Kopirano %0% stavki + Kopirano %0% od %1% stavku + Kopirano %0% od %1% stavki + + + Naslov linka + Link + Anchor / querystring + Naziv + Upravljajte imenima hostova + Zatvorite ovaj prozor + Jeste li sigurni da želite izbrisati + %0% od %1% stavki]]> + Jeste li sigurni da želite onemogućiti + Jeste li sigurni da želite ukloniti + %0%]]> + Jeste li sigurni? + Jeste li sigurni? + Izreži + Uredi stavku iz rječnika + Uredi jezik + Uredite odabrane medije + Umetnite lokalnu vezu + Umetni znak + Umetnite grafički naslov + Umetnite sliku + Umetnite link + Kliknite da dodate makro + Umetnite tabelu + Ovo će izbrisati jezik + Promjena kulture jezika može biti skupa operacija i rezultirat će + u kešu sadržaja i indeksima koji se rekonstruišu + + Posljednji put uređeno + Link + Interni link: + Kada koristite lokalne veze, umetnite "#" ispred linka + Otvori u novom prozoru? + Ovaj makro ne sadrži svojstva koja možete uređivati + Zalijepi + Uredite dozvole za + Postavite dozvole za + Postavite dozvole za %0% za grupu korisnika %1% + Odaberite grupe korisnika za koje želite postaviti dozvole + Stavke u korpi za otpatke se sada brišu. Molimo vas da ne zatvarate ovaj prozor + dok se ova operacija odvija + + Korpa za otpatke je sada prazna + Kada se predmeti izbrišu iz korpe za otpatke, oni će nestati zauvijek + + regexlib.com web servis trenutno ima nekih problema, nad kojima nemamo kontrolu. Veoma nam je žao zbog ove neugodnosti.]]> + Potražite regularni izraz da dodate provjeru valjanosti u polje obrasca. Primjer: 'e-pošta, + 'poštanski broj', 'URL'. + + Ukloni makro + Obavezno polje + Stranica je ponovo indeksirana + Predmemorija web stranice je osvježena. Sav objavljeni sadržaj je sada ažuriran. Dok će sav + neobjavljen sadržaj ostati neobjavljen + + Keš web stranice će biti osvježen. Svi objavljeni sadržaji će biti ažurirani, dok će sav + neobjavljeni sadržaj ostati neobjavljen. + + Broj kolona + Broj redova + Kliknite na sliku da vidite punu veličinu + Izaberite stavku + Prikaži keš stavku + Odnosi se na original + Uključiti potomke + Najljubaznija zajednica + Link na stranicu + Otvara povezani dokument u novom prozoru ili kartici + Link do medija + Odaberite početni čvor sadržaja + Odaberite medije + Odaberite tip medija + Odaberite ikonu + Odaberite stavku + Odaberite vezu + Odaberite makro + Odaberite sadržaj + Odaberite tip sadržaja + Odaberite početni čvor medija + Odaberite člana + Odaberite grupu članova + Odaberite tip članova + Odaberite čvor + Odaberite jezike + Odaberite sekcije + Odaberite korisnika + Odaberite korisnike + Ikone nisu pronađene + Nema parametara za ovaj makro + Nema dostupnih makroa za umetanje + Eksterni provajderi prijave + Detalji o izuzetku + Stacktrace + Inner Exception + Povežite svoje + Odspojite svoju vezu + račun + Odaberite uređivač + Odaberite isječak + Ovo će izbrisati čvor i sve njegove jezike. Ako želite da izbrišete samo jedan + jezik, trebali biste poništiti objavljivanje čvora na tom jeziku. + + %0%.]]> + %0% iz grupe %1%]]> + Da, ukloni + Brišete izgled + Promjena izgleda će rezultirati gubitkom podataka za bilo koji postojeći sadržaj koji je zasnovan na ovoj konfiguraciji. + + + + Da biste uvezli stavku iz rječnika, pronađite ".udt" datoteku na svom računaru klikom na + Dugme "Uvezi" (na sljedećem ekranu će se tražiti da potvrdite) + + Stavka iz rječnika ne postoji. + Nadređena stavka ne postoji. + Ne postoje stavke iz rječnika. + U ovoj datoteci nema stavki iz rječnika. + Nisu pronađene stavke iz rječnika. + Kreirajte stavku iz rječnika + + + %0%' ispod + ]]> + Kultura + + Pregled riječnika + + + Konfigurisani pretraživači + Prikazuje svojstva i alate za bilo koji konfigurirani pretraživač (tj + višeindeksni pretraživač) + + Vrijednosti polja + Zdravstveno stanje + Zdravstveno stanje indeksa i da li se može pročitati + Indeksi + Indeks info + Sadržaj u indeksu + Navodi svojstva indeksa + Upravljajte Examine-ovim indeksima + Omogućava vam da vidite detalje svakog indeksa i pruža neke alate za + upravljanje indeksima + + Ponovo izgradi indeks + + Ovisno o tome koliko sadržaja ima na vašoj web lokaciji, to može potrajati.
+ Ne preporučuje se obnavljanje indeksa u vrijeme velikog prometa na web stranici ili kada urednici uređuju sadržaj. + ]]> +
+ Pretraživači + Pretražite indeks i pogledajte rezultate + Alati + Alati za upravljanje indeksom + polja + Indeks se ne može pročitati i morat će se ponovo izgraditi + Proces traje duže od očekivanog, provjerite Umbraco dnevnik da vidite + je li bilo grešaka tokom ove operacije + + Ovaj indeks se ne može ponovo izgraditi jer mu nije dodijeljen + IIndexPopulator + + + Unesite svoje korisničko ime + Unesite svoju lozinku + Potvrdite lozinku + Imenujte %0%... + Unesite ime... + Unesite email... + Unesite korisničko ime... + Labela... + Unesite opis... + Unesite za pretragu... + Unesite za filtriranje... + Unesite da dodate oznake (pritisnite enter nakon svake oznake)... + Unesite vaš email + Unesite poruku... + Vaše korisničko ime je obično vaš email + #value ili ?key=value + Unesite alias... + Generišite alias... + Kreiraj stavku + Uredi + Ime + + + Kreirajte prilagođeni prikaz liste + Ukloni prilagođeni prikaz liste + Tip sadržaja, tip medija ili tip člana s ovim aliasom već postoji + + + Preimenovano + Ovdje unesite novi naziv mape + %0% je preimenovan u %1% + + + Dodajte vrijednost + Tip podataka baze podataka + Uređivač osobine GUID + Uređivač osobina + Dugmad + Omogući napredne postavke za + Omogući kontekstni meni + Maksimalna zadana veličina umetnutih slika + Povezani stilovi + Prikaži oznaku + Širina i visina + Odaberite mapu za premještanje + do u strukturi stabla ispod + je premeštena ispod + + + Vaši podaci su sačuvani, ali prije nego što možete objaviti ovu stranicu postoje neke + greške koje prvo morate ispraviti: + + Trenutni provajder članstva ne podržava promjenu lozinke + (Omogući preuzimanje lozinke mora biti uključeno) + + %0% već postoji + Bilo je grešaka: + Bilo je grešaka: + Lozinka treba da ima najmanje %0% znakova i da sadrži najmanje %1% + znakova koji nisu alfanumerički + + %0% mora biti cijeli broj + Polje %0% na kartici %1% je obavezno + %0% je obavezno polje + %0% na %1% nije u ispravnom formatu + %0% nije u ispravnom formatu + + + Primljena greška sa servera + Administrator je zabranio navedeni tip datoteke + NAPOMENA! Iako je CodeMirror omogućen konfiguracijom, on je onemogućen u + Internet Explorer-u jer nije dovoljno stabilan. + + Unesite i pseudonim i ime na novu vrstu osobine! + Postoji problem sa pristupom za čitanje/pisanje određenoj datoteci ili fascikli + Greška pri učitavanju skripte djelomičnog prikaza (fajl: %0%) + Unesite naslov + Molimo odaberite tip + Napravit ćete sliku veću od originalne veličine. Jeste li sigurni + da želite nastaviti? + + Početni čvor je obrisan, kontaktirajte svog administratora + Molimo označite sadržaj prije promjene stila + Nema dostupnih aktivnih stilova + Postavite kursor lijevo od dvije ćelije koje želite spojiti + Ne možete podijeliti ćeliju koja nije spojena. + Ovo svojstvo je nevažeće + + + O + Akcija + Akcije + Dodaj + Alias + Sve + Da li ste sigurni? + Nazad + Nazad na pregled + Rub + od + Otkaži + Margina ćelije + Odaberi + Očisti + Zatvori + Zatvori prozor + Zatvori okno + Komentar + Potvrdi + Ograniči + Ograniči proporcije + Sadržaj + Nastavi + Kopiraj + Kreiraj + Baza podataka + Datum + Podrazumjevano + Obriši + Obrisano + Brisanje... + Dizajn + Riječnik + Dimenzije + Otkaži + Dole + Preuzimanje + Uredi + Uređeno + Elementi + Email + Greška + Polje + Pronađi + Prvi + Fokusna tačka + Generalno + Grupe + Grupa + Visina + Pomoć + Sakrij + Historija + Ikona + Id + Uvezi + Pretraži samo ovu mapu + Info + Unutrašnja margina + Umetni + Instaliraj + Nevažeći + Poravnaj + Labela + Jezik + Zadnji + Izgled + Linkovi + Učitavanje + Zaključano + Prijava + Odjavi se + Odjavi se + Makro + Obavezno + Poruka + Pomakni + Ime + Novi + Sljedeći + Ne + Ime čvora + od + Isključeno + OK + Otvori + Opcije + Uključeno + ili + Poredaj po + Lozinka + Putanja + Jedan momenat molim... + Prethodni + Svojstva + Pročitaj više + Ponovo izgradi + Email za primanje obrasca + Kanta za smeće + Vaša kanta za smeće je prazna + Ponovo učitaj + Preostalo + Izbriši + Preimenuj + Obnovi + Obavezno + Povratiti + Pokušaj ponovo + Permisije + Planirano objavljivanje + Umbraco info + Pretraga + Žao nam je, ne možemo pronaći ono što tražite. + Nije dodana nijedna stavka + Server + Postavke + Prikaži + Prikaži stranicu na Pošalji + Veličina + Sortiranje + Status + Potvrdi + Uspjeh + Tip + Ime tipa + Unesite za pretragu... + ispod + Gore + Ažuriraj + Nadogradi + Prenesi + URL + Korisnik + Korisničko ime + Vrijednost + Pogled + Dobrodošli... + Širina + Da + Mapa + Rezultati pretrage + Promijeni redosljed + Završio sam sa promjenom redosljeda + Pregled + Promijeni lozinku + do + Prikaz liste + Spremanje... + trenutni + Ugradi + odabran + Ostalo + Članci + Videi + Avatar za + Zaglavlje + sistemsko polje + Posljednje ažurirano + + + Plava + + + Dodaj grupu + Dodaj svojstvo + Dodaj urednika + Dodaj šablon + Dodajte podređeni čvor + Dodaj dijete + Uredite tip podataka + Krećite se po odjeljcima + Prečice + prikaži prečice + Uključi prikaz liste + Uključi dozvoli kao root + Redovi za komentarisanje/dekomentarisanje + Uklonite liniju + Kopiraj linije gore + Kopiraj linije dole + Pomakni linije gore + Pomakni linije dole + Općenito + Uređivač + Uključi dozvoli varijante kulture + + + Boja pozadine + Boldirano + Boja teksta + Font + Tekst + + + Stranica + + + Instalacija se ne može povezati s bazom podataka. + Nije moguće sačuvati web.config datoteku. Molimo izmijenite konekcijski string + ručno. + + Vaša baza podataka je pronađena i identificirana je kao + Konfiguracija baze podataka + + Instaliraj za instalaciju Umbraco %0% baze podataka + ]]> + + + Dalje da nastavite.]]> + Baza podataka nije pronađena! Provjerite jesu li informacije u "konekcijskom string" u "web.config" fajlu ispravne.

+

Da nastavite, uredite "web.config" fajl. (koristeći Visual Studio ili vaš omiljeni uređivač teksta), skrolujte do dna, dodajte konekcijski string za vašu bazu podataka u ključ nazvan "UmbracoDbDSN" i sačuvajte fajl.

+

+ Kliknite na pokušaj ponovo dugme kada završite.
+ + Više informacija o uređivanju web.config fajla možete pronaći ovdje.

]]>
+ + Molimo kontaktirajte svog ISP-a ako je potrebno. + Ako instalirate na lokalnoj mašini ili serveru, možda će vam trebati informacije od administratora sistema.]]> + + Pritisnite nadogradnja za nadogradnju vaše baze podataka na Umbraco %0%

+

+ Ne brinite - nijedan sadržaj neće biti obrisan i sve će nastaviti raditi nakon toga! +

+ ]]>
+ Pritisnite Dalje da nastavite.]]> + + Dalje da nastavite sa čarobnjakom za konfiguraciju]]> + + Zadanu korisničku lozinku treba promijeniti!]]> + + Zadani korisnik je onemogućen ili nema pristup Umbraco!

Ne treba preduzimati nikakve daljnje radnje. Pritisnite Dalje da nastavite.]]> + + Zadana korisnička lozinka je uspješno promijenjena od instalacije!

Ne treba preduzimati nikakve daljnje radnje. Pritisnite Dalje da nastavite.]]> + Lozinka je promijenjena! + Započnite odlično, pogledajte naše uvodne video zapise + Klikom na sljedeće dugme (ili modifikacijom umbracoConfigurationStatus u web.config), + prihvatate licencu za ovaj softver kao što je navedeno u polju ispod. Primijetite da je ova Umbraco distribucija + sastoji se od dvije različite licence, open source MIT licence za okvir i licence za besplatni softver Umbraco + koji pokriva korisničko sučelje. + + Još nije instalirano. + Zahvaćeni datoteke i mape + Više informacija o postavljanju dozvola za Umbraco ovdje + Morate dodijeliti dozvole za izmjenu ASP.NET-a za sljedeće + datoteke/mape + + Vaše postavke dozvola su gotovo savršene!

+ Možete pokrenuti Umbraco bez problema, ali nećete moći instalirati pakete koji se preporučuju da biste u potpunosti iskoristili Umbraco.]]>
+ Kako riješiti + Kliknite ovdje da pročitate tekstualnu verziju + + video tutorijale o postavljanju dozvola foldera za Umbraco ili pročitajte tekstualnu verziju.]]> + Vaše postavke dozvola mogu biti problem! +

+ Možete pokrenuti Umbraco bez problema, ali nećete moći kreirati foldere ili instalirati pakete koji se preporučuju da biste u potpunosti iskoristili Umbraco.]]>
+ Vaše postavke dozvola nisu spremne za Umbraco! +

+ Da biste pokrenuli Umbraco, morat ćete ažurirati postavke dozvola.]]>
+ Vaše postavke dozvola su savršene!

+ Spremni ste da pokrenete Umbraco i instalirate pakete!]]>
+ Rješavanje problema sa mapom + Pratite ovu vezu za više informacija o problemima sa ASP.NET i + kreiranje foldera + + Postavljanje dozvola za foldere + + Želim da počnem od nule + naučite kako) + I dalje možete odabrati da kasnije instalirate Runway. Molimo idite na odjeljak Developer i odaberite Paketi. + ]]> + Upravo ste postavili čistu Umbraco platformu. Šta želite sljedeće učiniti? + Runway je instaliran + + Ovo je naša lista preporučenih modula, označite one koje želite da instalirate ili pogledajte punu listu modula + ]]> + Preporučuje se samo iskusnim korisnicima + Želim početi s jednostavnom web-stranicom + + "Runway" je jednostavna web stranica koja nudi neke osnovne tipove dokumenata i predloške. Instalater može postaviti Runway za vas automatski, + ali ga možete lako urediti, proširiti ili ukloniti. Nije potrebno i možete savršeno koristiti Umbraco i bez njega. Kako god, + Runway nudi laku osnovu zasnovanu na najboljim praksama za početak brže nego ikad. + Ako se odlučite za instalaciju Runway, opciono možete odabrati osnovne građevne blokove tzv. Runway Modules da poboljšate svoje Runway stranice. +

+ + Uključeno u Runway: Početna stranica, Stranica za početak, Stranica za instaliranje modula.
+ Dodatni moduli: Navigacija, Sitemap, Kontakt, Galerija. +
+ ]]>
+ Šta je Runway + Korak 1/5: Prihvatite licencu + Korak 2/5: Konfiguracija baze podataka + Korak 3/5: Potvrđivanje dozvola za fajlove + Korak 4/5: Provjerite Umbraco sigurnost + Korak 5/5: Umbraco je spreman za početak + Hvala vam što ste odabrali Umbraco + Pregledajte svoju novu stranicu +Instalirali ste Runway, pa zašto ne biste vidjeli kako izgleda vaša nova web stranica.]]> + Dodatna pomoć i informacije +Potražite pomoć od naše nagrađivane zajednice, pregledajte dokumentaciju ili pogledajte nekoliko besplatnih videozapisa o tome kako napraviti jednostavnu stranicu, kako koristiti pakete i brzi vodič za terminologiju Umbraco]]> + Umbraco %0% je instaliran i spreman za upotrebu + /web.config fajl i ažurirate ključ unutar AppSetting UmbracoConfigurationStatus na dnu do vrijednosti od '%0%'.]]> + započeto odmah klikom na "Pokreni Umbraco" dugme ispod.
Ako ste novi u Umbraco-u, +možete pronaći mnogo resursa na našim stranicama za početak.]]>
+ Pokreni Umbraco +Da upravljate svojom web lokacijom, jednostavno otvorite Umbraco backoffice i počnite dodavati sadržaj, ažurirati predloške i stilove ili dodati novu funkcionalnost]]> + Povezivanje s bazom podataka nije uspjelo. + Umbraco Verzija 3 + Umbraco Verzija 4 + Gledaj + Umbraco %0% za novu instalaciju ili nadogradnju sa verzije 3.0. +

+ Pritisnite "Dalje" da pokrenete čarobnjaka.]]>
+ + + Kod kulture + Naziv kulture + + + Bili ste u stanju mirovanja i automatski će doći do odjave + Obnovite sada da sačuvate svoj rad + + + Sretna super nedelja + Sretan divan ponedeljak + Sretan specifičan utorak + Sretna divna srijeda + Sretan gromoglasan četvrtak + Sretan zanimljiv petak + Sretna opuštena subota + Prijavite se ispod + Prijavite se sa + Isteklo je vrijeme sesije + + © 2001 - %0%
Umbraco.com

]]>
+ Zaboravljena lozinka? + E-mail će biti poslan na adresu navedenu sa vezom za reset + lozinke + + E-mail s uputama za poništavanje lozinke će biti poslan na + navedenu adresu ukoliko odgovara našoj evidenciji + + Prikaži lozinku + Sakrij lozinku + Vratite se na obrazac za prijavu + Molimo unesite novu lozinku + Vaša lozinka je ažurirana + Link na koji ste kliknuli je nevažeći ili je istekao + Umbraco: Reset lozinke + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+
+ + + + + + +
+
+
+ + + + +
+ + + + +
+

+ Zatraženo je ponovno postavljanje lozinke +

+

+ Vaše korisničko ime za prijavu na Umbraco backoffice je: %0% +

+

+ + + + + + +
+ + Kliknite na ovaj link da poništite lozinku + +
+

+

Ukoliko ne možete kliknuti na link, kopirajte i zalijepite ovaj URL u prozor vašeg pretraživača:

+ + + + +
+ +%1% + +
+

+
+
+


+
+
+ + + ]]>
+ Umbraco: Sigurnosni kod + Vaš sigurnosni kod je: %0% + Poslednji korak + Omogućili ste 2-faktorsku autentifikaciju i morate potvrditi svoj identitet. + Molimo odaberite 2-faktor provajdera + Verifikacijski kod + Unesite verifikacioni kod + Unesen je nevažeći kod + + + Kontrolna tabla + Sekcije + Sadržaj + + + Odaberite stranicu iznad... + %0% je kopiran u %1% + Odaberite gdje dokument %0% treba kopirati ispod + %0% je premješten u %1% + Odaberite gdje dokument %0% treba premjestiti ispod + je odabrano kao korijen vašeg novog sadržaja, kliknite na 'Uredu' ispod. + Još nije odabran čvor, molimo odaberite čvor na gornjoj listi prije nego kliknete na 'Uredu' + Trenutni čvor nije dozvoljen pod odabranim čvorom zbog njegovog tipa + Trenutni čvor se ne može premjestiti na jednu od njegovih podstranica niti roditelj i odredište mogu biti isti + Trenutni čvor ne može postojati u korijenu + Radnja nije dozvoljena jer nemate dovoljna dopuštenja za 1 ili više djece + dokumenata. + + Povežite kopirane stavke s originalom + + + %0%]]> + Postavke obavještenja su sačuvane za + + Sljedeći jezici su izmijenjeni %0% + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+
+ + + + + + +
+
+
+ + + + +
+ + + + +
+

+ Zdravo %0%, +

+

+ Ovo je automatiziran email koja vas obavještava da je zadatak '%1%' izvršen na stranici '%2%' od korisnika '%3%' +

+ + + + + + +
+ +
+EDIT
+
+

+

Sažetak izmjena:

+ %6% +

+

+ Ugodan dan!

+ Pozdrav od Umbraco robota +

+
+
+


+
+
+ + + ]]>
+ Sljedeći jezici su izmijenjeni:

+ %0% + ]]>
+ [%0%] Obavještenje o %1% izvedena na %2% + Obavještenja + + + Akcije + Kreirano + Kreiraj paket + + i locirajte paket. Umbraco paketi uglavnom imaju ".umb" ili ".zip" ekstenziju. + ]]> + Ovo će izbrisati paket + Uključi sve podređene čvorove + Instalirano + Instalirani paketi + Uputstvo za instalaciju + Ovaj paket nema prikaz konfiguracije + Još nije kreiran nijedan paket + Nijedan paket nije instaliran + + 'Paketi' u gornjem desnom uglu ekrana]]> + Sadržaj paketa + Licenca + Pretražite pakete + Rezultati za + Nismo mogli pronaći ništa za + Pokušajte potražiti drugi paket ili pregledajte kategorije + + Popularno + Promocije + Nova izdanja + ima + karma poeni + Informacije + Vlasnik + Saradnici + Kreirano + Trenutna verzija + .NET verzija + Preuzimanja + Lajkovi + Kompatibilnost + Ovaj paket je kompatibilan sa sljedećim verzijama Umbraco-a, kako su + prijavili članovi zajednice. Potpuna kompatibilnost se ne može garantirati za dolje navedene verzije 100% + + Eksterni izvori + Autor + Dokumentacija + Meta podaci paketa + Naziv paketa + Paket ne sadrži nikakve stavke +
+ Ovo možete bezbjedno ukloniti iz sistema klikom na "deinstaliraj paket".]]>
+ Opcije paketa + Pokrenite migracije paketa na čekanju + Readme paketa + Repozitorij paketa + Potvrdi deinstalaciju paketa + Paket je deinstaliran + Paket je uspješno deinstaliran + Deinstaliraj paket + + Bilješka: svi dokumenti, mediji itd. u zavisnosti od stavki koje uklonite, prestat će raditi i mogu dovesti do nestabilnosti sistema, + pa deinstalirajte sa oprezom. Ako ste u nedoumici, kontaktirajte autora paketa.]]> + Verzija paketa + Provjereno za rad na Umbraco Cloud + + + Zalijepi s punim formatiranjem (nije preporučljivo) + Tekst koji pokušavate zalijepiti sadrži posebne znakove ili formatiranje. Ovo bi moglo biti + uzrokovano kopiranjem teksta iz programa Microsoft Word. Umbraco može automatski ukloniti posebne znakove ili formatiranje, tako da + zalijepljeni sadržaj će biti prikladniji za web. + + Zalijepite kao sirovi tekst bez ikakvog oblikovanja + Zalijepi, ali ukloni oblikovanje (preporučeno) + + + Grupna zaštita + Ako želite dodijeliti pristup svim članovima određenih grupa članova + Morate kreirati grupu članova prije nego što možete koristiti grupnu autentifikaciju + Stranica sa greškom + Koristi se kada su ljudi prijavljeni, ali nemaju pristup + %0%]]> + %0% je sada zaštićen]]> + %0%]]> + Stranica za prijavu + Odaberite stranicu koja sadrži obrazac za prijavu + Uklonite zaštitu... + + %0%?]]> + Odaberite stranice koje sadrže obrazac za prijavu i poruke o greškama + %0%]]> + %0%]]> + Posebna zaštita članova + Ako želite dati pristup određenim članovima + + + + + + + + + + Uključite neobjavljene podstranice + Objavljivanje u toku - molimo sačekajte... + %0% od %1% stranica je objavljeno... + %0% je objavljeno + %0% i objavljene su podstranice + Objavi %0% i sve njegove podstranice + Objavi za objavu %0% i na taj način svoj sadržaj učiniti javno dostupnim.

+ Ovu stranicu i sve njene podstranice možete objaviti odabirom Uključi neobjavljene podstranice. + ]]>
+ + + Niste konfigurirali nijednu odobrenu boju + + + Možete odabrati samo stavke tipa: %0% + Odabrali ste stavku sadržaja koja je trenutno izbrisana ili je u korpi za otpatke + Odabrali ste stavke sadržaja koje su trenutno izbrisane ili su u korpi za otpatke + + + Izbrisana stavka + Odabrali ste medijsku stavku koja je trenutno izbrisana ili je u korpi za otpatke + Odabrali ste medijske stavke koje su trenutno izbrisane ili su u korpi za otpatke + Otpad + Otvorite u biblioteci medija + Promjena medijske stavke + Uredi %0% od %1% + Odbaci kreiranje? + + Izmijenili ste ovaj sadržaj. Jeste li sigurni da ga želite + odbaciti? + + Uklonite sve medije? + Međuspremnik + Nije dozvoljeno + Otvorite birač medija + + + unesite eksterni link + izaberite internu stranicu + Naslov + Link + Otvori u novom prozoru + unesite natpis na ekranu + Unesite link + + + Resetujte izrezivanje + Gotovo + Poništi izmjene + Korisnički definisano + + + Promjene + Kreirano + Trenutna verzija + + Crveni tekst će biti uklonjen u odabranoj verziji, zeleni tekst će biti dodan]]> + Nema razlike između trenutne verzije (nacrta) i odabrane verzije + Dokument je vraćen + Odaberite verziju koju želite usporediti sa trenutnom verzijom + Ovo prikazuje odabranu verziju kao HTML, ako želite vidjeti razliku između dvije + verzije u isto vrijeme, koristite pogled diff + + Vratite se na + Odaberite verziju + Pogled + + + Uredite datoteku skripte + + + Portirnica + Sadržaj + Kurir + Developer + Forme + Pomoć + Umbraco Konfiguracijski Čarobnjak + Mediji + Članovi + Bilteni + Paketi + Marketplace + Postavke + Statistika + Prevodi + Korisnici + + + Ture + Najbolji Umbraco video tutorijali + Posjetite our.umbraco.com + Posjetite umbraco.tv + Pogledajte naše besplatne video tutoriale + na Umbraco Learning Base + + + Podrazumevani šablon + Da biste uvezli vrstu dokumenta, pronađite ".udt" datoteku na svom računaru klikom na + dugme "Uvezi" (na sljedećem ekranu će se tražiti da potvrdite) + + Naslov nove kartice + Tip čvora + Tip + Stilovi + Skripte + Kartica + Naslov kartice + Kartice + Glavni tip sadržaja je omogućen + Ovaj tip sadržaja koristi + Nema definiranih svojstava na ovoj kartici. Kliknite na vezu "dodaj novu nekretninu" na + vrh za kreiranje novog svojstva. + + Kreirajte odgovarajući šablon + Dodaj ikonu + + + Redoslijed sortiranja + Datum kreiranja + Sortiranje završeno. + Povucite različite stavke gore ili dolje ispod da postavite kako bi trebale biti raspoređene. Ili kliknite na + zaglavlja kolona za sortiranje cijele kolekcije stavki + + + + + Validacija + Greške u validaciji moraju biti ispravljene pre nego što se stavka može sačuvati + Nije uspjelo + Sačuvano + Nedovoljne korisničke dozvole, ne mogu dovršiti operaciju + Otkazano + Operaciju je otkazao dodatak treće strane + Ovaj fajl se učitava kao deo fascikle, ali kreiranje novog foldera ovde nije dozvoljeno + Kreiranje novog foldera ovdje nije dozvoljeno + Objavljivanje je otkazao dodatak treće strane + Tip svojstva već postoji + Tip svojstva kreiran + Tip podatka: %1%]]> + Tip svojstva obrisan + Tip dokumenta sačuvan + Kartica kreirana + Kartica je izbrisana + Kartica sa id-em: %0% je obrisana + Stilovi nisu sačuvani + Stilovi sačuvani + Stilovi sačuvani bez ikakvih grešaka + Tip podatka sačuvan + Stavka rječnika je sačuvana + Objavljivanje nije uspjelo jer nadređena stranica nije objavljena + Sadržaj objavljen + i vidljivo na web stranici + Šablon sadržaja je sačuvan + Promjene su uspješno sačuvane + Sadržaj sačuvan + Ne zaboravite objaviti da promjene budu vidljive + Poslano na odobrenje + Promjene su poslane na odobrenje + Medij sačuvan + Medij sačuvan bez ikakvih grešaka + Član sačuvan + Svojstvo stilova sačuvano + Stilovi sačuvani + Šablon sačuvan + Greška pri spremanju korisnika (provjerite log) + Korisnik sačuvan + Tip korisnika sačuvan + Grupa korisnika sačuvana + Kulture i imena hostova su sačuvani + Greška pri spremanju kultura i imena hostova + Fajl nije sačuvan + fajl nije mogao biti sačuvan. Molimo provjerite dozvole za fajlove + Fajl sačuvan + Fajl sačuvan bez ikakvih grešaka + Jezik sačuvan + Tip medija sačuvan + Tip člana sačuvan + Grupa članova sačuvana + Druga grupa članova sa istim imenom već postoji + Šablon nije sačuvan + Uvjerite se da nemate 2 šablona sa istim pseudonimom + Šablon sačuvan + Šablon sačuvan bez ikakvih grešaka! + Sadržaj nije objavljen + Djelomični prikaz sačuvan + Djelomični prikaz sačuvan bez ikakvih grešaka! + Djelomični prikaz nije sačuvan + Došlo je do greške prilikom spremanja fajla. + Dozvole su sačuvane za + Izbrisano je %0% grupa korisnika + %0% je obrisano + Omogućeno %0% korisnika + Onemogućeno %0% korisnika + %0% je sada omogućen + %0% je sada onemogućen + Grupe korisnika su postavljene + Otključano %0% korisnika + %0% je sada otključan + Član je izvezen u fajl + Došlo je do greške prilikom izvoza člana + Korisnik %0% je obrisan + Pozovi korisnika + Pozivnica je ponovo poslana na %0% + Tip dokumenta je izvezen u fajl + Došlo je do greške prilikom izvoza tipa dokumenta + Stavke iz rječnika su izvezene u fajl + Došlo je do greške prilikom izvoza stavki rječnika + Sljedeće stavke iz rječnika su uvezene! + Domene nisu konfigurirane za višejezične stranice, molimo kontaktirajte administratora, + pogledajte dnevnik za više informacija + + Nijedan domen nije konfigurisan za %0%, molimo kontaktirajte administratora, pogledajte + prijavite se za više informacija + + Vaše sistemske informacije su uspješno kopirane u međuspremnik + Nije moguće kopirati vaše sistemske informacije u međuspremnik + + + Dodaj stil + Uredi stil + Stilovi za uređivanje bogatog teksta + Definirajte stilove koji bi trebali biti dostupni u uređivaču obogaćenog teksta za ove + stilove + + Uredi stilove + Uredi svojstvo stilova + Ime prikazano u uređivaču birača stilova + Pregled + Kako će tekst izgledati u uređivaču obogaćenog teksta. + Selektor + Koristite CSS sintaksu, npr. "h1" ili ".redHeader" + Stilovi + CSS koji treba primijeniti u uređivaču obogaćenog teksta, npr. "color:red;" + Kod + Uređivač + + + Produkcija.]]> + Brisanje šablona sa ID-om %0% nije uspjelo + Uredi šablon + Sekcije + Umetnite područje sadržaja + Umetnite čuvar mjesta u području sadržaja + Umetni + Odaberite šta ćete umetnuti u svoj šablon + Stavka iz rječnika + Stavka rječnika je čuvar mjesta za prevodljiv dio teksta, koji + olakšava kreiranje dizajna za višejezične web stranice. + + Makro + + Makro je komponenta koja se može konfigurirati i odlična je za + višekratni dijelovi vašeg dizajna, gdje vam je potrebna opcija za pružanje parametara, + kao što su galerije, obrasci i liste. + + Vrijednost + Prikazuje vrijednost imenovanog polja sa trenutne stranice, s opcijama za izmjenu + vrijednost ili povratak na alternativne vrijednosti. + + Djelomičan pogled + + Djelomični prikaz je zasebna datoteka šablona koja se može prikazati unutar druge + predložak, odličan je za ponovnu upotrebu markupa ili za odvajanje složenih predložaka u zasebne datoteke. + + Master šablon + Nema mastera + Renderirajte podređeni predložak + @RenderBody(). + ]]> + Definirajte imenovanu sekciju + @section { ... }. Ovo se može prikazati u + određenom području nadređenog predloška, koristeći @RenderSection. + ]]> + Renderirajte imenovanu sekciju + @RenderSection(name). + Ovo prikazuje područje podređenog šablona koje je umotano u odgovarajuću @section [name]{ ... } definiciju. + ]]> + Naziv sekcije + Sekcija je obavezna + @section, u suprotnom se prikazuje greška. + ]]> + Kreator upita + stavke vraćene, u + želim + sav sadržaj + sadržaj tipa "%0%" + iz + moja web stranica + gdje + i + je + nije + prije + prije (uključujući odabrani datum) + poslije + poslije (uključujući odabrani datum) + jednako + nije jednako + sadrži + ne sadrži + veće od + veće ili jednako + manje od + manje ili jednako + Id + Naziv + Kreirano + Ažurirano + poredaj po + uzlazno + silazno + Predložak + + + Slika + Makro + Odaberite tip sadržaja + Odaberite izgled + Dodaj red + Dodaj sadržaj + Ispusti sadržaj + Postavke su primijenjene + Ovaj sadržaj ovdje nije dozvoljen + Ovaj sadržaj je ovdje dozvoljen + Kliknite za ugradnju + Kliknite da umetnete sliku + Kliknite da umetnete makro + Pišite ovdje... + Raspored mreže + Izgledi su cjelokupno radno područje za uređivač mreže, obično vam je potreban samo jedan ili + dva različita izgleda + + Dodajte raspored mreže + Uredite raspored mreže + Prilagodite izgled postavljanjem širine kolona i dodavanjem dodatnih odjeljaka + Konfiguracije redova + Redovi su predefinirani za raspored vodoravno + Dodajte konfiguraciju reda + Uredite konfiguraciju reda + Podesite red postavljanjem širine ćelija i dodavanjem dodatnih ćelija + Nije dostupna dodatna konfiguracija + Kolone + Ukupan kombinovani broj kolona u rasporedu mreže + Postavke + Konfigurirajte koje postavke urednici mogu promijeniti + Stilovi + Konfigurirajte šta uređivači stilova mogu promijeniti + Dozvoli svim urednicima + Dozvoli sve konfiguracije redaka + Maksimalan broj stavki + Ostavite prazno ili postavite na 0 za neograničeno + Postavi kao zadano + Odaberite extra + Odaberite zadano + su dodani + Upozorenje + + Promjena imena konfiguracije reda će rezultirati gubitkom podataka za bilo koji postojeći sadržaj koji se temelji na ovoj konfiguraciji.

Izmjena samo oznake neće rezultirati gubitkom podataka.

]]>
+ Brišete konfiguraciju reda + + Brisanje imena konfiguracije reda će rezultirati gubitkom podataka za bilo koji postojeći sadržaj koji je zasnovan na ovome + konfiguraciju. + + Brišete izgled + Izmjena izgleda će rezultirati gubitkom podataka za bilo koji postojeći sadržaj koji je zasnovan + na ovoj konfiguraciji. + + + + Kompozicije + Grupa + Niste dodali nijednu grupu + Dodaj grupu + Naslijeđeno od + Dodaj svojstvo + Obavezna oznaka + Omogući prikaz liste + Konfiguriše stavku sadržaja da prikaže njenu listu koja se može sortirati i pretraživati + djeco, djeca neće biti prikazana na drvetu + + Dozvoljeni predlošci + Odaberite koje predloške urednici mogu koristiti na sadržaju ove vrste + + Dozvoli kao korijen + Dozvolite urednicima da kreiraju sadržaj ovog tipa u korijenu stabla sadržaja. + + Dozvoljeni tipovi podređenih čvorova + Dozvolite da se sadržaj navedenih tipova kreira ispod sadržaja ovog + tip. + + Odaberite podređeni čvor + Naslijediti kartice i svojstva iz postojeće vrste dokumenta. Nove kartice će biti + dodano trenutnoj vrsti dokumenta ili spojeno ako postoji kartica s identičnim imenom. + + Ovaj tip sadržaja se koristi u kompoziciji i stoga se ne može sam sastaviti. + + Nema dostupnih tipova sadržaja za upotrebu kao kompozicija. + Uklanjanje kompozicije će izbrisati sve povezane podatke o svojstvu. Jednom ti + sačuvajte tip dokumenta, nema povratka. + + Napravi novi + Koristite postojeće + Postavke urednika + Konfiguracija + Da, izbriši + je premještena ispod + je kopirano ispod + Odaberite folder za premještanje + Odaberite folder za kopiranje + do u strukturi stabla ispod + Svi tipovi dokumenata + Svi dokumenti + Sve medijske stavke + korištenje ovog tipa dokumenta će biti trajno izbrisano, potvrdite da želite + izbrišite i ove. + + korištenje ove vrste medija će biti trajno izbrisano, potvrdite da želite izbrisati + ovi takođe. + + korištenje ove vrste člana će biti trajno izbrisano, potvrdite da želite izbrisati + ovi takođe + + i svi dokumenti koji koriste ovu vrstu + i sve medijske stavke koje koriste ovu vrstu + i svi članovi koji koriste ovaj tip + Član može uređivati + Dozvolite da ovu vrijednost svojstva uređuje član na svojoj stranici profila + + Osjetljivi podaci + Sakrij ovu vrijednost svojstva od uređivača sadržaja koji nemaju pristup pregledu + osjetljive informacije + + Prikaži na profilu člana + Dozvolite da se ova vrijednost svojstva prikaže na stranici profila člana + + kartica nema redoslijed sortiranja + Gdje se koristi ovaj sastav? + Ovaj sastav se trenutno koristi u sastavu sljedećih + tipa sadržaja: + + Dozvoli varijacije + Dozvolite varirati u zavisnosti od kulture + Dozvoli segmentaciju + Varijacije po kulturi + Varijacije po segmentima + Dozvolite urednicima da kreiraju sadržaj ove vrste na različitim jezicima. + Dozvolite urednicima da kreiraju sadržaj na različitim jezicima. + Dozvolite urednicima da kreiraju segmente ovog sadržaja. + Dozvolite varijaciju po kulturi + Dozvoli segmentaciju + Tip elementa + Je li tip elementa + Tip elementa je namijenjen za korištenje na primjer u ugniježđenom sadržaju, a ne u + drvo. + + Tip dokumenta se ne može promijeniti u tip elementa nakon što je naviknut + kreirati jednu ili više stavki sadržaja. + + Ovo nije primjenjivo za tip elementa + Napravili ste promjene u ovoj nekretnini. Jeste li sigurni da ih želite odbaciti? + Izgled + Oznaka iznad (puna širina) + Uklanjate podređeni čvor + Uklanjanje podređenog čvora ograničit će opcije urednika da kreiraju drugačiji sadržaj + tipovi ispod čvora. + + korišćenjem ovog uređivača biće ažurirane nove postavke. + Čišćenje istorije + Dozvoli zaobilaženje postavki čišćenja globalne historije. + Neka sve verzije budu novije od dana + Čuvajte najnoviju verziju po danu danima + Spriječi čišćenje + Omogući čišćenje + BILJEŠKA! Čišćenje istorijskih verzija sadržaja onemogućeno je globalno. Ove postavke neće stupiti na snagu prije nego što se omogući.]]> + + + Dodaj jezik + ISO kod + Obavezan jezik + Svojstva na ovom jeziku moraju biti popunjena prije nego što se čvor može ispuniti + objavljeno. + + Zadani jezik + Umbraco stranica može imati samo jedan zadani jezik. + Promjena zadanog jezika može rezultirati nedostatkom zadanog sadržaja. + Vraća se na + Nema zamjenskog jezika + Da se omogući višejezični sadržaj da se vrati na drugi jezik ako ne + bude prisutan na traženom jeziku, odaberite ga ovdje. + + Zamjenski jezik + nijedan + + + Dodaj parameter + Uredi parameter + Unesite naziv makroa + Parametri + Definišite parametre koji bi trebali biti dostupni kada koristite ovaj makro. + Odaberite djelimični prikaz makro datoteke + + + Kreiranje modela + ovo može potrajati, ne brinite + Modeli generisani + Models nije mogao biti generisani + Generisanje modela nije uspjelo, pogledajte izuzetak u log dnevniku + + + Dodajte zadanu vrijednost + Zadana vrijednost + Rezervno polje + Zadana vrijednost + Veličina slova + Kodiranje + Odaberite polje + Pretvorite prijelome redaka + Zamjenjuje prijelome reda sa 'br' html oznakom + Prilagođena polja + Samo datum + Formatiraj kao datum + Kodirati kao HTML + Zamijenit će specijalne znakove njihovim HTML ekvivalentom. + Biće umetnuto iza vrednosti polja + Biće umetnuto ispred vrednosti polja + Mala slova + Nema + Izlazni uzorak + Umetnuti nakon polja + Umetnuti ispred polja + Rekurzivno + Da, neka bude rekurzivno + Standardna polja + Velika slova + Kodirati kao URL + Formatirati će posebne znakove u URL-ovima + Koristit će se samo kada su vrijednosti polja iznad prazne + Ovo polje će se koristiti samo ako je primarno polje prazno + Datum i vrijeme + + + Detalji prijevoda + Preuzmi XML DTD + Polja + Uključi podstranice + + Nije pronađen nijedan korisnik prevodioca. Molimo kreirajte korisnika prevodioca prije nego počnete slati + sadržaj u prijevod + + Stranica '%0%' je poslana na prijevod + Pošaljite stranicu '%0%' u prijevod + Ukupno riječi + Prevedi na + Prevod završen. + Možete pregledati stranice koje ste upravo preveli klikom ispod. Ako je + originalna stranica je pronađena, dobićete poređenje 2 stranice. + + Prevod nije uspio, XML datoteka je možda oštećena + Opcije prevođenja + Prevodilac + Uvezite XML prijevod + + + Sadržaj + Predlošci sadržaja + Mediji + Pretraživač keša + Kanta za smeće + Kreirani paketi + Tipovi podataka + Riječnik + Instalirani paketi + Instaliraj skin + Instaliraj početnički kit + Jezici + Instaliraj lokalni paket + Makroi + Tipovi medija + Članovi + Grupe članova + Uloge članova + Tipovi članova + Tipovi dokumenata + Tipovi relacija + Paketi + Paketi + Parcijalni pogledi + Parcijalni pregledi makro fajlova + Instaliraj iz repozitorija + Instaliraj Runway + Runway moduli + Skripte + Skripte + Stilovi + Predlošci + Log preglednik + Korisnici + Postavke + Predložak + Treća strana + + + Novo ažuriranje spremno + %0% je spremno, kliknite ovdje za preuzimanje + Nema konekcije sa serverom + Greška pri provjeri ažuriranja. Molimo pregledajte log za dodatne informacije + + + Pristup + Na osnovu dodijeljenih grupa i početnih čvorova, korisnik ima pristup sljedećim čvorovima + + Dodijeli pristup + Administrator + Polje kategorije + Korisnik kreiran + Promijeni lozinku + Promijeni sliku + Nova lozinka + Najmanje %0% znakova! + Trebalo bi biti najmanje %0% specijalnih znakova. + nije zaključan + Lozinka nije promijenjena + Potvrdite novu lozinku + Možete promijeniti lozinku za pristup Umbraco backofficeu popunjavanjem + izađite iz donjeg obrasca i kliknite na dugme 'Promijeni lozinku' + + Kanal sadržaja + Kreirajte drugog korisnika + Kreirajte nove korisnike da im date pristup Umbraco. Kada se novom korisniku kreira lozinka + će biti generisano koje možete podijeliti s korisnikom. + + Polje za opis + Onemogući korisnika + Tip dokumenta + Urednik + Obavezno - unesite email adresu za ovog korisnika + Polje izvoda + Neuspjeli pokušaji prijave + Idite na korisnički profil + Dodajte grupe za dodjelu pristupa i dozvola + Pozovite drugog korisnika + Pozovite nove korisnike da im daju pristup Umbraco. E-mail sa pozivom će biti poslan na + korisnik s informacijama o tome kako se prijaviti na Umbraco. Pozivnice traju 72 sata. + + Jezik + Podesite jezik koji ćete videti u menijima i dijalozima + Zadnji datum zaključavanja + Zadnja prijava + Lozinka zadnji put promijenjena + Korisničko ime + Medijski startni čvor + Ograničite biblioteku medija na određeni početni čvor + Medijski startni čvorovi + Ograničite biblioteku medija na određene početne čvorove + Sekcije + Obavezno - unesite ime za ovog korisnika + Onemogućite pristup Umbraco-u + se još nije prijavio + Stara lozinka + Lozinka + Reset lozinke + Vaša lozinka je promijenjena! + Lozinka je promijenjena + Molimo potvrdite novu lozinku + Unesite novu lozinku + Vaša nova lozinka ne može biti prazna! + Trenutna lozinka + Nevažeća trenutna lozinka + Postojala je razlika između nove lozinke i potvrđene lozinke. Molim te + pokušaj ponovo! + + Potvrđena lozinka ne odgovara novoj lozinki! + Zamijenite dozvole podređenog čvora + Trenutno mijenjate dozvole za stranice: + Odaberite stranice da promijenite njihove dozvole + Ukloni sliku + Zadane dozvole + Detaljne dozvole + Postavite dozvole za određene čvorove + Profil + Pretražite svu djecu + Ograničite jezike kojima korisnici imaju pristup za uređivanje + Dodajte odjeljke da korisnicima omogućite pristup + Odaberite grupe korisnika + Nije odabran početni čvor + Nije odabran nijedan početni čvor + Početni čvor sadržaja + Ograničite stablo sadržaja na određeni početni čvor + Početni čvorovi sadržaja + Ograničite stablo sadržaja na određene početne čvorove + Korisnik zadnji put ažuriran + je kreiran + Novi korisnik je uspješno kreiran. Za prijavu na Umbraco koristite + lozinka ispod. + + Upravljanje korisnicima + Korisničko ime + Korisničke dozvole + Grupa korisnika + je pozvan + Novom korisniku je poslana pozivnica s detaljima o tome kako se prijaviti + Umbraco. + + Pozdrav i dobrodošli u Umbraco! Za samo 1 minut možete krenuti, mi + samo trebate postaviti lozinku i dodati sliku za svoj avatar. + + Dobrodošli u Umbraco! Nažalost, vaš poziv je istekao. Molimo kontaktirajte svoju + administratora i zamolite ih da ga ponovo pošalju. + + Ako otpremite svoju fotografiju, drugi korisnici će ga lako prepoznati + ti. Kliknite na krug iznad da otpremite svoju fotografiju. + + Pisac + Promjena + Vaš profil + Vaša nedavna istorija + Sesija ističe za + Pozovi korisnika + Kreiraj korisnika + Pošalji pozivnicu + Nazad na korisnike + Umbraco: Pozivnica + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+
+ + + + + + +
+
+
+ + + + +
+ + + + +
+

+ Zdravo %0%, +

+

+ Pozvani ste od %1% u Umbraco Back Office. +

+

+ Poruka od %1%: +
+%2% +

+ + + + + + +
+ + + + + + +
+ + Kliknite na ovaj link da prihvatite pozivnicu + +
+
+

Ukoliko ne možete kliknuti na link, kopirajte i zalijepite ovaj URL u prozor vašeg pretraživača:

+ + + + +
+ +%3% + +
+

+
+
+


+
+
+ +]]>
+ Ponovno slanje pozivnice... + Izbriši korisnika + Jeste li sigurni da želite izbrisati ovaj korisnički račun? + Sve + Aktivan + Onemogućen + Zaključan + Odobren + Pozvan + Neaktivan + Ime (A-Z) + Ime (Z-A) + Najnovije + Najstarije + Zadnja prijava + Nijedna korisnička grupa nije dodana + Ako želite da onemogućite ovog dvofaktorskog provajdera, onda morate uneti kod prikazan na vašem uređaju za autentifikaciju: + Ovaj dvofaktorski provajder je omogućen + Ovaj dvofaktorski provajder je sada onemogućen + Nešto je pošlo po zlu s pokušajem da se onemogući ovaj dvofaktorski provajder + Želite li onemogućiti ovog dvofaktorskog provajdera za ovog korisnika? + + + Validacija + Nema validacije + Potvrdi kao adresu e-pošte + Potvrdite kao broj + Potvrdi kao URL + ...ili unesite prilagođenu validaciju + Polje je obavezno + Unesite prilagođenu poruku o grešci validacije (opcionalno) + Unesite regularni izraz + Unesite prilagođenu poruku o grešci validacije (opcionalno) + Morate dodati barem + Samo možeš imati + Dodajte do + stavke + URL-ovi + Odabrani URL-ovi + odabrane stavke + Nevažeći datum + Nije broj + Nije važeća brojčana veličina koraka + Nevažeći email + Vrijednost ne može biti null + Vrijednost ne može biti prazna + Vrijednost je nevažeća, ne odgovara ispravnom uzorku + Prilagođena validacija + %1% više.]]> + %1% previše.]]> + Zahtjevi za količinu sadržaja nisu ispunjeni za jedno ili više područja. + + + + Vrijednost je postavljena na preporučenu vrijednost: '%0%'. + Očekivana vrijednost '%1%' za '%2%' u konfiguracijskoj datoteci '%3%', ali + pronađeno '%0%'. + + Pronađena neočekivana vrijednost '%0%' za '%2%' u konfiguracijskoj datoteci '%3%'. + + + Makro greške su postavljene na '%0%'. + Greške makroa su postavljene na '%0%' što će spriječiti potpuno učitavanje nekih ili svih stranica + na vašem sajtu ako postoje greške u makroima. Ako ovo ispravite, vrijednost će biti postavljena na '%1%'. + + + + Certifikat Vaše web stranice je važeći. + Greška u validaciji certifikata: '%0%' + SSL certifikat vaše web stranice je istekao. + SSL certifikat vaše web stranice ističe za %0% dana. + Greška pri pingovanju URL-a %0% - '%1%' + Trenutno %0% pregledavate stranicu koristeći HTTPS šemu. + AppSetting 'Umbraco:CMS:Global:UseHttps' je postavljen na 'false' u + vašem appSettings.json fajl. Jednom kada pristupite ovoj stranici koristeći HTTPS šemu, to bi trebalo biti postavljeno na 'true'. + + Postavka aplikacije 'Umbraco:CMS:Global:UseHttps' je postavljena na '%0%' u vašem + appSettings.json datoteku, vaši kolačići su %1% označeni kao sigurni. + + + Način kompilacije otklanjanja grešaka je onemogućen. + Način kompilacije za otklanjanje grešaka je trenutno omogućen. Preporučuje se da se + onemogućite ovu postavku prije emitiranja uživo. + + + + + %0%.]]> + AppSetting 'Umbraco:CMS:WebRouting:UmbracoApplicationUrl' nije postavljen. + + X-Frame-Options koji se koristi za kontrolu da li neko mjesto može biti IFRAMED od strane drugog je pronađen.]]> + + X-Frame-Options koji se koristi za kontrolu da li neko mjesto može biti IFRAMED od strane drugog nije pronađen.]]> + + X-Content-Type-Options koji se koristi za zaštitu od ranjivosti MIME sniffinga je pronađen.]]> + + X-Content-Type-Options koji se koristi za zaštitu od ranjivosti MIME sniffinga nije pronađen.]]> + + Strict-Transport-Security, takođe poznat kao HSTS-header, je pronađen.]]> + + Strict-Transport-Security nije pronađeno.]]> + + Strict-Transport-Security, takođe poznat kao HSTS-header, je pronađen. Ovo zaglavlje ne bi trebalo biti prisutno na lokalnom hostu.]]> + + + Strict-Transport-Security nije pronađeno. Ovo zaglavlje ne bi trebalo biti prisutno na lokalnom hostu.]]> + + X-XSS-Protection je pronađeno.]]> + + X-XSS-Protection nije pronađeno.]]> + + + %0%.]]> + Nisu pronađena zaglavlja koja otkrivaju informacije o tehnologiji web stranice. + + U datoteci Web.config, system.net/mailsettings nije moguće pronaći. + U datoteci Web.config, system.net/mailsettings, host + nije konfigurisan. + + SMTP postavke su ispravno konfigurisane i usluga radi + kao što je očekivano. + + SMTP server konfigurisan sa hostom '%0%' i portom '%1%' ne može biti + dosegnut. Provjerite jesu li SMTP postavke u datoteci Web.config, system.net/mailsettings ispravne. + + + %0%.]]> + + %0%.]]> + +

Rezultati zakazanih Umbraco provjera zdravlja koji se pokreću na %0% na %1% su sljedeći:

%2%]]>
+ Status provjere zdravlja Umbraco: %0% + Provjerite grupu + + Provjera zdravlja procjenjuje različita područja vaše web lokacije u pogledu postavki najbolje prakse, konfiguracije, potencijalnih problema itd. Možete jednostavno riješiti probleme pritiskom na dugme. + Možete dodati svoje zdravstvene preglede, pogledajte dokumentaciju za više informacija o prilagođenim zdravstvenim pregledima.

+ ]]> +
+ + + Onemogući URL tragač + Omogući URL tragač + Originalni URL + Preusmjeri na + Preusmjeravanje URL-ova + Sljedeći URL-ovi preusmjeravaju na ovu stavku sadržaja: + Nisu napravljena nikakva preusmjeravanja + Kada se objavljena stranica preimenuje ili premjesti, preusmjeravanje će automatski biti + napravljen na novu stranicu. + + Preusmjeravanje uklonjeno. + Greška pri uklanjanju preusmjeravanja. + Ovo će ukloniti preusmjeravanje + Jeste li sigurni da želite onemogućiti URL tragač? + URL tragač je sada onemogućen. + Greška pri onemogućavanju URL tragača, više informacija možete pronaći u vašem log fajlu. + URL tragač je sada omogućen. + Greška pri omogućavanju URL tragača, više informacija možete pronaći u vašem log fajlu. + + + Nema stavki iz rječnika za odabir + + + %0% preostalih znakova.]]> + %1% previše.]]> + + + Sadržaj u otpadu s ID-om: {0} povezan je s originalnim nadređenim sadržajem s ID-om: {1} + Medij u otpadu s ID-om: {0} povezan je s originalnim nadređenim medijem s ID-om: {1} + Nije moguće automatski vratiti ovu stavku + Ne postoji lokacija na kojoj se ova stavka može automatski vratiti. Vi + može ručno premjestiti stavku koristeći stablo ispod. + + je restauriran pod + + + Smjer + Roditelj djetetu + Bidirectional + Roditelj + Dijete + Broj + Relacija + Relacije + Kreirano + Komentar + Naziv + Nema relacija za ovu vrstu odnosa + Tip relacije + Relacije + Je zavisan + Da + Ne + + + Početak rada + Preusmjeravanje URL-ova + Sadržaj + Dobrodošli + Examine menadžment + Status stranice + Generator modela + Provjera zdravlja + Profilisanje + Početak rada + Instaliraj Umbraco Forms + + + Vrati se + Aktivan raspored: + Skoči na + grupa + prošao + upozorenje + neuspješno + prijedlog + Provjera prošla + Provjera nije uspjela + Otvorite backoffice pretragu + Otvori/Zatvori pomoć za backoffice + Opcije otvaranja/zatvaranja profila + Postavite kulturu i imena hostova za %0% + Kreirajte novi čvor ispod %0% + Postavite ograničenja pristupa uključena %0% + Dozvole za postavljanje su uključene %0% + Promijenite redoslijed sortiranja za %0% + Kreirajte predložak sadržaja na osnovu %0% + Otvorite kontekstni meni za + Trenutni jezik + Prebaci jezik na + Kreirajte novi folder + Parcijalni pogled + Makro za djelomični prikaz + Član + Tip podatka + Pretražite kontrolnu tablu za preusmjeravanje + Pretražite odjeljak korisničke grupe + Pretražite odjeljak korisnika + Kreiraj stavku + Kreiraj + Uredi + Naziv + Dodaj novi red + Pogledajte više opcija + Pogledajte više opcija + Potražite čvorove sadržaja, medijske čvorove itd. u backofficeu. + Kada su dostupni rezultati autodovršavanja, pritisnite strelice gore i dolje ili koristite + taster tab i koristite taster enter da izaberete. + + Putanja: + Pronađeno u + Ima prevod + Nedostaje prijevod + Stavke iz rječnika + Odaberite jednu od opcija za uređivanje čvora. + Izvršite akciju %0% na čvoru %1%. + Dodajte natpis slike + Pretraži stablo sadržaja + Maksimalni iznos + + + Reference + Ovaj tip podataka nema reference. + Ova stavka nema reference. + Koristi se u tipovima dokumenata + Koristi se u tipovima medija + Koristi se u tipovima članova + Koristi + Stavke u upotrebi + Potomci u upotrebi + Ova stavka ili njeni potomci se koriste. Brisanje može dovesti do neispravnih veza na vašoj web stranici. + Ova stavka ili njeni potomci se koriste. Poništavanje objavljivanja može dovesti do neispravnih veza na vašoj web stranici. Molimo poduzmite odgovarajuće radnje. + Ova stavka ili njeni potomci se koriste. Stoga je brisanje onemogućeno. + Sljedeće stavke koje pokušavate %0% koriste drugi sadržaj. + + + Izbriši sačuvane pretrage + Nivoi loga + Označi sve + Odznači sve + Sačuvane pretrage + Sačuvaj pretragu + Unesite prijateljski naziv za vaš upit za pretragu + Filtriraj pretragu + Ukupno + Vrijeme + Nivo + Uređaj + Poruka + Izuzetak + Svojstva + Pretraži pomoću Google-a + Pretraži ovu poruku pomoću Google-a + Pretraži pomoću Bing-a + Pretraži ovu poruku pomoću Bing-a + Pretraži Our Umbraco + Pretraži ovu poruku na Our Umbraco forumu i dokumentaciji + Pretraži Our Umbraco pomoću Google-a + Pretraži Our Umbraco forume pomoću Google-a + Pretraži Umbraco Source + Pretraži Umbraco source code on Github-u + Pretraži Umbraco Issues + Pretraži Umbraco Issues na Github-u + Obriši ovu pretragu + Pronađi logove sa ID-om zatjeva + Pronađi logove sa namespace-om + Pronađi logove sa nazivom uređaja + Otvori + Provjera + Svako 2 sekunde + Svako 5 sekundi + Svako 10 sekundi + Svako 20 sekundi + Svako 30 sekundi + Provjera svako 2s + Provjera svako 5s + Provjera svako 10s + Provjera svako 20s + Provjera svako 30s + + + Kopiraj %0% + %0% od %1% + Zbirka od %0% + Uklonite sve stavke + Očisti međuspremnik + + + Otvorite radnje svojstva + Zatvorite Property Actions + + + Osvježi status + Memorijski keš + + + + Ponovo učitaj + Keš baze podataka + + Obnova može biti skupa. + Koristite ga kada ponovno učitavanje nije dovoljno, a mislite da keš baze podataka nije bio + pravilno generisan—što bi ukazivalo na neko kritično pitanje Umbraco. + ]]> + + Ponovo sagradi + Unutrašnjost + + ne morate koristiti. + ]]> + + Skupiti + Objavljeni status keša + Predmemorije + + + Profiliranje performansi + + + Umbraco trenutno radi u načinu za otklanjanje grešaka. To znači da možete koristiti ugrađeni profiler performansi za procjenu performansi prilikom renderiranja stranica. +

+

+ Ako želite da aktivirate profiler za određeno prikazivanje stranice, jednostavno dodajte umbDebug=true na string upita kada tražite stranicu. +

+

+ Ako želite da se profilator aktivira prema zadanim postavkama za sve prikaze stranica, možete koristiti prekidač ispod. + On će postaviti kolačić u vaš pretraživač, koji zatim automatski aktivira profiler. + Drugim riječima, profiler će biti aktivan samo po defaultu u vašen pretraživaču - ne svačijem drugom. +

+ ]]> +
+ Podrazumevano aktivirajte profiler + Prijateljski podsjetnik + + + Nikada ne biste trebali dozvoliti da proizvodna lokacija radi u načinu za otklanjanje grešaka. Režim za otklanjanje grešaka se isključuje podešavanjem Umbraco:CMS:Hosting:Debug na false u appsettings.json, appsettings.{Environment}.json ili preko varijable okruženja. +

+ ]]> +
+ + + Umbraco trenutno ne radi u načinu za otklanjanje grešaka, tako da ne možete koristiti ugrađeni profiler. Ovako bi trebalo da bude za proizvodnu lokaciju. +

+

+ Režim za otklanjanje grešaka se uključuje podešavanjem Umbraco:CMS:Hosting:Debug na true u appsettings.json, appsettings.{Environment}.json ili preko varijable okruženja. +

+ ]]> +
+ + + Sati Umbraco trening videa udaljeni su samo jedan klik + + Želite savladati Umbraco? Provedite nekoliko minuta učeći neke najbolje prakse gledajući jedan od ovih videozapisa o korištenju Umbraco-a. I posjetite umbraco.tv za još više Umbraco videa

+ ]]> +
+ + Želite savladati Umbraco? Provedite nekoliko minuta učeći neke najbolje prakse gledajući jedan od ovih videozapisa o korištenju Umbraco-a Umbraco Learning Base Youtube kanal. Ovdje možete pronaći gomilu video materijala koji pokriva mnoge aspekte Umbraco-a.

+ ]]> +
+ Za početak + + + Počni ovdje + Ovaj odjeljak sadrži blokove za izgradnju vaše Umbraco stranice. Slijedite dolje + veze da saznate više o radu sa stavkama u odjeljku Postavke + + Saznajte više + + u odjeljku Dokumentacija na Our Umbraco + ]]> + + + Forumu zajednice + ]]> + + + video tutorijale na Umbraco Learning Base + ]]> + + + alatima za povećanje produktivnosti i komercijalna podrška + ]]> + + + obuke i certifikacije + ]]> + + + + Dobrodošli u The Friendly CMS + Hvala vam što ste odabrali Umbraco - mislimo da bi ovo mogao biti početak nečega + predivno. Iako se u početku može činiti neodoljivim, učinili smo mnogo da kriva učenja bude što glatka i brza + što je moguće. + + + + Umbraco Forms + Kreirajte obrasce pomoću intuitivnog drag and drop interfejsa. Od jednostavnih kontakt obrazaca + koji šalje e-mail do naprednih obrazaca koji se integrišu sa CRM sistemima. Vašim klijentima će se svidjeti! + + + + Odaberite tip elementa + Priložite postavke na tip elementa + Odaberite prikaz + Odaberite stil + Odaberite sličicu + Kreirajte novi tip elementa + Prilagođeni stil + Dodaj stil + Izgled bloka + Modeli podataka + Izgled kataloga + Boja pozadine + Boja ikone + Model sadržaja + Labela + Prilagođeni prikaz + Prikaži opis prilagođenog prikaza + Zamenite način na koji se ovaj blok pojavljuje u korisničkom sučelju backofficea. Odaberite .html datoteku + koji sadrži vašu prezentaciju. + + Model postavki + Veličina uređivača preklapanja + Dodaj prilagođeni prikaz + Dodaj postavke + + %0%?]]> + + %0%?]]> + Sadržaj ovog bloka će i dalje biti prisutan, uređivanje ovog sadržaja + više neće biti dostupan i bit će prikazan kao nepodržani sadržaj. + + + %0% i sve konfiguracije ovog bloka?]]> + Sadržaj ovih blokova će i dalje biti prisutan, uređivanje ovog sadržaja + više neće biti dostupan i bit će prikazan kao nepodržani sadržaj. + + + Ne može se uređivati jer tip elementa ne postoji. + Sličica + Dodaj sličicu + Kreiraj prazno + Međuspremnik + Postavke + Napredno + Sakrij uređivač sadržaja + Sakrijte dugme za uređivanje sadržaja i uređivač sadržaja iz preklapanja Block Editor. + Inline editovanje + Omogućava inline uređivanje za prvo svojstvo. Dodatna svojstva se mogu uređivati u prekrivaču. + Izmijenili ste ovaj sadržaj. Jeste li sigurni da ih želite odbaciti? + Odbaciti kreiranje? + + Greška! + Tip elementa ovog bloka više ne postoji + Dodaj sadržaj + Dodaj %0% + Svojstvo '%0%' koristi uređivač '%1%' koji nije podržan u blokovima. + Postavite fokus na blok kontejnera + Identifikacija + Validacija + %0% mora biti prisutan barem %2% puta.]]> + %0% mora biti maksimalno prisutan %3% puta.]]> + Broj blokova + Dozvolite samo određene tipove blokova + Dozvoljene vrste blokova + Definirajte tipove blokova koji su dozvoljeni u ovoj oblasti i opciono koliko svakog tipa treba biti prisutan. + Jeste li sigurni da želite izbrisati ovo područje? + Svi blokovi koji su trenutno kreirani unutar ovog područja bit će obrisani. + Opcije rasporeda + Strukturno + Opcije veličine + Definirajte jednu ili više opcija veličine, ovo omogućava promjenu veličine bloka + Definirajte jednu ili više opcija veličine, ovo omogućava promjenu veličine bloka + Definirajte različit broj kolona preko kojih ovaj blok može da se proteže. Ovo ne sprječava postavljanje blokova u područja s manjim rasponom kolona. + Dostupni rasponi redova + Definirajte raspon redova rasporeda preko kojih se ovaj blok može prostirati. + Dozvolite u korijenu + Učinite ovaj blok dostupnim u korijenu izgleda. + Dozvolite u područjima + Učinite ovaj blok dostupnim prema zadanim postavkama unutar područja drugih blokova (osim ako za ove oblasti nisu postavljene eksplicitne dozvole). + Prema zadanim postavkama, svi tipovi blokova su dozvoljeni u području. Koristite ovu opciju da dozvolite samo odabrane tipove. + Područja + Mrežne kolone za područja + Definirajte koliko će stupaca biti dostupno za područja. Ako nije definiran, koristit će se broj kolona definiranih za cijeli izgled. + Područja + Da biste omogućili ugniježđenje blokova unutar ovog bloka, definirajte jedno ili više područja. Područja slijede raspored definiran njihovom vlastitom konfiguracijom stupca mreže. 'Raspon kolone' i 'raspon reda' za svaku oblast može se podesiti korištenjem okvira za rukovanje skalom u donjem desnom uglu odabranog područja. + %0% nije dozvoljeno na ovom mestu.]]> + Zadani raspored stilova + Nedozvoljeni sadržaj je odbijen + + + + + Povucite za skaliranje + Kreiraj oznaku dugmeta + Nadjačajte tekst oznake za dodavanje novog bloka u ovo područje, primjer: 'Dodaj widget' + Prikaži opcije promjene veličine + Dodaj blok + Dodaj grupu + Odaberi grupu ili blok + Postavite minimalni zahtjev + Postavite maksimalan zahtjev + Blok + Blok + Postavke + Područja + Napredno + Dozvole + Instalirajte uzorak konfiguracije + + Instaliraj + Način sortiranja + Završi način sortiranja + Ovaj pseudonim oblasti mora biti jedinstven u poređenju sa drugim oblastima ovog bloka. + Konfiguriraj područje + Obriši područje + Dodajte opciju raspona %0% kolona + + + Šta su predlošci sadržaja? + Predlošci sadržaja su unaprijed definirani sadržaj koji se može odabrati prilikom kreiranja novog + sadržaj čvora. + + Kako da kreiram predložak sadržaja? + + Postoje dva načina za kreiranje predloška sadržaja:

+
    +
  • Desnom tipkom miša kliknite čvor sadržaja i odaberite "Kreiraj predložak sadržaja" da kreirate novi predložak sadržaja.
  • +
  • Kliknite desnim tasterom miša na stablo predložaka sadržaja u odjeljku Postavke i odaberite vrstu dokumenta za koju želite da kreirate predložak sadržaja.
  • +
+

Nakon što dobiju ime, urednici mogu početi koristiti predložak sadržaja kao osnovu za svoju novu stranicu.

+ ]]> +
+ Kako da upravljam predlošcima sadržaja? + Možete uređivati i brisati predloške sadržaja iz stabla "Šabloni sadržaja" u + odjeljak postavki. Proširite vrstu dokumenta na kojoj se temelji predložak sadržaja i kliknite na nju da biste uredili ili izbrisali to. + + + + Kraj + Završi način pregleda + Pregledajte web stranicu + Otvorite web stranicu u načinu pregleda + Pregledajte web stranicu? + Završili ste način pregleda, želite li ga ponovo omogućiti da vidite + najnovija sačuvana verzija vaše web stranice? + + Pregledajte najnoviju verziju + Pogledajte objavljenu verziju + Pogledajte objavljenu verziju? + Nalazite se u načinu pregleda, želite li izaći da biste vidjeli + objavljena verzija Vaše web stranice? + + Pogledajte objavljenu verziju + Ostanite u načinu pregleda + + + Kreiranje foldera + Pisanje datoteka za pakete + Pisanje fajlova + Kreiranje medijskog foldera + + + stavka vraćena + stavke vraćene + +
diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index 0555ea3934..cf0518613d 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -342,6 +342,7 @@ Opret et nyt medlem Alle medlemmer Medlemgrupper har ingen yderligere egenskaber til redigering. + Totrinsbekræftelse Kopiering af indholdstypen fejlede @@ -1902,6 +1903,7 @@ Mange hilsner fra Umbraco robotten genkende dig. Klik på cirklen ovenfor for at uploade et billede. Forfatter + Konfigurer totrinsbekræftelse Skift Din profil Din historik @@ -1929,11 +1931,11 @@ Mange hilsner fra Umbraco robotten Ældste Sidst logget ind Ingen brugere er blevet tilføjet - Hvis du ønsker at slå denne autentificeringsmetode fra, så skal du nu indtaste koden fra dit device: - Denne autentificeringsmetode er slået til - Den valgte autentificeringsmetode er nu slået fra - Der skete en ukendt fejl da denne autentificeringsmetode skulles slåes fra - Er du sikker på, at du vil fjerne denne autentificeringsmetode for denne bruger? + Hvis du ønsker at slå denne totrinsbekræftelse fra, så skal du nu indtaste koden fra din enhed: + Denne totrinsbekræftelse er slået til + Den valgte totrinsbekræftelse er nu slået fra + Der skete en ukendt fejl da denne totrinsbekræftelse skulles slåes fra + Er du sikker på, at du vil fjerne denne totrinsbekræftelse for denne bruger? Validering diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index d568f48102..efc125d6d5 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -878,6 +878,8 @@ Header system field Last Updated + Skip to menu + Skip to content Blue @@ -1329,7 +1331,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Likes Compatibility This package is compatible with the following versions of Umbraco, as - reported by community members. Full compatability cannot be guaranteed for versions reported below 100% + reported by community members. Full compatibility cannot be guaranteed for versions reported below 100% External sources Author @@ -1540,7 +1542,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Property type already exists Property type created DataType: %1%]]> - Propertytype deleted + Property type deleted Document Type saved Tab created Tab deleted @@ -2358,9 +2360,13 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Strict-Transport-Security was not found. This header should not be present on localhost.]]> - X-XSS-Protection was found.]]> + + X-XSS-Protection was found. It is recommended not to add this header to your website.
+ You can read about this on the Mozilla website ]]> +
- X-XSS-Protection was not found.]]> + X-XSS-Protection was not found.]]> + @@ -2775,7 +2781,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Set focus on the container block Identification Validation - %0% must be present atleast %2% time(s).]]> + %0% must be present at least %2% time(s).]]> %0% must maximum be present %3% time(s).]]> Number of blocks Only allow specific block types diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index 5acf1b7abe..78fcd0f13d 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -908,6 +908,8 @@ Header system field Last Updated + Skip to menu + Skip to content Blue @@ -2461,7 +2463,9 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Strict-Transport-Security was not found. This header should not be present on localhost.]]> - X-XSS-Protection was found.]]> + + X-XSS-Protection was found. It is recommended not to add this header to your website.
+ You can read about this on the Mozilla website ]]>
X-XSS-Protection was not found.]]> "; //create an id class for this element so we can re-select it after inserting var uniqueId = "umb-macro-" + editor.dom.uniqueId(); - var macroDiv = editor.dom.create('div', + var isInlined = macroObject.macroParamsDictionary["enableInlineMacro"] === "1"; + var macroElementType = isInlined ? 'span' : 'div'; + var macroElement = editor.dom.create(macroElementType, { - 'class': 'umb-macro-holder ' + macroObject.macroAlias + " " + uniqueId + ' mceNonEditable', + 'class': 'umb-macro-holder ' + macroObject.macroAlias + " " + uniqueId + ' mceNonEditable' + (isInlined ? ' inlined-macro' : ''), 'contenteditable': 'false' }, macroSyntaxComment + 'Macro alias: ' + macroObject.macroAlias + ''); //if there's an activeMacroElement then replace it, otherwise set the contents of the selected node if (activeMacroElement) { - activeMacroElement.replaceWith(macroDiv); //directly replaces the html node + activeMacroElement.replaceWith(macroElement); //directly replaces the html node } else { - editor.selection.setNode(macroDiv); + editor.selection.setNode(macroElement); } - var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId)); + var $macroElement = $(editor.dom.select(".umb-macro-holder." + uniqueId)); editor.setDirty(true); //async load the macro content - this.loadMacroContent($macroDiv, macroObject, editor); + this.loadMacroContent($macroElement, macroObject, editor); }, /** loads in the macro content async from the server */ - loadMacroContent: function ($macroDiv, macroData, editor) { + loadMacroContent: function ($macroElement, macroData, editor) { //if we don't have the macroData, then we'll need to parse it from the macro div if (!macroData) { - var contents = $macroDiv.contents(); + var contents = $macroElement.contents(); var comment = _.find(contents, function (item) { return item.nodeType === 8; }); @@ -870,15 +878,15 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s macroData = parsed; } - var $ins = $macroDiv.find("ins"); + var $ins = $macroElement.find("ins"); //show the throbber - $macroDiv.addClass("loading"); + $macroElement.addClass("loading"); // Add the contenteditable="false" attribute // As just the CSS class of .mceNonEditable is not working by itself?! // TODO: At later date - use TinyMCE editor DOM manipulation as opposed to jQuery - $macroDiv.attr("contenteditable", "false"); + $macroElement.attr("contenteditable", "false"); var contentId = $routeParams.id; @@ -887,7 +895,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s macroResource.getMacroResultAsHtmlForEditor(macroData.macroAlias, contentId, macroData.macroParamsDictionary) .then(function (htmlResult) { - $macroDiv.removeClass("loading"); + $macroElement.removeClass("loading"); htmlResult = htmlResult.trim(); if (htmlResult !== "") { var wasDirty = editor.isDirty(); @@ -1144,7 +1152,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s // the href might be an external url, so check the value for an anchor/qs // href has the anchor re-appended later, hence the reset here to avoid duplicating the anchor - if (!target.anchor) { + if (!target.anchor && href) { var urlParts = href.split(/(#|\?)/); if (urlParts.length === 3) { href = urlParts[0]; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-header.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-header.less index a9b9cf6936..bc02705187 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-header.less @@ -110,3 +110,27 @@ .umb-app-header__button:focus .umb-app-header__action-icon { opacity: 1; } + +.umb-app-header__skip-button { + position: absolute; + top: 7.5px; + left: 5px; + background-color: #FFF; + border: 1px solid #000; + display: block; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px,1px,1px,1px); + border-radius: 3px; + z-index: 1; +} + +.umb-app-header__skip-button:focus { + height: auto; + width: auto; + clip: auto; + padding: 10px; + line-height: normal; + text-decoration: none; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less index 0cbb3ee60f..6f2f6cef14 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-editor.less @@ -5,6 +5,7 @@ .umb-editor { box-shadow: 0px 0 30px 0 rgba(0,0,0,.3); + position: fixed; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/rte-content.less b/src/Umbraco.Web.UI.Client/src/less/rte-content.less index 3fe4e52f92..f19b1edb98 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte-content.less @@ -4,9 +4,13 @@ .mce-content-body .umb-macro-holder { border: 3px dotted @pinkLight; padding: 7px; - display: block; margin: 3px; } +.mce-content-body .umb-macro-holder.inlined-macro { + border: 1px dotted @pinkLight; + padding: 1px; + margin: 0px; +} .umb-rte .mce-content-body .umb-macro-holder.loading { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html index 9d673ce8bb..fae3543429 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html @@ -1,5 +1,9 @@
+ + + + -
\ No newline at end of file +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditorOption.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditorOption.component.js index 78d27a6055..6436b3f510 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditorOption.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridColumnEditorOption.component.js @@ -23,9 +23,13 @@ } }); - function BlockGridColumnOptionController() { + function BlockGridColumnOptionController(localizationService) { - var vm = this; + var vm = this; + + localizationService.localize("general_remove").then(function (value) { + vm.removeLabel = value; + }) vm.$onInit = function() { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js index 4ebac72928..9717e5537a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js @@ -708,7 +708,7 @@ } else if(allowance.elementTypeKey) { const blockType = vm.availableBlockTypes.find(x => x.blockConfigModel.contentElementTypeKey === allowance.elementTypeKey); - if(allowedElementTypes.indexOf(blockType) === -1) { + if(blockType && allowedElementTypes.indexOf(blockType) === -1) { allowedElementTypes.push(blockType); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 9c36ce7355..300aa771e3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -1,890 +1,909 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, $location, contentEditingHelper, listViewHelper, navigationService, editorService, overlayService, languageResource, mediaHelper, eventsService) { - //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content - // that isn't created yet, if we continue this will use the parent id in the route params which isn't what - // we want. NOTE: This is just a safety check since when we scaffold an empty model on the server we remove - // the list view tab entirely when it's new. - if ($routeParams.create) { - $scope.isNew = true; - return; + //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content + // that isn't created yet, if we continue this will use the parent id in the route params which isn't what + // we want. NOTE: This is just a safety check since when we scaffold an empty model on the server we remove + // the list view tab entirely when it's new. + if ($routeParams.create) { + $scope.isNew = true; + return; + } + + //Now we need to check if this is for media, members or content because that will depend on the resources we use + var contentResource, getContentTypesCallback, getListResultsCallback, deleteItemCallback, getIdCallback, createEditUrlCallback; + + //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) + if (($scope.model.config.entityType && $scope.model.config.entityType === "member") || (appState.getSectionState("currentSection") === "member")) { + //handling issue 13770 + if ($scope.model.config.entityType && $scope.model.config.entityType === "media") { + + $scope.entityType = "media"; + contentResource = $injector.get('mediaResource'); + getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes; + + getListResultsCallback = contentResource.getChildren; + deleteItemCallback = contentResource.deleteById; + getIdCallback = function (selected) { + return selected.id; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.id + + "?list=" + $routeParams.id + "&page=" + $scope.options.pageNumber + "&filter=" + $scope.options.filter + + "&orderBy=" + $scope.options.orderBy + "&orderDirection=" + $scope.options.orderDirection; + }; + + + } else { + $scope.entityType = "member"; + contentResource = $injector.get('memberResource'); + getContentTypesCallback = $injector.get('memberTypeResource').getTypes; + getListResultsCallback = contentResource.getPagedResults; + deleteItemCallback = contentResource.deleteByKey; + getIdCallback = function (selected) { + return selected.key; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key + "?page=" + $scope.options.pageNumber + "&listName=" + $scope.contentId; + }; } - //Now we need to check if this is for media, members or content because that will depend on the resources we use - var contentResource, getContentTypesCallback, getListResultsCallback, deleteItemCallback, getIdCallback, createEditUrlCallback; + } + else { //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) - if (($scope.model.config.entityType && $scope.model.config.entityType === "member") || (appState.getSectionState("currentSection") === "member")) { - $scope.entityType = "member"; - contentResource = $injector.get('memberResource'); - getContentTypesCallback = $injector.get('memberTypeResource').getTypes; - getListResultsCallback = contentResource.getPagedResults; - deleteItemCallback = contentResource.deleteByKey; - getIdCallback = function (selected) { - return selected.key; - }; - createEditUrlCallback = function (item) { - return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key + "?page=" + $scope.options.pageNumber + "&listName=" + $scope.contentId; - }; + if (($scope.model.config.entityType && $scope.model.config.entityType === "media") || (appState.getSectionState("currentSection") === "media")) { + $scope.entityType = "media"; + contentResource = $injector.get('mediaResource'); + getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes; } else { - //check the config for the entity type, or the current section name (since the config is only set in c#, not in pre-vals) - if (($scope.model.config.entityType && $scope.model.config.entityType === "media") || (appState.getSectionState("currentSection") === "media")) { - $scope.entityType = "media"; - contentResource = $injector.get('mediaResource'); - getContentTypesCallback = $injector.get('mediaTypeResource').getAllowedTypes; + $scope.entityType = "content"; + contentResource = $injector.get('contentResource'); + getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes; + } + getListResultsCallback = contentResource.getChildren; + deleteItemCallback = contentResource.deleteById; + getIdCallback = function (selected) { + return selected.id; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.id + + "?list=" + $routeParams.id + "&page=" + $scope.options.pageNumber + "&filter=" + $scope.options.filter + + "&orderBy=" + $scope.options.orderBy + "&orderDirection=" + $scope.options.orderDirection; + }; + } + + $scope.pagination = []; + $scope.isNew = false; + $scope.actionInProgress = false; + $scope.selection = []; + $scope.folders = []; + $scope.page = { + createDropdownOpen: false + }; + $scope.listViewResultSet = { + totalPages: 0, + items: [] + }; + + $scope.createAllowedButtonSingle = false; + $scope.createAllowedButtonSingleWithBlueprints = false; + $scope.createAllowedButtonMultiWithBlueprints = false; + + //when this is null, we don't check permissions + $scope.currentNodePermissions = $scope.entityType === "content" ? contentEditingHelper.getPermissionsForContent() : null; + + //when this is null, we don't check permissions + $scope.buttonPermissions = null; + + //When we are dealing with 'content', we need to deal with permissions on child nodes. + // Currently there is no real good way to + if ($scope.entityType === "content") { + + var idsWithPermissions = null; + + $scope.buttonPermissions = { + canCopy: false, + canCreate: false, + canDelete: false, + canMove: false, + canPublish: false, + canUnpublish: false + }; + + $scope.$watch("selection.length", function (newVal, oldVal) { + + if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { + + //get all of the selected ids + var ids = _.map($scope.selection, function (i) { + return i.id.toString(); + }); + + //remove the dictionary items that don't have matching ids + var filtered = {}; + _.each(idsWithPermissions, function (value, key, list) { + if (_.contains(ids, key)) { + filtered[key] = value; + } + }); + idsWithPermissions = filtered; + + //find all ids that we haven't looked up permissions for + var existingIds = _.keys(idsWithPermissions); + var missingLookup = _.map(_.difference(ids, existingIds), function (i) { + return Number(i); + }); + + if (missingLookup.length > 0) { + currentUserResource.getPermissions(missingLookup).then(function (p) { + $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); + }); } else { - $scope.entityType = "content"; - contentResource = $injector.get('contentResource'); - getContentTypesCallback = $injector.get('contentTypeResource').getAllowedTypes; + $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions); } - getListResultsCallback = contentResource.getChildren; - deleteItemCallback = contentResource.deleteById; - getIdCallback = function (selected) { - return selected.id; - }; - createEditUrlCallback = function (item) { - return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.id - + "?list=" + $routeParams.id + "&page=" + $scope.options.pageNumber + "&filter=" + $scope.options.filter - + "&orderBy=" + $scope.options.orderBy + "&orderDirection=" + $scope.options.orderDirection; - }; - } + } + }); - $scope.pagination = []; - $scope.isNew = false; - $scope.actionInProgress = false; - $scope.selection = []; - $scope.folders = []; - $scope.page = { - createDropdownOpen: false - }; - $scope.listViewResultSet = { - totalPages: 0, - items: [] - }; + } - $scope.createAllowedButtonSingle = false; - $scope.createAllowedButtonSingleWithBlueprints = false; - $scope.createAllowedButtonMultiWithBlueprints = false; + var listParamsForCurrent = $routeParams.id == $routeParams.list; - //when this is null, we don't check permissions - $scope.currentNodePermissions = $scope.entityType === "content" ? contentEditingHelper.getPermissionsForContent() : null; - - //when this is null, we don't check permissions - $scope.buttonPermissions = null; - - //When we are dealing with 'content', we need to deal with permissions on child nodes. - // Currently there is no real good way to - if ($scope.entityType === "content") { - - var idsWithPermissions = null; - - $scope.buttonPermissions = { - canCopy: false, - canCreate: false, - canDelete: false, - canMove: false, - canPublish: false, - canUnpublish: false - }; - - $scope.$watch("selection.length", function (newVal, oldVal) { - - if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { - - //get all of the selected ids - var ids = _.map($scope.selection, function (i) { - return i.id.toString(); - }); - - //remove the dictionary items that don't have matching ids - var filtered = {}; - _.each(idsWithPermissions, function (value, key, list) { - if (_.contains(ids, key)) { - filtered[key] = value; - } - }); - idsWithPermissions = filtered; - - //find all ids that we haven't looked up permissions for - var existingIds = _.keys(idsWithPermissions); - var missingLookup = _.map(_.difference(ids, existingIds), function (i) { - return Number(i); - }); - - if (missingLookup.length > 0) { - currentUserResource.getPermissions(missingLookup).then(function (p) { - $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); - }); - } - else { - $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions); - } - } - }); - - } - - var listParamsForCurrent = $routeParams.id == $routeParams.list; - - $scope.options = { - useInfiniteEditor: $scope.model.config.useInfiniteEditor === true, - pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, + $scope.options = { + useInfiniteEditor: $scope.model.config.useInfiniteEditor === true, + pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, pageNumber: (listParamsForCurrent && $routeParams.page && !isNaN($routeParams.page) && Number($routeParams.page) > 0) ? $routeParams.page : 1, - filter: (listParamsForCurrent && $routeParams.filter ? $routeParams.filter : '').trim(), - orderBy: (listParamsForCurrent && $routeParams.orderBy ? $routeParams.orderBy : $scope.model.config.orderBy ? $scope.model.config.orderBy : 'VersionDate').trim(), - orderDirection: (listParamsForCurrent && $routeParams.orderDirection ? $routeParams.orderDirection : $scope.model.config.orderDirection ? $scope.model.config.orderDirection : "desc").trim(), - orderBySystemField: true, - includeProperties: $scope.model.config.includeProperties ? $scope.model.config.includeProperties : [ - { alias: 'updateDate', header: 'Last edited', isSystem: 1 }, - { alias: 'updater', header: 'Last edited by', isSystem: 1 } - ], - layout: { - layouts: $scope.model.config.layouts, - activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) - }, - allowBulkPublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkPublish && !$scope.readonly, - allowBulkUnpublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkUnpublish && !$scope.readonly, - allowBulkCopy: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkCopy && !$scope.readonly, - allowBulkMove: $scope.entityType !== 'member' && $scope.model.config.bulkActionPermissions.allowBulkMove && !$scope.readonly, - allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete && !$scope.readonly, - allowCreate: !$scope.readonly, - cultureName: $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture, - readonly: $scope.readonly - }; - - _.each($scope.options.includeProperties, function (property) { + filter: (listParamsForCurrent && $routeParams.filter ? $routeParams.filter : '').trim(), + orderBy: (listParamsForCurrent && $routeParams.orderBy ? $routeParams.orderBy : $scope.model.config.orderBy ? $scope.model.config.orderBy : 'VersionDate').trim(), + orderDirection: (listParamsForCurrent && $routeParams.orderDirection ? $routeParams.orderDirection : $scope.model.config.orderDirection ? $scope.model.config.orderDirection : "desc").trim(), + orderBySystemField: true, + includeProperties: $scope.model.config.includeProperties ? $scope.model.config.includeProperties : [ + { alias: 'updateDate', header: 'Last edited', isSystem: 1 }, + { alias: 'updater', header: 'Last edited by', isSystem: 1 } + ], + layout: { + layouts: $scope.model.config.layouts, + activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) + }, + allowBulkPublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkPublish && !$scope.readonly, + allowBulkUnpublish: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkUnpublish && !$scope.readonly, + allowBulkCopy: $scope.entityType === 'content' && $scope.model.config.bulkActionPermissions.allowBulkCopy && !$scope.readonly, + allowBulkMove: $scope.entityType !== 'member' && $scope.model.config.bulkActionPermissions.allowBulkMove && !$scope.readonly, + allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete && !$scope.readonly, + allowCreate: !$scope.readonly, + cultureName: $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture, + readonly: $scope.readonly + }; + + _.each($scope.options.includeProperties, function (property) { property.nameExp = property.nameTemplate - ? $interpolate(property.nameTemplate) - : undefined; - }); + ? $interpolate(property.nameTemplate) + : undefined; + }); - //watch for culture changes in the query strings and update accordingly - $scope.$watch(function () { - return $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; - }, function (newVal, oldVal) { - if (newVal && newVal !== oldVal) { - //update the options - $scope.options.cultureName = newVal; - $scope.reloadView($scope.contentId); - } - }); + //watch for culture changes in the query strings and update accordingly + $scope.$watch(function () { + return $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture; + }, function (newVal, oldVal) { + if (newVal && newVal !== oldVal) { + //update the options + $scope.options.cultureName = newVal; + $scope.reloadView($scope.contentId); + } + }); - // Check if selected order by field is actually custom field - for (var j = 0; j < $scope.options.includeProperties.length; j++) { - var includedProperty = $scope.options.includeProperties[j]; - if (includedProperty.alias.toLowerCase() === $scope.options.orderBy.toLowerCase()) { - $scope.options.orderBySystemField = includedProperty.isSystem === 1; - break; - } + // Check if selected order by field is actually custom field + for (var j = 0; j < $scope.options.includeProperties.length; j++) { + var includedProperty = $scope.options.includeProperties[j]; + if (includedProperty.alias.toLowerCase() === $scope.options.orderBy.toLowerCase()) { + $scope.options.orderBySystemField = includedProperty.isSystem === 1; + break; + } + } + + //update all of the system includeProperties to enable sorting + _.each($scope.options.includeProperties, function (e, i) { + e.allowSorting = true; + + // Special case for members, only the configured system fields should be enabled sorting + // (see MemberRepository.ApplySystemOrdering) + if (e.isSystem && $scope.entityType === "member") { + e.allowSorting = e.alias === "username" || + e.alias === "email" || + e.alias === "updateDate" || + e.alias === "createDate" || + e.alias === "contentTypeAlias"; } - //update all of the system includeProperties to enable sorting - _.each($scope.options.includeProperties, function (e, i) { - e.allowSorting = true; + if (e.isSystem) { + //localize the header + var key = getLocalizedKey(e.alias); + localizationService.localize(key).then(function (v) { + e.header = v; + }); + } + }); - // Special case for members, only the configured system fields should be enabled sorting - // (see MemberRepository.ApplySystemOrdering) - if (e.isSystem && $scope.entityType === "member") { - e.allowSorting = e.alias === "username" || - e.alias === "email" || - e.alias === "updateDate" || - e.alias === "createDate" || - e.alias === "contentTypeAlias"; - } + $scope.selectLayout = function (layout) { + $scope.options.layout.activeLayout = listViewHelper.setLayout($routeParams.id, layout, $scope.model.config.layouts); + }; - if (e.isSystem) { - //localize the header - var key = getLocalizedKey(e.alias); - localizationService.localize(key).then(function (v) { - e.header = v; - }); - } - }); + function showNotificationsAndReset(err, reload, successMsgPromise) { - $scope.selectLayout = function (layout) { - $scope.options.layout.activeLayout = listViewHelper.setLayout($routeParams.id, layout, $scope.model.config.layouts); - }; + //check if response is ysod + if (err.status && err.status >= 500) { - function showNotificationsAndReset(err, reload, successMsgPromise) { - - //check if response is ysod - if (err.status && err.status >= 500) { - - // Open ysod overlay - overlayService.ysod(err); - } - - $timeout(function () { - $scope.bulkStatus = ""; - $scope.actionInProgress = false; - }, 500); - - if (successMsgPromise) - { - localizationService.localize("bulk_done").then(function (v) { - successMsgPromise.then(function (successMsg) { - notificationsService.success(v, successMsg); - }) - }); - } + // Open ysod overlay + overlayService.ysod(err); } - $scope.next = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $timeout(function () { + $scope.bulkStatus = ""; + $scope.actionInProgress = false; + }, 500); - $scope.goToPage = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + if (successMsgPromise) { + localizationService.localize("bulk_done").then(function (v) { + successMsgPromise.then(function (successMsg) { + notificationsService.success(v, successMsg); + }) + }); + } + } - $scope.prev = function (pageNumber) { - $scope.options.pageNumber = pageNumber; - $scope.reloadView($scope.contentId); - }; + $scope.next = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; + + $scope.goToPage = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; + + $scope.prev = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView($scope.contentId); + }; - /*Loads the search results, based on parameters set in prev,next,sort and so on*/ - /*Pagination is done by an array of objects, due angularJS's funky way of monitoring state - with simple values */ + /*Loads the search results, based on parameters set in prev,next,sort and so on*/ + /*Pagination is done by an array of objects, due angularJS's funky way of monitoring state + with simple values */ - $scope.getContent = function (contentId) { - $scope.reloadView($scope.contentId, true); - }; + $scope.getContent = function (contentId) { + $scope.reloadView($scope.contentId, true); + }; - $scope.reloadView = function (id, reloadActiveNode) { - if (!id) { - return; - } - - $scope.viewLoaded = false; - $scope.folders = []; + $scope.reloadView = function (id, reloadActiveNode) { + if (!id) { + return; + } - listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + $scope.viewLoaded = false; + $scope.folders = []; - getListResultsCallback(id, $scope.options).then(function (data) { + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); - $scope.actionInProgress = false; - $scope.listViewResultSet = data; + getListResultsCallback(id, $scope.options).then(function (data) { - //update all values for display - var section = appState.getSectionState("currentSection"); - if ($scope.listViewResultSet.items) { - _.each($scope.listViewResultSet.items, function (e, index) { - setPropertyValues(e); - // create the folders collection (only for media list views) - if (section === "media" && !mediaHelper.hasFilePropertyType(e)) { - $scope.folders.push(e); - } - }); - } + $scope.actionInProgress = false; + $scope.listViewResultSet = data; - $scope.viewLoaded = true; - - //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example - // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last - // available page and then re-load again - if ($scope.options.pageNumber > $scope.listViewResultSet.totalPages) { - $scope.options.pageNumber = $scope.listViewResultSet.totalPages; - - //reload! - $scope.reloadView(id, reloadActiveNode); - } - // in the media section, the list view items are by default also shown in the tree, so we need - // to refresh the current tree node when changing the folder contents (adding and removing) - else if (reloadActiveNode && section === "media") { - var activeNode = appState.getTreeState("selectedNode"); - if (activeNode) { - if (activeNode.expanded) { - navigationService.reloadNode(activeNode); - } - } else { - navigationService.reloadSection(section); - } - } - }).catch(function(error){ - // if someone attempts to add mix listviews across sections (i.e. use a members list view on content types), - // a not-supported exception will be most likely be thrown, at least for the default list views - lets be - // helpful and show a meaningful error message directly in content/content type UI - if(error.data && error.data.ExceptionType && error.data.ExceptionType.indexOf("System.NotSupportedException") > -1) { - $scope.viewLoadedError = error.errorMsg + ": " + error.data.ExceptionMessage; + //update all values for display + var section = appState.getSectionState("currentSection"); + if ($scope.listViewResultSet.items) { + _.each($scope.listViewResultSet.items, function (e, index) { + setPropertyValues(e); + // create the folders collection (only for media list views) + if (section === "media" && !mediaHelper.hasFilePropertyType(e)) { + $scope.folders.push(e); } - $scope.viewLoaded = true; }); - }; + } - $scope.makeSearch = function() { - if ($scope.options.filter !== null && $scope.options.filter !== undefined) { - $scope.options.pageNumber = 1; - $scope.reloadView($scope.contentId); - } - }; + $scope.viewLoaded = true; - $scope.onSearchStartTyping = function() { - $scope.viewLoaded = false; - } + //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example + // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last + // available page and then re-load again + if ($scope.options.pageNumber > $scope.listViewResultSet.totalPages) { + $scope.options.pageNumber = $scope.listViewResultSet.totalPages; - $scope.selectedItemsCount = function () { - return $scope.selection.length; - }; - - $scope.clearSelection = function () { - listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); - }; - - $scope.getIcon = function (entry) { - return iconHelper.convertFromLegacyIcon(entry.icon); - }; - - function serial(selected, fn, getStatusMsg, index) { - return fn(selected, index).then(function (content) { - index++; - getStatusMsg(index, selected.length).then(function (value) { - $scope.bulkStatus = value; - }); - return index < selected.length ? serial(selected, fn, getStatusMsg, index) : content; - }, function (err) { - var reload = index > 0; - showNotificationsAndReset(err, reload); - return err; - }); - } - - function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) { - var selected = $scope.selection; - if (selected.length === 0) - return; - if (confirmMsg && !confirm(confirmMsg)) - return; - - $scope.actionInProgress = true; - - getStatusMsg(0, selected.length).then(function (value) { - $scope.bulkStatus = value; - }); - - return serial(selected, fn, getStatusMsg, 0).then(function (result) { - // executes once the whole selection has been processed - // in case of an error (caught by serial), result will be the error - if (!(result.data && Utilities.isArray(result.data.notifications))) - showNotificationsAndReset(result, true, getSuccessMsg(selected.length)); - }); - } - - $scope.delete = function (numberOfItems, totalItems) { - - const dialog = { - view: "views/propertyeditors/listview/overlays/delete.html", - deletesVariants: selectionHasVariants(), - isTrashed: $scope.isTrashed, - selection: $scope.selection, - submitButtonLabelKey: "contentTypeEditor_yesDelete", - submitButtonStyle: "danger", - submit: function (model) { - performDelete(); - overlayService.close(); - }, - close: function () { - overlayService.close(); - }, - numberOfItems: numberOfItems, - totalItems: totalItems - }; - - localizationService.localize("general_delete").then(value => { - dialog.title = value; - overlayService.open(dialog); - }); - }; - - function performDelete() { - applySelected( - function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, - function (count, total) { - var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); - return localizationService.localize(key, [total]); - }).then(function () { - $scope.reloadView($scope.contentId, true); - }); - } - - function selectionHasVariants() { - let variesByCulture = false; - - // check if any of the selected nodes has variants - $scope.selection.forEach(selectedItem => { - $scope.listViewResultSet.items.forEach(resultItem => { - if ((selectedItem.id === resultItem.id || selectedItem.key === resultItem.key) && resultItem.variesByCulture) { - variesByCulture = true; - } - }) - }); - - return variesByCulture; - } - - $scope.publish = function () { - - const dialog = { - view: "views/propertyeditors/listview/overlays/listviewpublish.html", - submitButtonLabelKey: "actions_publish", - submit: function (model) { - // create a comma separated array of selected cultures - let selectedCultures = []; - if (model.languages && model.languages.length > 0) { - model.languages.forEach(language => { - if (language.publish) { - selectedCultures.push(language.culture); - } - }); - } - performPublish(selectedCultures); - overlayService.close(); - }, - close: function () { - overlayService.close(); - } - }; - - // if any of the selected nodes has variants we want to - // show a dialog where the languages can be chosen - if (selectionHasVariants()) { - languageResource.getAll() - .then(languages => { - dialog.languages = languages; - overlayService.open(dialog); - }, error => { - notificationsService.error(error); - }); + //reload! + $scope.reloadView(id, reloadActiveNode); + } + // in the media section, the list view items are by default also shown in the tree, so we need + // to refresh the current tree node when changing the folder contents (adding and removing) + else if (reloadActiveNode && section === "media") { + var activeNode = appState.getTreeState("selectedNode"); + if (activeNode) { + if (activeNode.expanded) { + navigationService.reloadNode(activeNode); + } } else { - overlayService.open(dialog); + navigationService.reloadSection(section); } + } + }).catch(function (error) { + // if someone attempts to add mix listviews across sections (i.e. use a members list view on content types), + // a not-supported exception will be most likely be thrown, at least for the default list views - lets be + // helpful and show a meaningful error message directly in content/content type UI + if (error.data && error.data.ExceptionType && error.data.ExceptionType.indexOf("System.NotSupportedException") > -1) { + $scope.viewLoadedError = error.errorMsg + ": " + error.data.ExceptionMessage; + } + $scope.viewLoaded = true; + }); + }; + $scope.makeSearch = function () { + if ($scope.options.filter !== null && $scope.options.filter !== undefined) { + $scope.options.pageNumber = 1; + $scope.reloadView($scope.contentId); + } + }; + + $scope.onSearchStartTyping = function () { + $scope.viewLoaded = false; + } + + $scope.selectedItemsCount = function () { + return $scope.selection.length; + }; + + $scope.clearSelection = function () { + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + }; + + $scope.getIcon = function (entry) { + return iconHelper.convertFromLegacyIcon(entry.icon); + }; + + function serial(selected, fn, getStatusMsg, index) { + return fn(selected, index).then(function (content) { + index++; + getStatusMsg(index, selected.length).then(function (value) { + $scope.bulkStatus = value; + }); + return index < selected.length ? serial(selected, fn, getStatusMsg, index) : content; + }, function (err) { + var reload = index > 0; + showNotificationsAndReset(err, reload); + return err; + }); + } + + function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) { + var selected = $scope.selection; + if (selected.length === 0) + return; + if (confirmMsg && !confirm(confirmMsg)) + return; + + $scope.actionInProgress = true; + + getStatusMsg(0, selected.length).then(function (value) { + $scope.bulkStatus = value; + }); + + return serial(selected, fn, getStatusMsg, 0).then(function (result) { + // executes once the whole selection has been processed + // in case of an error (caught by serial), result will be the error + if (!(result.data && Utilities.isArray(result.data.notifications))) + showNotificationsAndReset(result, true, getSuccessMsg(selected.length)); + }); + } + + $scope.delete = function (numberOfItems, totalItems) { + + const dialog = { + view: "views/propertyeditors/listview/overlays/delete.html", + deletesVariants: selectionHasVariants(), + isTrashed: $scope.isTrashed, + selection: $scope.selection, + submitButtonLabelKey: "contentTypeEditor_yesDelete", + submitButtonStyle: "danger", + submit: function (model) { + performDelete(); + overlayService.close(); + }, + close: function () { + overlayService.close(); + }, + numberOfItems: numberOfItems, + totalItems: totalItems }; - function performPublish(cultures) { - applySelected( - function (selected, index) { return contentResource.publishById(getIdCallback(selected[index]), cultures); }, - function (count, total) { - var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); - return localizationService.localize(key, [total]); - }).then(function () { - $scope.reloadView($scope.contentId); - }); - } + localizationService.localize("general_delete").then(value => { + dialog.title = value; + overlayService.open(dialog); + }); + }; - $scope.unpublish = function () { + function performDelete() { + applySelected( + function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, + function (count, total) { + var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); + return localizationService.localize(key, [total]); + }).then(function () { + $scope.reloadView($scope.contentId, true); + }); + } - const dialog = { - view: "views/propertyeditors/listview/overlays/listviewunpublish.html", - submitButtonLabelKey: "actions_unpublish", - submitButtonStyle: "warning", - selection: $scope.selection, - submit: function (model) { - // create a comma separated array of selected cultures - let selectedCultures = []; - if (model.languages && model.languages.length > 0) { - model.languages.forEach(language => { - if (language.unpublish) { - selectedCultures.push(language.culture); - } - }); - } - performUnpublish(selectedCultures); - overlayService.close(); - }, - close: function () { - overlayService.close(); - } - }; + function selectionHasVariants() { + let variesByCulture = false; - // if any of the selected nodes has variants we want to - // show a dialog where the languages can be chosen - if (selectionHasVariants()) { - languageResource.getAll() - .then(languages => { - dialog.languages = languages; - overlayService.open(dialog); - }, error => { - notificationsService.error(error); - }); - } else { - overlayService.open(dialog); + // check if any of the selected nodes has variants + $scope.selection.forEach(selectedItem => { + $scope.listViewResultSet.items.forEach(resultItem => { + if ((selectedItem.id === resultItem.id || selectedItem.key === resultItem.key) && resultItem.variesByCulture) { + variesByCulture = true; } + }) + }); + return variesByCulture; + } + + $scope.publish = function () { + + const dialog = { + view: "views/propertyeditors/listview/overlays/listviewpublish.html", + submitButtonLabelKey: "actions_publish", + submit: function (model) { + // create a comma separated array of selected cultures + let selectedCultures = []; + if (model.languages && model.languages.length > 0) { + model.languages.forEach(language => { + if (language.publish) { + selectedCultures.push(language.culture); + } + }); + } + performPublish(selectedCultures); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } }; - function performUnpublish(cultures) { - applySelected( - function (selected, index) { return contentResource.unpublish(getIdCallback(selected[index]), cultures); }, - function (count, total) { - var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems"); - return localizationService.localize(key, [total]); - }).then(function () { - $scope.reloadView($scope.contentId, true); - }); - } - - $scope.move = function () { - var move = { - section: $scope.entityType, - currentNode: $scope.contentId, - submit: function (model) { - if (model.target) { - performMove(model.target); - } - editorService.close(); - }, - close: function () { - editorService.close(); - } - } - editorService.move(move); - }; - - - function performMove(target) { - - //NOTE: With the way this applySelected/serial works, I'm not sure there's a better way currently to return - // a specific value from one of the methods, so we'll have to try this way. Even though the first method - // will fire once per every node moved, the destination path will be the same and we need to use that to sync. - var newPath = null; - applySelected( - function (selected, index) { - return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) - .then(function (path) { - newPath = path; - return path; - }); - }, - function (count, total) { - var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); - return localizationService.localize(key, [total]); - }) - .then(function () { - //executes if all is successful, let's sync the tree - if (newPath) { - // reload the current view so the moved items are no longer shown - $scope.reloadView($scope.contentId); - - //we need to do a double sync here: first refresh the node where the content was moved, - // then refresh the node where the content was moved from - navigationService.syncTree({ - tree: target.nodeType ? target.nodeType : (target.metaData.treeAlias), - path: newPath, - forceReload: true, - activate: false - }) - .then(function (args) { - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - if (activeNode) { - navigationService.reloadNode(activeNode); - } - }); - } - }); - } - - $scope.copy = function () { - var copyEditor = { - section: $scope.entityType, - currentNode: $scope.contentId, - submit: function (model) { - if (model.target) { - performCopy(model.target, model.relateToOriginal, model.includeDescendants); - } - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.copy(copyEditor); - }; - - function performCopy(target, relateToOriginal, includeDescendants) { - applySelected( - function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal, recursive: includeDescendants }); }, - function (count, total) { - var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); - return localizationService.localize(key, [count, total]); - }, - function (total) { - var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); - return localizationService.localize(key, [total]); - }); - } - - function getCustomPropertyValue(alias, properties) { - var value = ''; - var index = 0; - var foundAlias = false; - for (var i = 0; i < properties.length; i++) { - if (properties[i].alias == alias) { - foundAlias = true; - break; - } - index++; - } - - if (foundAlias) { - value = properties[index].value; - } - - return value; - } - - /** This ensures that the correct value is set for each item in a row, we don't want to call a function during interpolation or ng-bind as performance is really bad that way */ - function setPropertyValues(result) { - - //set the edit url - result.editPath = createEditUrlCallback(result); - - _.each($scope.options.includeProperties, function (e, i) { - - var alias = e.alias; - - // First try to pull the value directly from the alias (e.g. updatedBy) - var value = result[alias]; - - // If this returns an object, look for the name property of that (e.g. owner.name) - if (value === Object(value)) { - value = value['name']; - } - - // If we've got nothing yet, look at a user defined property - if (typeof value === 'undefined') { - value = getCustomPropertyValue(alias, result.properties); - } - - // If we have a date, format it - if (isDate(value)) { - value = value.substring(0, value.length - 3); - } - - if (e.nameExp) { - if (/{{.*\s*\w+\s*\|\s*\w+\s*.*}}/.test(e.nameTemplate)) { //check whether the name template has a filter - value = { - value, - expression: e.nameExp - }; - } - else { - var newValue = e.nameExp({ value }); - - if (newValue && (newValue = newValue.trim())) { - value = newValue; - } - } - } - - // set what we've got on the result - result[alias] = value; + // if any of the selected nodes has variants we want to + // show a dialog where the languages can be chosen + if (selectionHasVariants()) { + languageResource.getAll() + .then(languages => { + dialog.languages = languages; + overlayService.open(dialog); + }, error => { + notificationsService.error(error); }); + } else { + overlayService.open(dialog); } - function isDate(val) { - if (Utilities.isString(val)) { - return val.match(/^(\d{4})\-(\d{2})\-(\d{2})\ (\d{2})\:(\d{2})\:(\d{2})$/); - } - return false; - } - - function initView() { - - var id = $routeParams.id; - if (id === undefined) { - // no ID found in route params - don't list anything as we don't know for sure where we are - return; - } - - // Get current id for node to load it's children - $scope.contentId = editorState.current ? editorState.current.id : id; - $scope.isTrashed = editorState.current ? editorState.current.trashed : id === "-20" || id === "-21"; - - $scope.options.allowBulkPublish = $scope.options.allowBulkPublish && !$scope.isTrashed; - $scope.options.allowBulkUnpublish = $scope.options.allowBulkUnpublish && !$scope.isTrashed; - $scope.options.allowBulkCopy = $scope.options.allowBulkCopy && !$scope.isTrashed; - - $scope.options.bulkActionsAllowed = $scope.options.allowBulkPublish || - $scope.options.allowBulkUnpublish || - $scope.options.allowBulkCopy || - $scope.options.allowBulkMove || - $scope.options.allowBulkDelete; - - if ($scope.isTrashed === false) { - getContentTypesCallback(id).then(function (listViewAllowedTypes) { - $scope.listViewAllowedTypes = listViewAllowedTypes; - - var blueprints = false; - _.each(listViewAllowedTypes, function (allowedType) { - if (_.isEmpty(allowedType.blueprints)) { - // this helps the view understand that there are no blueprints available - allowedType.blueprints = null; - } - else { - blueprints = true; - // turn the content type blueprints object into an array of sortable objects for the view - allowedType.blueprints = _.map(_.pairs(allowedType.blueprints || {}), function (pair) { - return { - id: pair[0], - name: pair[1] - }; - }); - } - }); - - if (listViewAllowedTypes.length === 1 && blueprints === false) { - $scope.createAllowedButtonSingle = true; - } - if (listViewAllowedTypes.length === 1 && blueprints === true) { - $scope.createAllowedButtonSingleWithBlueprints = true; - } - if (listViewAllowedTypes.length > 1) { - $scope.createAllowedButtonMultiWithBlueprints = true; - } - }); - } + }; + function performPublish(cultures) { + applySelected( + function (selected, index) { return contentResource.publishById(getIdCallback(selected[index]), cultures); }, + function (count, total) { + var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); + return localizationService.localize(key, [total]); + }).then(function () { $scope.reloadView($scope.contentId); - } + }); + } - function getLocalizedKey(alias) { + $scope.unpublish = function () { - switch (alias) { - case "sortOrder": - return "general_sort"; - case "updateDate": - return "content_updateDate"; - case "updater": - return "content_updatedBy"; - case "createDate": - return "content_createDate"; - case "owner": - return "content_createBy"; - case "published": - return "content_isPublished"; - case "contentTypeAlias": - return $scope.entityType === "content" - ? "content_documentType" - : $scope.entityType === "media" - ? "content_mediatype" - : "content_membertype"; - case "email": - return "general_email"; - case "username": - return "general_username"; + const dialog = { + view: "views/propertyeditors/listview/overlays/listviewunpublish.html", + submitButtonLabelKey: "actions_unpublish", + submitButtonStyle: "warning", + selection: $scope.selection, + submit: function (model) { + // create a comma separated array of selected cultures + let selectedCultures = []; + if (model.languages && model.languages.length > 0) { + model.languages.forEach(language => { + if (language.unpublish) { + selectedCultures.push(language.culture); + } + }); } - return alias; + performUnpublish(selectedCultures); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + // if any of the selected nodes has variants we want to + // show a dialog where the languages can be chosen + if (selectionHasVariants()) { + languageResource.getAll() + .then(languages => { + dialog.languages = languages; + overlayService.open(dialog); + }, error => { + notificationsService.error(error); + }); + } else { + overlayService.open(dialog); } - function getItemKey(itemId) { - for (var i = 0; i < $scope.listViewResultSet.items.length; i++) { - var item = $scope.listViewResultSet.items[i]; - if (item.id === itemId) { - return item.key; - } + }; + + function performUnpublish(cultures) { + applySelected( + function (selected, index) { return contentResource.unpublish(getIdCallback(selected[index]), cultures); }, + function (count, total) { + var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems"); + return localizationService.localize(key, [total]); + }).then(function () { + $scope.reloadView($scope.contentId, true); + }); + } + + $scope.move = function () { + var move = { + section: $scope.entityType, + currentNode: $scope.contentId, + submit: function (model) { + if (model.target) { + performMove(model.target); } + editorService.close(); + }, + close: function () { + editorService.close(); + } } + editorService.move(move); + }; - function createBlank(entityType, docTypeAlias) { - if ($scope.options.useInfiniteEditor) { - - var editorModel = { - create: true, - submit: function(model) { - editorService.close(); - $scope.reloadView($scope.contentId); - }, - close: function() { - editorService.close(); - $scope.reloadView($scope.contentId); - } - }; - if (entityType == "content") - { - editorModel.parentId = $scope.contentId; - editorModel.documentTypeAlias = docTypeAlias; - editorService.contentEditor(editorModel); - return; - } + function performMove(target) { - if (entityType == "media") - { - editorService.mediaEditor(editorModel); - return; - } + //NOTE: With the way this applySelected/serial works, I'm not sure there's a better way currently to return + // a specific value from one of the methods, so we'll have to try this way. Even though the first method + // will fire once per every node moved, the destination path will be the same and we need to use that to sync. + var newPath = null; + applySelected( + function (selected, index) { + return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) + .then(function (path) { + newPath = path; + return path; + }); + }, + function (count, total) { + var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); + return localizationService.localize(key, [total]); + }) + .then(function () { + //executes if all is successful, let's sync the tree + if (newPath) { + // reload the current view so the moved items are no longer shown + $scope.reloadView($scope.contentId); - if (entityType == "member") - { - editorModel.doctype = docTypeAlias; - editorService.memberEditor(editorModel); - return; - } + //we need to do a double sync here: first refresh the node where the content was moved, + // then refresh the node where the content was moved from + navigationService.syncTree({ + tree: target.nodeType ? target.nodeType : (target.metaData.treeAlias), + path: newPath, + forceReload: true, + activate: false + }) + .then(function (args) { + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + if (activeNode) { + navigationService.reloadNode(activeNode); + } + }); } + }); + } - $location - .path("/" + entityType + "/" + entityType + "/edit/" + $scope.contentId) - .search("doctype", docTypeAlias) - .search("create", "true"); + $scope.copy = function () { + var copyEditor = { + section: $scope.entityType, + currentNode: $scope.contentId, + submit: function (model) { + if (model.target) { + performCopy(model.target, model.relateToOriginal, model.includeDescendants); + } + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.copy(copyEditor); + }; + + function performCopy(target, relateToOriginal, includeDescendants) { + applySelected( + function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal, recursive: includeDescendants }); }, + function (count, total) { + var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); + return localizationService.localize(key, [total]); + }); + } + + function getCustomPropertyValue(alias, properties) { + var value = ''; + var index = 0; + var foundAlias = false; + for (var i = 0; i < properties.length; i++) { + if (properties[i].alias == alias) { + foundAlias = true; + break; + } + index++; } - function createFromBlueprint(entityType, docTypeAlias, blueprintId) { - $location - .path("/" + entityType + "/" + entityType + "/edit/" + $scope.contentId) - .search("doctype", docTypeAlias) - .search("create", "true") - .search("blueprintId", blueprintId); + if (foundAlias) { + value = properties[index].value; } - function toggleDropdown () { - $scope.page.createDropdownOpen = !$scope.page.createDropdownOpen; + return value; + } + + /** This ensures that the correct value is set for each item in a row, we don't want to call a function during interpolation or ng-bind as performance is really bad that way */ + function setPropertyValues(result) { + + //set the edit url + result.editPath = createEditUrlCallback(result); + + _.each($scope.options.includeProperties, function (e, i) { + + var alias = e.alias; + + // First try to pull the value directly from the alias (e.g. updatedBy) + var value = result[alias]; + + // If this returns an object, look for the name property of that (e.g. owner.name) + if (value === Object(value)) { + value = value['name']; + } + + // If we've got nothing yet, look at a user defined property + if (typeof value === 'undefined') { + value = getCustomPropertyValue(alias, result.properties); + } + + // If we have a date, format it + if (isDate(value)) { + value = value.substring(0, value.length - 3); + } + + if (e.nameExp) { + if (/{{.*\s*\w+\s*\|\s*\w+\s*.*}}/.test(e.nameTemplate)) { //check whether the name template has a filter + value = { + value, + expression: e.nameExp + }; + } + else { + var newValue = e.nameExp({ value }); + + if (newValue && (newValue = newValue.trim())) { + value = newValue; + } + } + } + + // set what we've got on the result + result[alias] = value; + }); + } + + function isDate(val) { + if (Utilities.isString(val)) { + return val.match(/^(\d{4})\-(\d{2})\-(\d{2})\ (\d{2})\:(\d{2})\:(\d{2})$/); + } + return false; + } + + function initView() { + + var id = $routeParams.id; + if (id === undefined) { + // no ID found in route params - don't list anything as we don't know for sure where we are + return; } - function leaveDropdown () { - $scope.page.createDropdownOpen = false; - } + // Get current id for node to load it's children + $scope.contentId = editorState.current ? editorState.current.id : id; + $scope.isTrashed = editorState.current ? editorState.current.trashed : id === "-20" || id === "-21"; - $scope.createBlank = createBlank; - $scope.createFromBlueprint = createFromBlueprint; - $scope.toggleDropdown = toggleDropdown; - $scope.leaveDropdown = leaveDropdown; + $scope.options.allowBulkPublish = $scope.options.allowBulkPublish && !$scope.isTrashed; + $scope.options.allowBulkUnpublish = $scope.options.allowBulkUnpublish && !$scope.isTrashed; + $scope.options.allowBulkCopy = $scope.options.allowBulkCopy && !$scope.isTrashed; - // if this listview has sort order in it, make sure it is updated when sorting is performed on the current content - if (_.find($scope.options.includeProperties, property => property.alias === "sortOrder")) { - var eventSubscription = eventsService.on("sortCompleted", function (e, args) { - if (parseInt(args.id) === parseInt($scope.contentId)) { - $scope.reloadView($scope.contentId); - } + $scope.options.bulkActionsAllowed = $scope.options.allowBulkPublish || + $scope.options.allowBulkUnpublish || + $scope.options.allowBulkCopy || + $scope.options.allowBulkMove || + $scope.options.allowBulkDelete; + + if ($scope.isTrashed === false) { + getContentTypesCallback(id).then(function (listViewAllowedTypes) { + $scope.listViewAllowedTypes = listViewAllowedTypes; + + var blueprints = false; + _.each(listViewAllowedTypes, function (allowedType) { + if (_.isEmpty(allowedType.blueprints)) { + // this helps the view understand that there are no blueprints available + allowedType.blueprints = null; + } + else { + blueprints = true; + // turn the content type blueprints object into an array of sortable objects for the view + allowedType.blueprints = _.map(_.pairs(allowedType.blueprints || {}), function (pair) { + return { + id: pair[0], + name: pair[1] + }; + }); + } }); - $scope.$on('$destroy', function () { - eventsService.unsubscribe(eventSubscription); - }); + if (listViewAllowedTypes.length === 1 && blueprints === false) { + $scope.createAllowedButtonSingle = true; + } + if (listViewAllowedTypes.length === 1 && blueprints === true) { + $scope.createAllowedButtonSingleWithBlueprints = true; + } + if (listViewAllowedTypes.length > 1) { + $scope.createAllowedButtonMultiWithBlueprints = true; + } + }); } - //GO! - initView(); + $scope.reloadView($scope.contentId); + } + + function getLocalizedKey(alias) { + + switch (alias) { + case "sortOrder": + return "general_sort"; + case "updateDate": + return "content_updateDate"; + case "updater": + return "content_updatedBy"; + case "createDate": + return "content_createDate"; + case "owner": + return "content_createBy"; + case "published": + return "content_isPublished"; + case "contentTypeAlias": + return $scope.entityType === "content" + ? "content_documentType" + : $scope.entityType === "media" + ? "content_mediatype" + : "content_membertype"; + case "email": + return "general_email"; + case "username": + return "general_username"; + } + return alias; + } + + function getItemKey(itemId) { + for (var i = 0; i < $scope.listViewResultSet.items.length; i++) { + var item = $scope.listViewResultSet.items[i]; + if (item.id === itemId) { + return item.key; + } + } + } + + function createBlank(entityType, docTypeAlias) { + if ($scope.options.useInfiniteEditor) { + + var editorModel = { + create: true, + submit: function (model) { + editorService.close(); + $scope.reloadView($scope.contentId); + }, + close: function () { + editorService.close(); + $scope.reloadView($scope.contentId); + } + }; + + if (entityType == "content") { + editorModel.parentId = $scope.contentId; + editorModel.documentTypeAlias = docTypeAlias; + editorService.contentEditor(editorModel); + return; + } + + if (entityType == "media") { + editorService.mediaEditor(editorModel); + return; + } + + if (entityType == "member") { + editorModel.doctype = docTypeAlias; + editorService.memberEditor(editorModel); + return; + } + } + + $location + .path("/" + entityType + "/" + entityType + "/edit/" + $scope.contentId) + .search("doctype", docTypeAlias) + .search("create", "true"); + } + + function createFromBlueprint(entityType, docTypeAlias, blueprintId) { + $location + .path("/" + entityType + "/" + entityType + "/edit/" + $scope.contentId) + .search("doctype", docTypeAlias) + .search("create", "true") + .search("blueprintId", blueprintId); + } + + function toggleDropdown() { + $scope.page.createDropdownOpen = !$scope.page.createDropdownOpen; + } + + function leaveDropdown() { + $scope.page.createDropdownOpen = false; + } + + $scope.createBlank = createBlank; + $scope.createFromBlueprint = createFromBlueprint; + $scope.toggleDropdown = toggleDropdown; + $scope.leaveDropdown = leaveDropdown; + + // if this listview has sort order in it, make sure it is updated when sorting is performed on the current content + if (_.find($scope.options.includeProperties, property => property.alias === "sortOrder")) { + var eventSubscription = eventsService.on("sortCompleted", function (e, args) { + if (parseInt(args.id) === parseInt($scope.contentId)) { + $scope.reloadView($scope.contentId); + } + }); + + $scope.$on('$destroy', function () { + eventsService.unsubscribe(eventSubscription); + }); + } + + //GO! + initView(); } diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 6619754315..097236e250 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -79,6 +79,9 @@ module.exports = function (config) { // CLI --runner-port 9100 runnerPort: 9100, + // Add support for new DNS resolution in Node 17+ + listenAddress: '::', + // enable / disable colors in the output (reporters and logs) // CLI --colors --no-colors colors: true, diff --git a/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs index d57a9345af..d4fd13489b 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs @@ -38,6 +38,8 @@ public class UmbLoginStatusController : SurfaceController return CurrentUmbracoPage(); } + MergeRouteValuesToModel(model); + var isLoggedIn = HttpContext.User.Identity?.IsAuthenticated ?? false; if (isLoggedIn) @@ -56,4 +58,16 @@ public class UmbLoginStatusController : SurfaceController // Redirect to current page by default. return RedirectToCurrentUmbracoPage(); } + + /// + /// We pass in values via encrypted route values so they cannot be tampered with and merge them into the model for use + /// + /// + private void MergeRouteValuesToModel(PostRedirectModel model) + { + if (RouteData.Values.TryGetValue(nameof(PostRedirectModel.RedirectUrl), out var redirectUrl) && redirectUrl is not null) + { + model.RedirectUrl = redirectUrl.ToString(); + } + } } diff --git a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs index 12c47c5fa4..dc98c5b813 100644 --- a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs @@ -64,6 +64,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs index 2e44bae31f..d14f0bded3 100644 --- a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs @@ -337,7 +337,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, string controllerName, - object additionalRouteVals, + object? additionalRouteVals, FormMethod method) => html.BeginUmbracoForm(action, controllerName, additionalRouteVals, new Dictionary(), method); @@ -348,7 +348,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, string controllerName, - object additionalRouteVals) + object? additionalRouteVals = null) => html.BeginUmbracoForm(action, controllerName, additionalRouteVals, new Dictionary()); /// @@ -358,7 +358,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, string controllerName, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes, FormMethod method) => html.BeginUmbracoForm( @@ -375,7 +375,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, string controllerName, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes) => html.BeginUmbracoForm( action, @@ -483,7 +483,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, Type surfaceType, - object additionalRouteVals, + object? additionalRouteVals, FormMethod method) => html.BeginUmbracoForm( action, @@ -499,21 +499,21 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, Type surfaceType, - object additionalRouteVals) => + object? additionalRouteVals = null) => html.BeginUmbracoForm(action, surfaceType, additionalRouteVals, new Dictionary()); /// /// Helper method to create a new form to execute in the Umbraco request pipeline to a surface controller plugin /// /// The type - public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals, FormMethod method) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object? additionalRouteVals, FormMethod method) where T : SurfaceController => html.BeginUmbracoForm(action, typeof(T), additionalRouteVals, method); /// /// Helper method to create a new form to execute in the Umbraco request pipeline to a surface controller plugin /// /// The type - public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object? additionalRouteVals = null) where T : SurfaceController => html.BeginUmbracoForm(action, typeof(T), additionalRouteVals); /// @@ -523,7 +523,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, Type surfaceType, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes) => html.BeginUmbracoForm( action, @@ -538,7 +538,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, Type surfaceType, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes, FormMethod method) => html.BeginUmbracoForm( @@ -555,7 +555,7 @@ public static class HtmlHelperRenderExtensions this IHtmlHelper html, string action, Type surfaceType, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes, FormMethod method, bool? antiforgery) => @@ -574,7 +574,7 @@ public static class HtmlHelperRenderExtensions public static MvcForm BeginUmbracoForm( this IHtmlHelper html, string action, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes, FormMethod method) where T : SurfaceController => @@ -587,7 +587,7 @@ public static class HtmlHelperRenderExtensions public static MvcForm BeginUmbracoForm( this IHtmlHelper html, string action, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes, FormMethod method, bool? antiforgery) @@ -600,7 +600,7 @@ public static class HtmlHelperRenderExtensions public static MvcForm BeginUmbracoForm( this IHtmlHelper html, string action, - object additionalRouteVals, + object? additionalRouteVals, object htmlAttributes) where T : SurfaceController => html.BeginUmbracoForm(action, typeof(T), additionalRouteVals, htmlAttributes); @@ -690,7 +690,7 @@ public static class HtmlHelperRenderExtensions public static MvcForm BeginUmbracoForm( this IHtmlHelper html, string action, - object additionalRouteVals, + object? additionalRouteVals, IDictionary htmlAttributes, FormMethod method) where T : SurfaceController => @@ -703,7 +703,7 @@ public static class HtmlHelperRenderExtensions public static MvcForm BeginUmbracoForm( this IHtmlHelper html, string action, - object additionalRouteVals, + object? additionalRouteVals, IDictionary htmlAttributes) where T : SurfaceController => html.BeginUmbracoForm(action, typeof(T), additionalRouteVals, htmlAttributes); @@ -767,6 +767,12 @@ public static class HtmlHelperRenderExtensions nameof(controllerName)); } + // Create a new form context in order to ensure client validation is set properly when adding multiple forms in a page. More context in PR #13914. + html.ViewContext.FormContext = new FormContext + { + CanRenderAtEndOfForm = true + }; + IUmbracoContextAccessor umbracoContextAccessor = GetRequiredService(html); IUmbracoContext umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); var formAction = umbracoContext.OriginalRequestUrl.PathAndQuery; diff --git a/src/Umbraco.Web.Website/Routing/SurfaceControllerMatcherPolicy.cs b/src/Umbraco.Web.Website/Routing/SurfaceControllerMatcherPolicy.cs new file mode 100644 index 0000000000..f2a834ca09 --- /dev/null +++ b/src/Umbraco.Web.Website/Routing/SurfaceControllerMatcherPolicy.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Matching; +using Umbraco.Cms.Web.Website.Controllers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Web.Website.Routing; + +/// +/// Ensures the surface controller requests takes priority over other things like virtual routes. +/// Also ensures that requests to a surface controller on a virtual route will return 405, like HttpMethodMatcherPolicy ensures for non-virtual route requests. +/// +internal class SurfaceControllerMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy +{ + private const string Http405EndpointDisplayName = "405 HTTP Method Not Supported"; + + public override int Order { get; } // default order should be okay. Count be everything positive to not conflict with MS policies + + public bool AppliesToEndpoints(IReadOnlyList endpoints) + { + // In theory all endpoints can have the query string data for a surface controller + return true; + } + + public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates) + { + ArgumentNullException.ThrowIfNull(nameof(httpContext)); + ArgumentNullException.ThrowIfNull(nameof(candidates)); + + if (candidates.Count < 2) + { + return Task.CompletedTask; + } + + int? surfaceControllerIndex = GetSurfaceControllerCandidateIndex(candidates); + if (surfaceControllerIndex.HasValue) + { + HashSet allowedHttpMethods = GetAllowedHttpMethods(candidates[surfaceControllerIndex.Value]); + + if (allowedHttpMethods.Any() + && allowedHttpMethods.Contains(httpContext.Request.Method) is false) + { + // We need to handle this as a 405 like the HttpMethodMatcherPolicy would do. + httpContext.SetEndpoint(CreateRejectEndpoint(allowedHttpMethods)); + httpContext.Request.RouteValues = null!; + } + else + { + // Otherwise we invalidate all other endpoints than the surface controller that matched. + InvalidateAllCandidatesExceptIndex(candidates, surfaceControllerIndex.Value); + } + } + + return Task.CompletedTask; + } + + + private static HashSet GetAllowedHttpMethods(CandidateState candidate) + { + var surfaceControllerAllowedHttpMethods = new HashSet(StringComparer.OrdinalIgnoreCase); + + IHttpMethodMetadata? httpMethodMetadata = candidate.Endpoint?.Metadata.GetMetadata(); + if (httpMethodMetadata is not null) + { + foreach (var httpMethod in httpMethodMetadata.HttpMethods) + { + surfaceControllerAllowedHttpMethods.Add(httpMethod); + } + } + + return surfaceControllerAllowedHttpMethods; + } + + private static int? GetSurfaceControllerCandidateIndex(CandidateSet candidates) + { + for (var i = 0; i < candidates.Count; i++) + { + if (candidates.IsValidCandidate(i)) + { + CandidateState candidate = candidates[i]; + ControllerActionDescriptor? controllerActionDescriptor = + candidate.Endpoint?.Metadata.GetMetadata(); + + if (controllerActionDescriptor?.ControllerTypeInfo.IsType() == true) + { + return i; + } + } + } + + return null; + } + + private static void InvalidateAllCandidatesExceptIndex(CandidateSet candidates, int index) + { + for (var i = 0; i < candidates.Count; i++) + { + if (i != index) + { + candidates.SetValidity(i, false); + } + } + } + + private static Endpoint CreateRejectEndpoint(ISet allowedHttpMethods) => + new Endpoint( + (context) => + { + context.Response.StatusCode = 405; + + context.Response.Headers.Allow = string.Join(", ", allowedHttpMethods); + + return Task.CompletedTask; + }, + EndpointMetadataCollection.Empty, + Http405EndpointDisplayName); +} diff --git a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj index fb9b051667..0d4a775a95 100644 --- a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj +++ b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj @@ -12,8 +12,6 @@ - - diff --git a/templates/UmbracoProject/UmbracoProject.csproj b/templates/UmbracoProject/UmbracoProject.csproj index 00f7c4b6ff..0989706f68 100644 --- a/templates/UmbracoProject/UmbracoProject.csproj +++ b/templates/UmbracoProject/UmbracoProject.csproj @@ -26,8 +26,10 @@ false false - + + + diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 8c3caad6e2..52861c754f 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -114,9 +114,9 @@ } }, "node_modules/@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", "dev": true }, "node_modules/@sideway/pinpoint": { @@ -1055,9 +1055,9 @@ } }, "@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", "dev": true }, "@sideway/pinpoint": { diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/HtmlEncodedStringExtensionsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/HtmlEncodedStringExtensionsTests.cs new file mode 100644 index 0000000000..4703cebb40 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/HtmlEncodedStringExtensionsTests.cs @@ -0,0 +1,26 @@ +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Strings; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions; + +[TestFixture] +public class HtmlEncodedStringExtensionsTests +{ + [TestCase(null, false, true)] + [TestCase("", false, true)] + [TestCase(" ", false, true)] + [TestCase("This is a non-empty string", false, false)] + [TestCase("

This is a non-empty string

", true,false)] + [TestCase("

This is a non-empty string

", false,false)] + [TestCase("

", true, true)] + [TestCase("

", false, false)] + public void IsNullOrWhiteSpace(string? htmlString, bool stripHtml, bool expectedResult) + { + var htmlEncodedString = htmlString == null ? null : Mock.Of(x => x.ToHtmlString() == htmlString); + var result = htmlEncodedString.IsNullOrWhiteSpace(stripHtml: stripHtml); + + Assert.AreEqual(expectedResult, result); + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs index a4f5d7436f..f4283aae88 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs @@ -4,9 +4,11 @@ using System.Globalization; using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Actions; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Dictionary; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Mapping; @@ -267,7 +269,9 @@ public class ContentControllerTests Mock.Of(), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of(), + Mock.Of(), + new OptionsWrapper(new ContentSettings())); return controller; } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs index bbfcf2103e..63300eaa1b 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs @@ -396,4 +396,25 @@ asdfsdf @"", result); } + + [Test] + public void Format_RTE_WhenMacroContainsParameter_EnableInlineMacro_WithValue_1_ItShouldBeInASpan() + { + var content = @"

asdfasdf

+

asdfsadf

+ +

asdfasdf

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor( + content, + new Dictionary { { "test1", "value1" }, { "enableInlineMacro", "1"} }); + + Assert.AreEqual( + @"

asdfasdf

+

asdfsadf

+ + +Macro alias: My.Map.isCool eh[boy!] +

asdfasdf

".StripNewLines(), + result.StripNewLines()); + } }