امن سازی برنامههای ASP.NET Core توسط IdentityServer 4x - قسمت چهاردهم- آماده شدن برای انتشار برنامه
SELECT DEPARTMENTNAME, GENDER, COUNT(1) AS COUNT FROM DBO.DIMEMPLOYEE GROUP BY DEPARTMENTNAME,GENDER ORDER BY DEPARTMENTNAME,GENDER
SELECT EMPLOYEEKEY,FIRSTNAME,LASTNAME, MIDDLENAME,TITLE,HIREDATE, BIRTHDATE,EMAILADDRESS,PHONE,GENDER FROM DBO.DIMEMPLOYEE WHERE DEPARTMENTNAME=@DEPARTMENTNAME AND GENDER=@GENDER
پیشنیازهای کار با کامپوننت ng2-file-upload
برای شروع به کار با کامپوننت ng2-file-upload، ابتدا نیاز است بستهی npm آنرا نصب کرد:
>npm install ng2-file-upload --save
همچنین یک کامپوننت آزمایشی را هم به برنامه (دقیقا همان مثال مطلب قبلی) جهت اعمال آن اضافه میکنیم:
>ng g c UploadFile/ng2-file-upload-test
پس از آن نیاز است به ماژولی که این کامپوننت جدید در آن قرار دارد، مدخل FileUploadModule کامپوننت ng2-file-upload را افزود:
import { FileUploadModule } from "ng2-file-upload"; @NgModule({ imports: [ FileUploadModule ]
تکمیل Ng2FileUploadTestComponent جهت اعمال ng2-file-upload
اکنون به کلاس کامپوننت جدیدی که ایجاد کردیم، مراجعه کرده و تغییرات ذیل را اعمال میکنیم:
import { FileUploader, FileUploaderOptions } from "ng2-file-upload"; import { Ticket } from "./../ticket"; export class Ng2FileUploadTestComponent implements OnInit { fileUploader: FileUploader; model = new Ticket();
وهله سازی از کامپوننت ng2-file-upload و انجام تنظیمات اولیهی آن
پس از تعریف خاصیت عمومی fileUploader، اکنون نوبت به وهله سازی آن است:
this.fileUploader = new FileUploader( <FileUploaderOptions>{ url: "api/SimpleUpload/SaveTicket", headers: [ { name: "X-XSRF-TOKEN", value: this.getCookie("XSRF-TOKEN") }, { name: "Accept", value: "application/json" } ], isHTML5: true, // allowedMimeType: ["image/jpeg", "image/png", "application/pdf", "application/msword", "application/zip"] allowedFileType: [ "application", "image", "video", "audio", "pdf", "compress", "doc", "xls", "ppt" ], removeAfterUpload: true, autoUpload: false, maxFileSize: 10 * 1024 * 1024 } );
- اگر برنامه از نکات anti-forgery token استفاده میکند، این کامپوننت برخلاف روش مطرح شدهی در مطلب مشابه قبلی، هیچ هدری را به سمت سرور ارسال نمیکند. بنابراین نیاز است کوکی مرتبط را خودمان یافته و سپس به لیست هدرها اضافه کنیم. در اینجا روش استخراج یک کوکی را توسط کدهای جاوا اسکریپتی مشاهده میکنید:
getCookie(name: string): string { const value = "; " + document.cookie; const parts = value.split("; " + name + "="); if (parts.length === 2) { return decodeURIComponent(parts.pop().split(";").shift()); } }
- برای محدود سازی فایلهای ارسالی توسط این کامپوننت، دو روش وجود دارد:
الف) مشخص سازی مقدار خاصیت allowedMimeType
همانطور که مشاهده میکنید، در اینجا باید mime type فایلهای مجاز را مشخص کرد.
ب) مشخص سازی مقدار خاصیت allowedFileType
برخلاف تصور، در اینجا از پسوند فایلها استفاده نمیکند و از یک لیست از پیش مشخص که نمونهای از آنرا در اینجا مشاهده میکنید، کمک گرفته میشود. بنابراین اگر برای مثال تنها نیاز به ارسال تصاویر بود، مقدار image را نگه داشته و مابقی را از لیست حذف کنید.
- removeAfterUpload به این معنا است که آیا لیست نهایی که نمایش داده میشود، پس از آپلود باقی بماند یا خیر؟
- توسط خاصیت maxFileSize میتوان حداکثر اندازهی قابل قبول فایلهای ارسالی را مشخص کرد.
مدیریت رخدادهای کامپوننت ng2-file-upload
اکنون که وهلهای از این کامپوننت ساخته شدهاست، میتوان رخدادهای آنرا نیز مدیریت کرد. برای مثال:
الف) نحوهی ارسال اطلاعات اضافی به همراه یک فایل به سمت سرور
this.fileUploader.onBuildItemForm = (fileItem, form) => { for (const key in this.model) { if (this.model.hasOwnProperty(key)) { form.append(key, this.model[key]); } } };
ب) اطلاع یافتن از رخداد خاتمهی کار
رخداد onCompleteAll پس از ارسال تمام فایلها به سمت سرور فراخوانی میشود:
this.fileUploader.onCompleteAll = () => { // clear the form // this.model = new Ticket(); };
ج) در حین وهله سازی fileUploader، تعدادی محدودیت نیز قابل اعمال هستند. این محدودیتها سبب نمایش هیچگونه پیام خطایی نمیشوند. فقط زمانیکه کاربر فایلی را انتخاب میکند، این فایل در لیست ظاهر نمیشود. اگر علاقمند به مدیریت این وضعیت باشید، میتوان از رخداد onWhenAddingFileFailed استفاده کرد:
this.fileUploader.onWhenAddingFileFailed = (item, filter, options) => { // msg: `You can't select ${item.name} file because of the ${filter.name} filter.` };
د) اگر ارسال فایلی به سمت سرور با شکست مواجه شود، در رخدادگردان onErrorItem میتوان به نام این فایل و اطلاعات بیشتری که از سمت سرور دریافت شدهاست، دسترسی یافت:
this.fileUploader.onErrorItem = (fileItem, response, status, headers) => { // };
ه) اگر از سمت سرور اطلاعات JSON مانندی یا هر اطلاعات دیگری به سمت کلاینت پس از آپلود ارسال میشود، این اطلاعات را میتوان در رخدادگردان onSuccessItem دریافت کرد:
this.fileUploader.onSuccessItem = (item, response, status, headers) => { if (response) { const ticket = JSON.parse(response); console.log(`ticket:`, ticket); } };
ارسال نهایی فرم و فایلها به سمت سرور
در پایان، با فراخوانی متد uploadAll شیء fileUploader جاری، میتوان اطلاعات فرم و تمام فایلهای آنرا به سمت سرور ارسال کرد:
submitForm(form: NgForm) { this.fileUploader.uploadAll(); // NOTE: Upload multiple files in one request -> https://github.com/valor-software/ng2-file-upload/issues/671 }
کدهای کامل کامپوننت ng2-file-upload-test.component.ts را در اینجا میتوانید مشاهده کنید.
تکمیل قالب کامپوننت Ng2FileUploadTestComponent
اکنون که کار تکمیل کامپوننت آزمایشی ارسال فایلها به سمت سرور به پایان رسید، نوبت به تکمیل قالب آن است.
افزودن فیلد اضافی توضیحات به فرم
<div class="container"> <h3>Support Form(ng2-file-upload)</h3> <form #form="ngForm" (submit)="submitForm(form)" novalidate> <div class="form-group" [class.has-error]="description.invalid && description.touched"> <label class="control-label">Description</label> <input #description="ngModel" required type="text" class="form-control" name="description" [(ngModel)]="model.description"> <div *ngIf="description.invalid && description.touched"> <div class="alert alert-danger" *ngIf="description.errors.required"> description is required. </div> </div> </div>
تعریف ویژهی فیلد ارسال فایلها به سمت سرور
<div class="form-group"> <label class="control-label">Screenshot(s)</label> <input required type="file" multiple ng2FileSelect [uploader]="fileUploader" class="form-control" name="screenshot"> </div>
ذکر ویژگی استاندارد multiple را نیز در اینجا مشاهده میکنید. وجود آن سبب خواهد شد تا کاربر بتواند چندین فایل را با هم انتخاب کند. اگر نیازی به ارسال چندین فایل نیست، این ویژگی را حذف کنید.
نمایش لیست فایلها و نمایش درصد پیشرفت آپلود آنها
جدولی را که در تصویر ابتدای بحث مشاهده کردید، به صورت ذیل شکل میگیرد (کدهای آن در همان صفحهی توضیحات کامپوننت نیز موجود هستند):
<div style="margin-bottom: 10px" *ngIf="fileUploader.queue.length"> <h3>Upload queue</h3> <p>Queue length: {{ fileUploader?.queue?.length }}</p> <table class="table"> <thead> <tr> <th width="50%">Name</th> <th>Size</th> <th>Progress</th> <th>Status</th> <th>Actions</th> </tr> </thead> <tbody> <tr *ngFor="let item of fileUploader.queue"> <td><strong>{{ item?.file?.name }}</strong></td> <td nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td> <td> <div class="progress" style="margin-bottom: 0;"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div> </div> </td> <td class="text-center"> <span *ngIf="item.isError"><i class="glyphicon glyphicon-remove"></i></span> </td> <td nowrap> <button type="button" class="btn btn-danger btn-xs" (click)="item.remove()"> <span class="glyphicon glyphicon-trash"></span> Remove </button> </td> </tr> </tbody> </table> <div> <div> Queue progress: <div class="progress"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': fileUploader.progress + '%' }"></div> </div> </div> <button type="button" class="btn btn-danger btn-s" (click)="fileUploader.clearQueue()" [disabled]="!fileUploader.queue.length"> <span class="glyphicon glyphicon-trash"></span> Remove all </button> </div> </div>
در اینجا از progress-bar بوت استرپ برای نمایش درصد آپلود فایلها استفاده شدهاست:
<div class="progress" style="margin-bottom: 0;"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div> </div>
غیرفعال کردن دکمهی ارسال، در صورت عدم انتخاب یک فایل
<button class="btn btn-primary" [disabled]="form.invalid || !fileUploader.queue.length" type="submit">Submit</button> </form>
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
OpenCVSharp #6
An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll Additional information: Exception has been thrown by the target of an invocation.
private void button1_Click(object sender, EventArgs e) { if (_worker != null && _worker.IsBusy) { return; } _worker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true }; _worker.DoWork += workerDoWork; _worker.ProgressChanged += workerProgressChanged; _worker.RunWorkerCompleted += workerRunWorkerCompleted; _worker.RunWorkerAsync(); }
private void workerDoWork(object sender, DoWorkEventArgs e) { //var interval = (int)(1000 / _capture.Fps); Image image; while ((image = _capture.QueryFrame().ToBitmap()) != null && _worker != null && !_worker.CancellationPending) { _worker.ReportProgress(0, image); //Thread.Sleep(interval); Thread.Sleep(10); } } private void workerProgressChanged(object sender, ProgressChangedEventArgs e) { var image = e.UserState as Image; if (image == null) return; //Cv.Not(image, image); //_pictureBoxIpl1.RefreshIplImage(image); //_pictureBoxIpl1.Image=image; _pictureBoxIpl1.Invoke(new EventHandler(delegate { _pictureBoxIpl1.Image = image; })); } private void workerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { _worker.Dispose(); _worker = null; }
Sqlite دیتابیس مناسبی برای برنامههای چندسکویی است و عموما به عنوان اولین گزینه استفاده میشود. برای کار با این دیتابیس، ما از ماژول sql.js که یکی از ماژولهای معروف در جاوااسکریپت است، استفاده میکنیم. برای نصب آن از طریق npm، به شکل زیر اقدام میکنیم:
npm install sql.js --save
const{app,BrowserWindow}=require("electron"); let win; function onLoad() { win=new BrowserWindow({ width:800, height:600 }); win.loadURL(`file://${__dirname}/index.html`); } app.on("ready",onLoad());
var path=require("path"); path.exists('filepath",(status)=> { .... }); var status=path.existsSync("file"); //=============================== var fs=require("fs"); fs.exists('filepath",(status)=> { .... }); var status=path.existsSync("file");
fs.stat('foo.txt', function(err, stat) { if(err == null) { console.log('فایل موجوده'); } else if(err.code == 'ENOENT') { // فایل وجود نداره fs.writeFile('log.txt', 'Some log\n'); } else { //خطای دیگری رخ داده است } });
try { stats = fs.statSync(path); console.log("File exists."); } catch (e) { console.log("File does not exist."); }
const fs = require('fs'); const sql = require('sql.js'); dbPath = './mydb.sqlite'; dbExists=false; try { dbExists = fs.statSync(dbPath); } catch (e) { } if(!dbExists) { //create Database var sqlStr=fs.readFileSync("./sql.txt"); var db = new sql.Database(); db.run(String(sqlStr)); //write to disk var data=db.export(); var buffer=new Buffer(data); fs.writeFileSync(dbPath,buffer); } else{ var buffer = fs.readFileSync(dbPath); var db = new sql.Database(buffer); }
CREATE TABLE numbers ( id INT PRIMARY KEY UNIQUE NOT NULL, fname VARCHAR (20) NOT NULL, lname VARCHAR (30) NOT NULL, number VARCHAR (15) NOT NULL ); insert into numbers values(1,'ali','yeganeh','03111223344'); insert into numbers values(2,'xxx','yyy','45454555');
بعد از آن نیاز است تا دیتابیس در دسترس Render Processها قرار بگیرد که در مقاله "شیوه کدنویسی در الکترون " در مورد global صحبت کردهایم و نحوه استفاده از آن را فرا گرفتیم:
global.db=db;
در پایان اجرای برنامه لازم است که دیتابیس توسط دستور close بسته شود. سپس کد زیر را در رویداد windows-all-closed مینویسیم:
app.on('window-all-closed', () => { db.close(); if (process.platform !== 'darwin') { app.quit(); } });
(چند مورد خارج از بحث): کد بعدی که مورد استفاده قرار گرفته است و در مقالات قبلی در مورد آن صحبت نکردهایم این است که در سیستمهای مک، وضعیت به این قرار است که اگر شما برنامه را ببیندید، آن برنامه بسته نشده و در پس زمینه فعال است و میتوانید آن را از طریق dock اطراف صفحه، مجددا فعال کنید. ولی با نوشتن کد بالا، ما این وضعیت را اعلام کردهایم که اگر تمامی پنجرهها بسته شدند، کل برنامه را ببند.
همچنین بسیار خوب است که کد زیر را هم همیشه اضافه کنید:
win.on('closed', () => { win = null; });
پس اگر این کد را نوشتید، وضعیت سیستم عامل مک را به خاطر داشته باشید و مجبور هستید کد زیر را نیز اضافه کنید:
app.on('activate', () => { if (win === null) { createWindow(); } });
بعد از اینکه دیتابیس را به شیء global دادیم، در صفحه html کد زیر را وارد میکنیم:
<html> <head> <script src="./jquery.min.js"></script> <link href="./bootstrap-3.3.6-dist/css/bootstrap.min.css" rel="stylesheet"></link> <meta charset="utf-8"> <title></title> <script> const {remote}=require("electron"); let db=remote.getGlobal("db"); </script> </head> <body> <table id="people" class="table table-hover table-striped"> <th> <tr> <td>First Name</td> <td>last Name</td> <td>Phone Number</td> </tr> </th> <tbody> </tbody> </table> </body> </html>
$(document).ready(()=> { //show data var tableBody=$("#people"); db.each("select * from numbers",(row)=>{ let rowTemplate=`<tr><td>${row.fname}</td><td>${row.lname}</td><td>${row.number}</td></tr>`; tableBody.append(rowTemplate); });
حال وقت آن رسیده است که خروجی کار را ببینیم. پس کد npm start را اجرا میکنیم. همانطور که میبینید خروجی به راحتی نمایش داده میشود. در مقاله بعدی بیشتر در این مورد صحبت میکنیم.
Memento یک الگوی طراحی مفید و ساده است که برای ذخیره و بازیابی state یک object استفاده میشود. در بعضی از مقالات از آن به عنوان snapshot نیز یاد شده است! اگر با git کار کرده باشید، این مفهوم را میتوان در git بسیار یافت؛ هر commit به عنوان یک snapshot میباشد که میتوان به صورت مکرر آن را undo کرد و یا مثال خیلی سادهتر میتوان به ctrl+z در سیستم عامل اشاره کرد.
به مثال زیر توجه کنید:
Int temp; Int a=1; temp=a; a=2; . . a=temp;
شما قطعا در برنامه نویسی با کد بالا زیاد برخورد داشتهاید و آنرا به صورت مکرر انجام دادهاید. کد بالا را در قالب یک object بیان میکنیم. به مثال زیر توجه کنید:
int main() { MyClass One = new MyClass(); MyClass Temp = new MyClass(); // Set an initial value. One.Value = 10; One.Name = "Ten"; // Save the state of the value. Temp.Value = One.Value; Temp.Name = One.Name; // Change the value. One.Value = 99; One.Name = "Ninety Nine"; // Undo and restore the state. One.Value = Temp.Value; One.Name = Temp.Name; }
در کد بالا با استفاده از یک temp، شیء مورد نظر را ذخیره کرده و در آخر مجدد دادهها را درون شیء، restore میکنیم.
از مشکلات کد بالا میتوان گفت :
۱- برای هر object باید یک شیء temp ایجاد کنیم.
۲- ممکن است بخواهیم که حالات یک object را بر روی هارد ذخیره کنیم. با روش فوق کدها خیلی پیچیدهتر خواهند شد.
۳- نوشتن کد به این سبک برای پروژههای بزرگ، پیچیده و مدیریت آن سختتر میشود.
پیاده سازی memento
ما این مثال را در قالب یک پروژه NET Core onsole. ایجاد میکنیم. برای این کار یک پوشهی جدید را ایجاد و درون ترمینال دستور زیر را وارد کنید:
dotnet new console
روشهای زیادی برای پیاده سازی memento وجود دارند. برای پیاده سازی memento ابتدا یک abstract class را به شکل زیر ایجاد میکنیم:
abstract class MementoBase { protected Guid mementoKey = Guid.NewGuid(); abstract public void SaveMemento(Memento memento); abstract public void RestoreMemento(Memento memento); }
اگر به کلاس بالا دقت کنید، این کلاس قرار است parent کلاسهای دیگری باشد که داری دو متد SaveMemento و RestoreMemento برای ذخیره و بازیابی و همچنین یک Guid برای نگهداری stateهای مختلف میباشد.
ورودی متدها از نوع memento میباشد. پس کلاس memento را به شکل زیر ایجاد میکنیم:
class Memento { private Dictionary<Guid, object> stateList = new Dictionary<Guid, object>(); public object GetState(Guid key) { return stateList[key]; } public void SetState(Guid key, object newState) { stateList[key] = newState; } public Memento() { } }
در کد بالا با یک Dictionary میتوان هر object را با کلیدش ذخیره کنیم. توجه کنید که value دیکشنری از نوع object میباشد و چون object پدر تمام objectهای دیگر است پس میتوانیم هر نوع دادهای را در آن ذخیره کنیم. تا اینجا، Memento پیاده سازی شده است. میتوان این کار را با جنریکها نیز پیاده سازی کرد.
در ادامه میخواهیم یک کلاس بسازیم و حالتهای مختلف را در آن بررسی کنیم. کلاس زیر را ایجاد کنید:
class ConcreteOriginator : MementoBase { private int value = 0; public ConcreteOriginator(int newValue) { SetData(newValue); } public void SetData(int newValue) { value = newValue; } public void Speak() { Console.WriteLine("My value is " + value.ToString()); } public override void SaveMemento(Memento memento) { memento.SetState(mementoKey, value); } public override void RestoreMemento(Memento memento) { int restoredValue = (int)memento.GetState(mementoKey); SetData(restoredValue); } }
کلاس ConcreteOriginator از کلاس MementoBase ارث بری کرده و دو متد RestoreMemento و SaveMemento را پیاده سازی میکند و همچنین دارای یک مشخصه value میباشد. برای خروجی گرفتن، متد main را به صورت زیر پیاده سازی میکنیم:
static void Main(string[] args) { Memento memento = new Memento(); // Create an originator, which will hold our state data. ConcreteOriginator myOriginator = new ConcreteOriginator("Hello World!", StateType.ONE); ConcreteOriginator anotherOriginator = new ConcreteOriginator("Hola!", StateType.ONE); ConcreteOriginator2 thirdOriginator = new ConcreteOriginator2(7); // Set some state data. myOriginator.Speak(); anotherOriginator.Speak(); thirdOriginator.Speak(); // Save the states into our memento. myOriginator.SaveMemento(memento); anotherOriginator.SaveMemento(memento); thirdOriginator.SaveMemento(memento); // Now change our originators' states. myOriginator.SetData("Goodbye!", StateType.TWO); anotherOriginator.SetData("Adios!", StateType.TWO); thirdOriginator.SetData(99); myOriginator.Speak(); anotherOriginator.Speak(); thirdOriginator.Speak(); // Restore our originator's state. myOriginator.RestoreMemento(memento); anotherOriginator.RestoreMemento(memento); thirdOriginator.RestoreMemento(memento); myOriginator.Speak(); anotherOriginator.Speak(); thirdOriginator.Speak(); Console.ReadKey(); }
Hello World! I'm in state ONE Hola! I'm in state ONE My value is 7 Goodbye! I'm in state TWO Adios! I'm in state TWO My value is 99 Hello World! I'm in state ONE Hola! I'm in state ONE My value is 7
- ساخت برنامههای دسکتاپ به صورت چندسکویی (ویندوز، لینوکس، مک)
- استفاده از HTML,CSS,JavaScript که طراحان وب در این زمینه با آن به آسانی ارتباط برقرار میکنند.
- قابلیت استفاده از کتابخانههای قدرتمند تحت وب چون Bootstrap,Jquery,Angular Js و ...
- متن باز و رایگان است.
D:\electron\test1>npm init
npm i electron-prebuilt --save-dev
لازم است در اینجا توضیحی کوتاه در مورد انواع وابستگیها داشته باشیم:
Dependencies این نوع از وابستگیها، بستههایی را نصب میکنند که شما از آنها در کدهایتان استفاده میکنید و در آینده به همراه پروژه کمپایل میشوند. به طور خودکار وقتی بستهای را به عنوان وابستگی معرفی میکنید، npm وابستههای آن بسته را به صورت درختی بررسی میکند و آنها را هم نصب میکند.
DevDependencies : این نوع از وابستگیهای برای کارهای دیباگینگ و ... است؛ مثل آزمون واحد و ... که نیازی نیست در کامپایل نهایی لحاظ گردند. اگر این نوع کتابخانهها را به جای devdependencies به dependencies ارسال کنید، اتفاق خاصی نمیافتد. ولی در حجم برنامهی نهایی شما تاثیرگذار خواهند بود.
PeerDependencies: این نوع وابستگیها برای معرفی بستههایی استفاده میشوند که در پلاگینهایی که استفاده میکنید تاثیر دارند. ممکن است پلاگینی نیاز به استفادهی از یک بسته را دارد، ولی آن را در کد، Require نکرده باشد (در مورد Require بعدا صحبت میکنیم). ولی برای اجرا نیاز به این بسته دارد. به همین دلیل از نسخهی 3 به بعد، به شما هشدار میدهد که این بستهها را نیز لحاظ کنید (تا نسخهی npm2 به طور خودکار نصب میشد). همچنین نسخه بندی این وابستگیها را نیز در نظر میگیرد. این حالت را میتوانید مانند پلاگینهای جیکوئری تصور کنید که نیاز است قبل از آنها، کتابخانهی جیکوئری صدا زده شود؛ در صورتی که در خود پلاگین، جی کوئری صدا زده نشده است.
ویرایشگر اتم
قبل از اینکه بخواهیم کدنویسی با هر زبانی را آغاز کنیم، عموما یک ادیتور مناسب را برای کارمان بر میگزینیم. الکترون نیازی به ادیتور خاصی ندارد و از Notepad گرفته تا هر ادیتور قدرتمند دیگری را میتوانید استفاده کنید. ولی ادیتور اتم Atom که توسط خود الکترون هم تولید شده است، برای استفاده رایج است. ویژوال استودیو هم در این زمینه بسیار خوب و قدرتمند ظاهر شده است و حاوی Intellisense هوشمندی است.
این ادیتور که با ظاهری جذاب، توسط تیم گیت هاب تولید شده است، یک ویرایشگر متن باز با قابلیت توسعه و تغییر پذیری بالاست و از بستههای Node.js پشتیانی میکند و به صورت داخلی مجهز به سیستم گیت میباشد. بیشتر فناوریهای استفاده شده در این ویرایشگر، رایگان بوده و دارای جامعهی بزرگ متن باز میباشند. از فناوریهای مورد استفادهی آن میتوان به الکترون، CoffeScript ، Node.js ,LESS و ... اشاره کرد. شعار سازندگان این ادیتور «یک ویرایشگر قابل هک برای قرن 21» میباشد.
برای پشتیبانی از زبانهای مختلف، حاوی تعدادی زیادی پلاگین پیش فرض است مانند روبی ، سی شارپ، PHP ,Git,Perl,C/C++, Go,Objective-C,YAML و ...
آغاز کدنویسی
بگذارید کدنویسی را شروع کنیم. اگر اتم را نصب کرده باشید، میتوانید با وارد کردن عبارت زیر، پروژه خود را در ادیتور باز کنید:
atom .
{ "name": "electron", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "electron ." }, "author": "", "license": "ISC" }
const electron = require('electron'); const {app} = electron; const {BrowserWindow} = electron;
let win; app.on('ready', function() { // Create the browser window. win = new BrowserWindow({ width: 800, height: 600 }); });
اکنون در کنسول مینویسیم:
npm start
برای اینکه اولین برنامه واقعا خالی نباشد و ظاهری به آن بدهیم، یک فایل html میسازیم و در callback رویداد ready، بعد از ساخت پنجره آن را صدا میزنیم:
win.loadURL(`file://${__dirname}/index.html`);
بررسی روش ارتقاء به NET Core 1.1.
(اسکریپت را در فایل متنی (برای مثال scriptFile ) قرار دهید سپس با دستور chmod u+rx scriptFile قابلیت اجرایی بهش بدید و اجراش کنید sudo ./scriptFile)
در مرحله بعد میتوانید پکیجها را خودتان بصورت دستی دانلود و نصب کنید از این آدرس
دقت شود که مطابق مقالات این سایت نسخه Current را نصب کنید و از LTS صرفنظر کنید تا به روزتر بمانید. ولی اگر قصد انجام پروژه جدی ای دارید وارون این گفته انتخاب کنید.
برای سهولت نصب و استفاده از آپدیتها میتوانید از ترمینال استفاده کنید. کارها رو خودکار پیش ببرید.
راهنمای نصب از طریق ترمینال برای لینوکس های اوبونتو و مینت ، دبیان، ردهت، فدورا، سنت اُ اِس و اُراکل، اُپن زوزه در اینجا
الف) نیاز است ارائه تصاویر تحت کنترل برنامه باشند.
using System.IO; using System.Net.Mime; using System.Web.Mvc; namespace MvcWatermark.Controllers { public class HomeController : Controller { const int ADay = 86400; public ActionResult Index() { return View(); } [OutputCache(VaryByParam = "fileName", Duration = ADay)] public ActionResult Image(string fileName) { fileName = Path.GetFileName(fileName); // تمیز سازی امنیتی است var rootPath = Server.MapPath("~/App_Data/Images"); var path = Path.Combine(rootPath, fileName); if (!System.IO.File.Exists(path)) { var notFoundImage = "notFound.png"; path = Path.Combine(rootPath, notFoundImage); return File(path, MediaTypeNames.Image.Gif, notFoundImage); } return File(path, MediaTypeNames.Image.Gif, fileName); } } }
نحوه استفاده از این اکشن متد نیز به نحو زیر است:
<img src="@Url.Action(actionName: "Image", controllerName: "Home", routeValues: new { fileName = "EF_Stra_08.gif" })" />
ب) آیا فراخوان تصویر ما را مستقیما در سایت خودش قرار داده است؟
private bool isEmbeddedIntoAnotherDomain { get { return this.HttpContext.Request.UrlReferrer != null && !this.HttpContext.Request.Url.Host.Equals(this.HttpContext.Request.UrlReferrer.Host, StringComparison.InvariantCultureIgnoreCase); } }
ج) افزودن خودکار Watermark در صورت کپی شدن در سایتی دیگر
private byte[] addWaterMark(string filePath, string text) { var image = new WebImage(filePath); image.AddTextWatermark(text); return image.GetBytes(); }
اما ... پس از امتحان تصاویر مختلف ممکن است گاها با خطای زیر مواجه شویم:
A Graphics object cannot be created from an image that has an indexed pixel format.
PixelFormatUndefined PixelFormatDontCare PixelFormat1bppIndexed PixelFormat4bppIndexed PixelFormat8bppIndexed PixelFormat16bppGrayScale PixelFormat16bppARGB1555
private byte[] addWaterMark(string filePath, string text) { using (var img = System.Drawing.Image.FromFile(filePath)) { using (var memStream = new MemoryStream()) { using (var bitmap = new Bitmap(img))//avoid gdi+ errors { bitmap.Save(memStream, ImageFormat.Png); var webImage = new WebImage(memStream); webImage.AddTextWatermark(text, verticalAlign: "Top", horizontalAlign: "Left", fontColor: "Brown"); return webImage.GetBytes(); } } } }
در ادامه، قسمت آخر کار، اعمال این مراحل به اکشن متد Image است:
if (isEmbeddedIntoAnotherDomain) { var text = Url.Action(actionName: "Index", controllerName: "Home", routeValues: null, protocol: "http"); var content = addWaterMark(path, text); return File(content, MediaTypeNames.Image.Gif, fileName); } return File(path, MediaTypeNames.Image.Gif, fileName);
کدهای نهایی این کنترلر را از اینجا میتوانید دریافت کنید:
HomeController.cs
به همراه نمونه تصویری که استثنای یاد شده را تولید میکند؛ جهت آزمایش بیشتر:
EFStra08.gif